Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/isa/aarch64/inst/regs.rs
1693 views
1
//! AArch64 ISA definitions: registers.
2
3
use crate::isa::aarch64::inst::OperandSize;
4
use crate::isa::aarch64::inst::ScalarSize;
5
use crate::isa::aarch64::inst::VectorSize;
6
use crate::machinst::RealReg;
7
use crate::machinst::{Reg, RegClass, Writable};
8
use regalloc2::PReg;
9
use regalloc2::VReg;
10
11
use std::string::{String, ToString};
12
13
//=============================================================================
14
// Registers, the Universe thereof, and printing
15
16
/// The pinned register on this architecture.
17
/// It must be the same as Spidermonkey's HeapReg, as found in this file.
18
/// https://searchfox.org/mozilla-central/source/js/src/jit/arm64/Assembler-arm64.h#103
19
pub const PINNED_REG: u8 = 21;
20
21
/// Get a reference to an X-register (integer register). Do not use
22
/// this for xsp / xzr; we have two special registers for those.
23
pub const fn xreg(num: u8) -> Reg {
24
Reg::from_real_reg(xreg_preg(num))
25
}
26
27
/// Get the given X-register as a PReg.
28
pub(crate) const fn xreg_preg(num: u8) -> PReg {
29
assert!(num < 31);
30
PReg::new(num as usize, RegClass::Int)
31
}
32
33
/// Get a writable reference to an X-register.
34
pub fn writable_xreg(num: u8) -> Writable<Reg> {
35
Writable::from_reg(xreg(num))
36
}
37
38
/// Get a reference to a V-register (vector/FP register).
39
pub fn vreg(num: u8) -> Reg {
40
Reg::from(vreg_preg(num))
41
}
42
43
/// Get the given V-register as a PReg.
44
pub(crate) const fn vreg_preg(num: u8) -> PReg {
45
assert!(num < 32);
46
PReg::new(num as usize, RegClass::Float)
47
}
48
49
/// Get a writable reference to a V-register.
50
#[cfg(test)] // Used only in test code.
51
pub fn writable_vreg(num: u8) -> Writable<Reg> {
52
Writable::from_reg(vreg(num))
53
}
54
55
/// Get a reference to the zero-register.
56
pub fn zero_reg() -> Reg {
57
let preg = PReg::new(31, RegClass::Int);
58
Reg::from(VReg::new(preg.index(), RegClass::Int))
59
}
60
61
/// Get a writable reference to the zero-register (this discards a result).
62
pub fn writable_zero_reg() -> Writable<Reg> {
63
Writable::from_reg(zero_reg())
64
}
65
66
/// Get a reference to the stack-pointer register.
67
pub fn stack_reg() -> Reg {
68
// XSP (stack) and XZR (zero) are logically different registers
69
// which have the same hardware encoding, and whose meaning, in
70
// real aarch64 instructions, is context-dependent. For extra
71
// correctness assurances and for correct printing, we make them
72
// be two different real registers from a regalloc perspective.
73
//
74
// We represent XZR as if it were xreg(31); XSP is xreg(31 +
75
// 32). The PReg bit-packing allows 6 bits (64 registers) so we
76
// make use of this extra space to distinguish xzr and xsp. We
77
// mask off the 6th bit (hw_enc & 31) to get the actual hardware
78
// register encoding.
79
let preg = PReg::new(31 + 32, RegClass::Int);
80
Reg::from(VReg::new(preg.index(), RegClass::Int))
81
}
82
83
/// Get a writable reference to the stack-pointer register.
84
pub fn writable_stack_reg() -> Writable<Reg> {
85
Writable::from_reg(stack_reg())
86
}
87
88
/// Get a reference to the link register (x30).
89
pub fn link_reg() -> Reg {
90
xreg(30)
91
}
92
93
/// Get a reference to the pinned register (x21).
94
pub fn pinned_reg() -> Reg {
95
xreg(PINNED_REG)
96
}
97
98
/// Get a writable reference to the link register.
99
pub fn writable_link_reg() -> Writable<Reg> {
100
Writable::from_reg(link_reg())
101
}
102
103
/// Get a reference to the frame pointer (x29).
104
pub fn fp_reg() -> Reg {
105
xreg(29)
106
}
107
108
/// Get a writable reference to the frame pointer.
109
pub fn writable_fp_reg() -> Writable<Reg> {
110
Writable::from_reg(fp_reg())
111
}
112
113
/// Get a reference to the first temporary, sometimes "spill temporary", register. This register is
114
/// used to compute the address of a spill slot when a direct offset addressing mode from FP is not
115
/// sufficient (+/- 2^11 words). We exclude this register from regalloc and reserve it for this
116
/// purpose for simplicity; otherwise we need a multi-stage analysis where we first determine how
117
/// many spill slots we have, then perhaps remove the reg from the pool and recompute regalloc.
118
///
119
/// We use x16 for this (aka IP0 in the AArch64 ABI) because it's a scratch register but is
120
/// slightly special (used for linker veneers). We're free to use it as long as we don't expect it
121
/// to live through call instructions.
122
pub fn spilltmp_reg() -> Reg {
123
xreg(16)
124
}
125
126
/// Get a writable reference to the spilltmp reg.
127
pub fn writable_spilltmp_reg() -> Writable<Reg> {
128
Writable::from_reg(spilltmp_reg())
129
}
130
131
/// Get a reference to the second temp register. We need this in some edge cases
132
/// where we need both the spilltmp and another temporary.
133
///
134
/// We use x17 (aka IP1), the other "interprocedural"/linker-veneer scratch reg that is
135
/// free to use otherwise.
136
pub fn tmp2_reg() -> Reg {
137
xreg(17)
138
}
139
140
/// Get a writable reference to the tmp2 reg.
141
pub fn writable_tmp2_reg() -> Writable<Reg> {
142
Writable::from_reg(tmp2_reg())
143
}
144
145
// PrettyPrint cannot be implemented for Reg; we need to invoke
146
// backend-specific functions from higher level (inst, arg, ...)
147
// types.
148
149
fn show_ireg(reg: RealReg) -> String {
150
match reg.hw_enc() {
151
29 => "fp".to_string(),
152
30 => "lr".to_string(),
153
31 => "xzr".to_string(),
154
63 => "sp".to_string(),
155
x => {
156
debug_assert!(x < 29);
157
format!("x{x}")
158
}
159
}
160
}
161
162
fn show_vreg(reg: RealReg) -> String {
163
format!("v{}", reg.hw_enc() & 31)
164
}
165
166
fn show_reg(reg: Reg) -> String {
167
if let Some(rreg) = reg.to_real_reg() {
168
match rreg.class() {
169
RegClass::Int => show_ireg(rreg),
170
RegClass::Float => show_vreg(rreg),
171
RegClass::Vector => unreachable!(),
172
}
173
} else {
174
format!("%{reg:?}")
175
}
176
}
177
178
pub fn pretty_print_reg(reg: Reg) -> String {
179
show_reg(reg)
180
}
181
182
fn show_reg_sized(reg: Reg, size: OperandSize) -> String {
183
match reg.class() {
184
RegClass::Int => show_ireg_sized(reg, size),
185
RegClass::Float => show_reg(reg),
186
RegClass::Vector => unreachable!(),
187
}
188
}
189
190
pub fn pretty_print_reg_sized(reg: Reg, size: OperandSize) -> String {
191
show_reg_sized(reg, size)
192
}
193
194
/// If `ireg` denotes an Int-classed reg, make a best-effort attempt to show
195
/// its name at the 32-bit size.
196
pub fn show_ireg_sized(reg: Reg, size: OperandSize) -> String {
197
let mut s = show_reg(reg);
198
if reg.class() != RegClass::Int || !size.is32() {
199
// We can't do any better.
200
return s;
201
}
202
203
// Change (eg) "x42" into "w42" as appropriate
204
if reg.class() == RegClass::Int && size.is32() && s.starts_with("x") {
205
s = "w".to_string() + &s[1..];
206
}
207
208
s
209
}
210
211
/// Show a vector register used in a scalar context.
212
pub fn show_vreg_scalar(reg: Reg, size: ScalarSize) -> String {
213
let mut s = show_reg(reg);
214
if reg.class() != RegClass::Float {
215
// We can't do any better.
216
return s;
217
}
218
219
// Change (eg) "v0" into "d0".
220
if s.starts_with("v") {
221
let replacement = match size {
222
ScalarSize::Size8 => "b",
223
ScalarSize::Size16 => "h",
224
ScalarSize::Size32 => "s",
225
ScalarSize::Size64 => "d",
226
ScalarSize::Size128 => "q",
227
};
228
s.replace_range(0..1, replacement);
229
}
230
231
s
232
}
233
234
/// Show a vector register.
235
pub fn show_vreg_vector(reg: Reg, size: VectorSize) -> String {
236
assert_eq!(RegClass::Float, reg.class());
237
let mut s = show_reg(reg);
238
239
let suffix = match size {
240
VectorSize::Size8x8 => ".8b",
241
VectorSize::Size8x16 => ".16b",
242
VectorSize::Size16x4 => ".4h",
243
VectorSize::Size16x8 => ".8h",
244
VectorSize::Size32x2 => ".2s",
245
VectorSize::Size32x4 => ".4s",
246
VectorSize::Size64x2 => ".2d",
247
};
248
249
s.push_str(suffix);
250
s
251
}
252
253
/// Show an indexed vector element.
254
pub fn show_vreg_element(reg: Reg, idx: u8, size: ScalarSize) -> String {
255
assert_eq!(RegClass::Float, reg.class());
256
let s = show_reg(reg);
257
let suffix = match size {
258
ScalarSize::Size8 => ".b",
259
ScalarSize::Size16 => ".h",
260
ScalarSize::Size32 => ".s",
261
ScalarSize::Size64 => ".d",
262
_ => panic!("Unexpected vector element size: {size:?}"),
263
};
264
format!("{s}{suffix}[{idx}]")
265
}
266
267
pub fn pretty_print_ireg(reg: Reg, size: OperandSize) -> String {
268
show_ireg_sized(reg, size)
269
}
270
271
pub fn pretty_print_vreg_scalar(reg: Reg, size: ScalarSize) -> String {
272
show_vreg_scalar(reg, size)
273
}
274
275
pub fn pretty_print_vreg_vector(reg: Reg, size: VectorSize) -> String {
276
show_vreg_vector(reg, size)
277
}
278
279
pub fn pretty_print_vreg_element(reg: Reg, idx: usize, size: ScalarSize) -> String {
280
show_vreg_element(reg, idx as u8, size)
281
}
282
283