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 alloc::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.
xreg(num: u8) -> Reg23 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.
xreg_preg(num: u8) -> PReg28 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.
writable_xreg(num: u8) -> Writable<Reg>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).
vreg(num: u8) -> Reg39 pub const fn vreg(num: u8) -> Reg {
40     Reg::from_real_reg(vreg_preg(num))
41 }
42 
43 /// Get the given V-register as a PReg.
vreg_preg(num: u8) -> PReg44 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.
writable_vreg(num: u8) -> Writable<Reg>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.
zero_reg() -> Reg56 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).
writable_zero_reg() -> Writable<Reg>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.
stack_reg() -> Reg67 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.
writable_stack_reg() -> Writable<Reg>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).
link_reg() -> Reg89 pub fn link_reg() -> Reg {
90     xreg(30)
91 }
92 
93 /// Get a reference to the pinned register (x21).
pinned_reg() -> Reg94 pub fn pinned_reg() -> Reg {
95     xreg(PINNED_REG)
96 }
97 
98 /// Get a writable reference to the link register.
writable_link_reg() -> Writable<Reg>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).
fp_reg() -> Reg104 pub fn fp_reg() -> Reg {
105     xreg(29)
106 }
107 
108 /// Get a writable reference to the frame pointer.
writable_fp_reg() -> Writable<Reg>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.
spilltmp_reg() -> Reg122 pub fn spilltmp_reg() -> Reg {
123     xreg(16)
124 }
125 
126 /// Get a writable reference to the spilltmp reg.
writable_spilltmp_reg() -> Writable<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.
tmp2_reg() -> Reg136 pub fn tmp2_reg() -> Reg {
137     xreg(17)
138 }
139 
140 /// Get a writable reference to the tmp2 reg.
writable_tmp2_reg() -> Writable<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 
show_ireg(reg: RealReg) -> String149 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 
show_vreg(reg: RealReg) -> String162 fn show_vreg(reg: RealReg) -> String {
163     format!("v{}", reg.hw_enc() & 31)
164 }
165 
show_reg(reg: Reg) -> String166 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 
pretty_print_reg(reg: Reg) -> String178 pub fn pretty_print_reg(reg: Reg) -> String {
179     show_reg(reg)
180 }
181 
show_reg_sized(reg: Reg, size: OperandSize) -> String182 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 
pretty_print_reg_sized(reg: Reg, size: OperandSize) -> String190 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.
show_ireg_sized(reg: Reg, size: OperandSize) -> String196 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.
show_vreg_scalar(reg: Reg, size: ScalarSize) -> String212 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.
show_vreg_vector(reg: Reg, size: VectorSize) -> String235 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.
show_vreg_element(reg: Reg, idx: u8, size: ScalarSize) -> String254 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 
pretty_print_ireg(reg: Reg, size: OperandSize) -> String267 pub fn pretty_print_ireg(reg: Reg, size: OperandSize) -> String {
268     show_ireg_sized(reg, size)
269 }
270 
pretty_print_vreg_scalar(reg: Reg, size: ScalarSize) -> String271 pub fn pretty_print_vreg_scalar(reg: Reg, size: ScalarSize) -> String {
272     show_vreg_scalar(reg, size)
273 }
274 
pretty_print_vreg_vector(reg: Reg, size: VectorSize) -> String275 pub fn pretty_print_vreg_vector(reg: Reg, size: VectorSize) -> String {
276     show_vreg_vector(reg, size)
277 }
278 
pretty_print_vreg_element(reg: Reg, idx: usize, size: ScalarSize) -> String279 pub fn pretty_print_vreg_element(reg: Reg, idx: usize, size: ScalarSize) -> String {
280     show_vreg_element(reg, idx as u8, size)
281 }
282