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::settings;
7 
8 use regalloc::{
9     PrettyPrint, RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES,
10 };
11 
12 use std::string::{String, ToString};
13 
14 //=============================================================================
15 // Registers, the Universe thereof, and printing
16 
17 /// The pinned register on this architecture.
18 /// It must be the same as Spidermonkey's HeapReg, as found in this file.
19 /// https://searchfox.org/mozilla-central/source/js/src/jit/arm64/Assembler-arm64.h#103
20 pub const PINNED_REG: u8 = 21;
21 
22 #[rustfmt::skip]
23 const XREG_INDICES: [u8; 31] = [
24     // X0 - X7
25     32, 33, 34, 35, 36, 37, 38, 39,
26     // X8 - X15
27     40, 41, 42, 43, 44, 45, 46, 47,
28     // X16, X17
29     58, 59,
30     // X18
31     60,
32     // X19, X20
33     48, 49,
34     // X21, put aside because it's the pinned register.
35     57,
36     // X22 - X28
37     50, 51, 52, 53, 54, 55, 56,
38     // X29 (FP)
39     61,
40     // X30 (LR)
41     62,
42 ];
43 
44 const ZERO_REG_INDEX: u8 = 63;
45 
46 const SP_REG_INDEX: u8 = 64;
47 
48 /// Get a reference to an X-register (integer register).
xreg(num: u8) -> Reg49 pub fn xreg(num: u8) -> Reg {
50     assert!(num < 31);
51     Reg::new_real(
52         RegClass::I64,
53         /* enc = */ num,
54         /* index = */ XREG_INDICES[num as usize],
55     )
56 }
57 
58 /// Get a writable reference to an X-register.
writable_xreg(num: u8) -> Writable<Reg>59 pub fn writable_xreg(num: u8) -> Writable<Reg> {
60     Writable::from_reg(xreg(num))
61 }
62 
63 /// Get a reference to a V-register (vector/FP register).
vreg(num: u8) -> Reg64 pub fn vreg(num: u8) -> Reg {
65     assert!(num < 32);
66     Reg::new_real(RegClass::V128, /* enc = */ num, /* index = */ num)
67 }
68 
69 /// Get a writable reference to a V-register.
writable_vreg(num: u8) -> Writable<Reg>70 pub fn writable_vreg(num: u8) -> Writable<Reg> {
71     Writable::from_reg(vreg(num))
72 }
73 
74 /// Get a reference to the zero-register.
zero_reg() -> Reg75 pub fn zero_reg() -> Reg {
76     // This should be the same as what xreg(31) returns, except that
77     // we use the special index into the register index space.
78     Reg::new_real(
79         RegClass::I64,
80         /* enc = */ 31,
81         /* index = */ ZERO_REG_INDEX,
82     )
83 }
84 
85 /// Get a writable reference to the zero-register (this discards a result).
writable_zero_reg() -> Writable<Reg>86 pub fn writable_zero_reg() -> Writable<Reg> {
87     Writable::from_reg(zero_reg())
88 }
89 
90 /// Get a reference to the stack-pointer register.
stack_reg() -> Reg91 pub fn stack_reg() -> Reg {
92     // XSP (stack) and XZR (zero) are logically different registers which have
93     // the same hardware encoding, and whose meaning, in real aarch64
94     // instructions, is context-dependent.  For convenience of
95     // universe-construction and for correct printing, we make them be two
96     // different real registers.
97     Reg::new_real(
98         RegClass::I64,
99         /* enc = */ 31,
100         /* index = */ SP_REG_INDEX,
101     )
102 }
103 
104 /// Get a writable reference to the stack-pointer register.
writable_stack_reg() -> Writable<Reg>105 pub fn writable_stack_reg() -> Writable<Reg> {
106     Writable::from_reg(stack_reg())
107 }
108 
109 /// Get a reference to the link register (x30).
link_reg() -> Reg110 pub fn link_reg() -> Reg {
111     xreg(30)
112 }
113 
114 /// Get a writable reference to the link register.
writable_link_reg() -> Writable<Reg>115 pub fn writable_link_reg() -> Writable<Reg> {
116     Writable::from_reg(link_reg())
117 }
118 
119 /// Get a reference to the frame pointer (x29).
fp_reg() -> Reg120 pub fn fp_reg() -> Reg {
121     xreg(29)
122 }
123 
124 /// Get a writable reference to the frame pointer.
writable_fp_reg() -> Writable<Reg>125 pub fn writable_fp_reg() -> Writable<Reg> {
126     Writable::from_reg(fp_reg())
127 }
128 
129 /// Get a reference to the first temporary, sometimes "spill temporary", register. This register is
130 /// used to compute the address of a spill slot when a direct offset addressing mode from FP is not
131 /// sufficient (+/- 2^11 words). We exclude this register from regalloc and reserve it for this
132 /// purpose for simplicity; otherwise we need a multi-stage analysis where we first determine how
133 /// many spill slots we have, then perhaps remove the reg from the pool and recompute regalloc.
134 ///
135 /// We use x16 for this (aka IP0 in the AArch64 ABI) because it's a scratch register but is
136 /// slightly special (used for linker veneers). We're free to use it as long as we don't expect it
137 /// to live through call instructions.
spilltmp_reg() -> Reg138 pub fn spilltmp_reg() -> Reg {
139     xreg(16)
140 }
141 
142 /// Get a writable reference to the spilltmp reg.
writable_spilltmp_reg() -> Writable<Reg>143 pub fn writable_spilltmp_reg() -> Writable<Reg> {
144     Writable::from_reg(spilltmp_reg())
145 }
146 
147 /// Get a reference to the second temp register. We need this in some edge cases
148 /// where we need both the spilltmp and another temporary.
149 ///
150 /// We use x17 (aka IP1), the other "interprocedural"/linker-veneer scratch reg that is
151 /// free to use otherwise.
tmp2_reg() -> Reg152 pub fn tmp2_reg() -> Reg {
153     xreg(17)
154 }
155 
156 /// Get a writable reference to the tmp2 reg.
writable_tmp2_reg() -> Writable<Reg>157 pub fn writable_tmp2_reg() -> Writable<Reg> {
158     Writable::from_reg(tmp2_reg())
159 }
160 
161 /// Create the register universe for AArch64.
create_reg_universe(flags: &settings::Flags) -> RealRegUniverse162 pub fn create_reg_universe(flags: &settings::Flags) -> RealRegUniverse {
163     let mut regs = vec![];
164     let mut allocable_by_class = [None; NUM_REG_CLASSES];
165 
166     // Numbering Scheme: we put V-regs first, then X-regs. The X-regs exclude several registers:
167     // x18 (globally reserved for platform-specific purposes), x29 (frame pointer), x30 (link
168     // register), x31 (stack pointer or zero register, depending on context).
169 
170     let v_reg_base = 0u8; // in contiguous real-register index space
171     let v_reg_count = 32;
172     for i in 0u8..v_reg_count {
173         let reg = Reg::new_real(
174             RegClass::V128,
175             /* enc = */ i,
176             /* index = */ v_reg_base + i,
177         )
178         .to_real_reg();
179         let name = format!("v{}", i);
180         regs.push((reg, name));
181     }
182     let v_reg_last = v_reg_base + v_reg_count - 1;
183 
184     // Add the X registers. N.B.: the order here must match the order implied
185     // by XREG_INDICES, ZERO_REG_INDEX, and SP_REG_INDEX above.
186 
187     let x_reg_base = 32u8; // in contiguous real-register index space
188     let mut x_reg_count = 0;
189 
190     let uses_pinned_reg = flags.enable_pinned_reg();
191 
192     for i in 0u8..32u8 {
193         // See above for excluded registers.
194         if i == 16 || i == 17 || i == 18 || i == 29 || i == 30 || i == 31 || i == PINNED_REG {
195             continue;
196         }
197         let reg = Reg::new_real(
198             RegClass::I64,
199             /* enc = */ i,
200             /* index = */ x_reg_base + x_reg_count,
201         )
202         .to_real_reg();
203         let name = format!("x{}", i);
204         regs.push((reg, name));
205         x_reg_count += 1;
206     }
207     let x_reg_last = x_reg_base + x_reg_count - 1;
208 
209     allocable_by_class[RegClass::I64.rc_to_usize()] = Some(RegClassInfo {
210         first: x_reg_base as usize,
211         last: x_reg_last as usize,
212         suggested_scratch: Some(XREG_INDICES[19] as usize),
213     });
214     allocable_by_class[RegClass::V128.rc_to_usize()] = Some(RegClassInfo {
215         first: v_reg_base as usize,
216         last: v_reg_last as usize,
217         suggested_scratch: Some(/* V31: */ 31),
218     });
219 
220     // Other regs, not available to the allocator.
221     let allocable = if uses_pinned_reg {
222         // The pinned register is not allocatable in this case, so record the length before adding
223         // it.
224         let len = regs.len();
225         regs.push((xreg(PINNED_REG).to_real_reg(), "x21/pinned_reg".to_string()));
226         len
227     } else {
228         regs.push((xreg(PINNED_REG).to_real_reg(), "x21".to_string()));
229         regs.len()
230     };
231 
232     regs.push((xreg(16).to_real_reg(), "x16".to_string()));
233     regs.push((xreg(17).to_real_reg(), "x17".to_string()));
234     regs.push((xreg(18).to_real_reg(), "x18".to_string()));
235     regs.push((fp_reg().to_real_reg(), "fp".to_string()));
236     regs.push((link_reg().to_real_reg(), "lr".to_string()));
237     regs.push((zero_reg().to_real_reg(), "xzr".to_string()));
238     regs.push((stack_reg().to_real_reg(), "sp".to_string()));
239 
240     // FIXME JRS 2020Feb06: unfortunately this pushes the number of real regs
241     // to 65, which is potentially inconvenient from a compiler performance
242     // standpoint.  We could possibly drop back to 64 by "losing" a vector
243     // register in future.
244 
245     // Assert sanity: the indices in the register structs must match their
246     // actual indices in the array.
247     for (i, reg) in regs.iter().enumerate() {
248         assert_eq!(i, reg.0.get_index());
249     }
250 
251     RealRegUniverse {
252         regs,
253         allocable,
254         allocable_by_class,
255     }
256 }
257 
258 /// If `ireg` denotes an I64-classed reg, make a best-effort attempt to show
259 /// its name at the 32-bit size.
show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: OperandSize) -> String260 pub fn show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: OperandSize) -> String {
261     let mut s = reg.show_rru(mb_rru);
262     if reg.get_class() != RegClass::I64 || !size.is32() {
263         // We can't do any better.
264         return s;
265     }
266 
267     if reg.is_real() {
268         // Change (eg) "x42" into "w42" as appropriate
269         if reg.get_class() == RegClass::I64 && size.is32() && s.starts_with("x") {
270             s = "w".to_string() + &s[1..];
271         }
272     } else {
273         // Add a "w" suffix to RegClass::I64 vregs used in a 32-bit role
274         if reg.get_class() == RegClass::I64 && size.is32() {
275             s.push('w');
276         }
277     }
278     s
279 }
280 
281 /// Show a vector register used in a scalar context.
show_vreg_scalar(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: ScalarSize) -> String282 pub fn show_vreg_scalar(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: ScalarSize) -> String {
283     let mut s = reg.show_rru(mb_rru);
284     if reg.get_class() != RegClass::V128 {
285         // We can't do any better.
286         return s;
287     }
288 
289     if reg.is_real() {
290         // Change (eg) "v0" into "d0".
291         if s.starts_with("v") {
292             let replacement = match size {
293                 ScalarSize::Size8 => "b",
294                 ScalarSize::Size16 => "h",
295                 ScalarSize::Size32 => "s",
296                 ScalarSize::Size64 => "d",
297                 ScalarSize::Size128 => "q",
298             };
299             s.replace_range(0..1, replacement);
300         }
301     } else {
302         // Add a "d" suffix to RegClass::V128 vregs.
303         if reg.get_class() == RegClass::V128 {
304             s.push('d');
305         }
306     }
307     s
308 }
309 
310 /// Show a vector register.
show_vreg_vector(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: VectorSize) -> String311 pub fn show_vreg_vector(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: VectorSize) -> String {
312     assert_eq!(RegClass::V128, reg.get_class());
313     let mut s = reg.show_rru(mb_rru);
314 
315     let suffix = match size {
316         VectorSize::Size8x8 => ".8b",
317         VectorSize::Size8x16 => ".16b",
318         VectorSize::Size16x4 => ".4h",
319         VectorSize::Size16x8 => ".8h",
320         VectorSize::Size32x2 => ".2s",
321         VectorSize::Size32x4 => ".4s",
322         VectorSize::Size64x2 => ".2d",
323     };
324 
325     s.push_str(suffix);
326     s
327 }
328 
329 /// Show an indexed vector element.
show_vreg_element( reg: Reg, mb_rru: Option<&RealRegUniverse>, idx: u8, size: VectorSize, ) -> String330 pub fn show_vreg_element(
331     reg: Reg,
332     mb_rru: Option<&RealRegUniverse>,
333     idx: u8,
334     size: VectorSize,
335 ) -> String {
336     assert_eq!(RegClass::V128, reg.get_class());
337     let mut s = reg.show_rru(mb_rru);
338 
339     let suffix = match size {
340         VectorSize::Size8x8 => "b",
341         VectorSize::Size8x16 => "b",
342         VectorSize::Size16x4 => "h",
343         VectorSize::Size16x8 => "h",
344         VectorSize::Size32x2 => "s",
345         VectorSize::Size32x4 => "s",
346         VectorSize::Size64x2 => "d",
347     };
348 
349     s.push_str(&format!(".{}[{}]", suffix, idx));
350     s
351 }
352