cranelift_codegen/isa/aarch64/inst/
regs.rs

1//! AArch64 ISA definitions: registers.
2
3use crate::isa::aarch64::inst::OperandSize;
4use crate::isa::aarch64::inst::ScalarSize;
5use crate::isa::aarch64::inst::VectorSize;
6use crate::machinst::RealReg;
7use crate::machinst::{Reg, RegClass, Writable};
8use regalloc2::PReg;
9use regalloc2::VReg;
10
11use 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
19pub 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.
23pub fn xreg(num: u8) -> Reg {
24    Reg::from(xreg_preg(num))
25}
26
27/// Get the given X-register as a PReg.
28pub(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.
34pub 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).
39pub fn vreg(num: u8) -> Reg {
40    Reg::from(vreg_preg(num))
41}
42
43/// Get the given V-register as a PReg.
44pub(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.
51pub fn writable_vreg(num: u8) -> Writable<Reg> {
52    Writable::from_reg(vreg(num))
53}
54
55/// Get a reference to the zero-register.
56pub 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).
62pub fn writable_zero_reg() -> Writable<Reg> {
63    Writable::from_reg(zero_reg())
64}
65
66/// Get a reference to the stack-pointer register.
67pub 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.
84pub fn writable_stack_reg() -> Writable<Reg> {
85    Writable::from_reg(stack_reg())
86}
87
88/// Get a reference to the link register (x30).
89pub fn link_reg() -> Reg {
90    xreg(30)
91}
92
93/// Get a reference to the pinned register (x21).
94pub fn pinned_reg() -> Reg {
95    xreg(PINNED_REG)
96}
97
98/// Get a writable reference to the link register.
99pub fn writable_link_reg() -> Writable<Reg> {
100    Writable::from_reg(link_reg())
101}
102
103/// Get a reference to the frame pointer (x29).
104pub fn fp_reg() -> Reg {
105    xreg(29)
106}
107
108/// Get a writable reference to the frame pointer.
109pub 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.
122pub fn spilltmp_reg() -> Reg {
123    xreg(16)
124}
125
126/// Get a writable reference to the spilltmp reg.
127pub 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.
136pub fn tmp2_reg() -> Reg {
137    xreg(17)
138}
139
140/// Get a writable reference to the tmp2 reg.
141pub 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
149fn 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
162fn show_vreg(reg: RealReg) -> String {
163    format!("v{}", reg.hw_enc() & 31)
164}
165
166fn 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
178pub fn pretty_print_reg(reg: Reg) -> String {
179    show_reg(reg)
180}
181
182fn 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
190pub 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.
196pub 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.
212pub 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.
235pub 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.
254pub 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
267pub fn pretty_print_ireg(reg: Reg, size: OperandSize) -> String {
268    show_ireg_sized(reg, size)
269}
270
271pub fn pretty_print_vreg_scalar(reg: Reg, size: ScalarSize) -> String {
272    show_vreg_scalar(reg, size)
273}
274
275pub fn pretty_print_vreg_vector(reg: Reg, size: VectorSize) -> String {
276    show_vreg_vector(reg, size)
277}
278
279pub fn pretty_print_vreg_element(reg: Reg, idx: usize, size: ScalarSize) -> String {
280    show_vreg_element(reg, idx as u8, size)
281}