1 //! AArch64 register definition. 2 3 use crate::isa::reg::Reg; 4 use crate::regset::RegBitSet; 5 use regalloc2::{PReg, RegClass}; 6 7 /// FPR index bound. 8 const MAX_FPR: u32 = 32; 9 /// FPR index bound. 10 const MAX_GPR: u32 = 32; 11 12 /// Construct a X-register from an index. 13 pub(crate) const fn xreg(num: u8) -> Reg { 14 assert!((num as u32) < MAX_GPR); 15 Reg::new(PReg::new(num as usize, RegClass::Int)) 16 } 17 18 /// Construct a V-register from an index. 19 pub(crate) const fn vreg(num: u8) -> Reg { 20 assert!((num as u32) < MAX_FPR); 21 Reg::new(PReg::new(num as usize, RegClass::Float)) 22 } 23 24 /// Scratch register. 25 /// Intra-procedure-call corruptible register. 26 pub(crate) const fn ip0() -> Reg { 27 xreg(16) 28 } 29 30 // Alias to register v31. 31 const fn float_scratch() -> Reg { 32 vreg(31) 33 } 34 35 /// Scratch register. 36 /// Intra-procedure-call corruptible register. 37 pub(crate) const fn ip1() -> Reg { 38 xreg(17) 39 } 40 41 /// Register used to carry platform state. 42 const fn platform() -> Reg { 43 xreg(18) 44 } 45 46 /// Frame pointer register. 47 pub(crate) const fn fp() -> Reg { 48 xreg(29) 49 } 50 51 /// Link register for function calls. 52 pub(crate) const fn lr() -> Reg { 53 xreg(30) 54 } 55 56 /// Zero register. 57 pub(crate) const fn zero() -> Reg { 58 xreg(31) 59 } 60 61 /// The VM context register. 62 pub(crate) const fn vmctx() -> Reg { 63 xreg(9) 64 } 65 66 /// Stack pointer register. 67 /// 68 /// In aarch64 the zero and stack pointer registers are contextually 69 /// different but have the same hardware encoding; to differentiate 70 /// them, we are following Cranelift's encoding and representing it as 71 /// 31 + 32. Ref: 72 /// https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/codegen/src/isa/aarch64/inst/regs.rs#L70 73 pub(crate) const fn sp() -> Reg { 74 Reg::new(PReg::new(31 + 32, RegClass::Int)) 75 } 76 77 /// Shadow stack pointer register. 78 /// 79 /// The shadow stack pointer (SSP) is used as the base for memory addressing 80 /// to workaround Aarch64's constraint on the stack pointer 16-byte 81 /// alignment for memory addressing. This allows word-size loads and 82 /// stores. It's always assumed that the real stack pointer (SP) is 83 /// 16-byte unaligned; the only exceptions to this assumption are: 84 /// 85 /// * The function prologue and epilogue in which we use SP 86 /// for addressing, assuming that the 16-byte alignment is respected. 87 /// * Call sites, in which the code generation process explicitly ensures that 88 /// the stack pointer is 16-byte aligned. 89 /// * Code that could result in signal handling, like loads/stores. 90 /// 91 /// SSP is utilized for space allocation. After each allocation, its value is 92 /// copied to SP, ensuring that SP accurately reflects the allocated space. 93 /// Accessing memory below SP may lead to undefined behavior, as this memory can 94 /// be overwritten by interrupts and signal handlers. 95 /// 96 /// This approach requires copying the value of SSP into SP every time SSP 97 /// changes, more explicitly, this happens at three main locations: 98 /// 99 /// 1. After space is allocated, to respect the requirement of avoiding 100 /// addressing space below SP. 101 /// 2. At function epilogue. 102 /// 3. After explicit SP is emitted (code that could result in signal handling). 103 /// 104 /// +-----------+ Prologue: 105 /// | | * Save SSP (callee-saved) 106 /// +-----------+----- * SP at function entry (after prologue, slots for FP and LR) 107 /// | | * Copy the value of SP to SSP 108 /// | | 109 /// +-----------+----- SSP after reserving stack space for locals and arguments 110 /// | | Copy the value of SSP to SP 111 /// | | 112 /// +-----------+----- SSP after a push 113 /// | | Copy the value of SSP to SP 114 /// | | 115 /// | | 116 /// | | 117 /// | | Epilogue: 118 /// | | * Copy SSP to SP 119 /// +-----------+----- * Restore SSP (callee-saved) 120 /// +-----------+ 121 /// 122 /// In summary, the following invariants must be respected: 123 /// 124 /// * SSP is considered primary, and must be used to allocate and deallocate 125 /// stack space(e.g. push, pop). This operation must always be followed by 126 /// a copy of SSP to SP. 127 /// * SP must never be used to address memory except when we are certain that 128 /// the required alignment is respected (e.g. during the prologue and epilogue) 129 /// * SP must be explicitly aligned when code could result in signal handling. 130 /// * The value of SP is copied to SSP when entering a function. 131 /// * The value of SSP doesn't change between 132 /// function calls (as it's callee saved), compliant with 133 /// Aarch64's ABI. 134 /// * SSP is not available during register allocation. 135 pub(crate) const fn shadow_sp() -> Reg { 136 xreg(28) 137 } 138 139 /// Bitmask for non-allocatable GPR. 140 const NON_ALLOCATABLE_GPR: u32 = (1 << ip0().hw_enc()) 141 | (1 << ip1().hw_enc()) 142 | (1 << platform().hw_enc()) 143 | (1 << fp().hw_enc()) 144 | (1 << lr().hw_enc()) 145 | (1 << zero().hw_enc()) 146 | (1 << shadow_sp().hw_enc()) 147 | (1 << vmctx().hw_enc()); 148 /// Bitmask to represent the available general purpose registers. 149 const ALLOCATABLE_GPR: u32 = u32::MAX & !NON_ALLOCATABLE_GPR; 150 151 /// Bitmask for non-allocatable FPR. 152 /// All FPRs are allocatable, v0..=v7 are generally used for params and results. 153 154 const NON_ALLOCATABLE_FPR: u32 = 1 << float_scratch().hw_enc(); 155 /// Bitmask to represent the available floating point registers. 156 const ALLOCATABLE_FPR: u32 = u32::MAX & !NON_ALLOCATABLE_FPR; 157 158 /// Allocatable scratch general purpose registers. 159 const ALLOCATABLE_SCRATCH_GPR: u32 = (1 << ip0().hw_enc()) | (1 << ip1().hw_enc()); 160 /// Non-allocatable scratch general purpose registers. 161 const NON_ALLOCATABLE_SCRATCH_GPR: u32 = u32::MAX & !ALLOCATABLE_SCRATCH_GPR; 162 163 const ALLOCATABLE_SCRATCH_FPR: u32 = 1 << float_scratch().hw_enc(); 164 /// Non-allocatable scratch general purpose registers. 165 const NON_ALLOCATABLE_SCRATCH_FPR: u32 = u32::MAX & !ALLOCATABLE_SCRATCH_FPR; 166 167 /// Bitset for allocatable general purpose registers. 168 pub fn gpr_bit_set() -> RegBitSet { 169 RegBitSet::int( 170 ALLOCATABLE_GPR.into(), 171 NON_ALLOCATABLE_GPR.into(), 172 usize::try_from(MAX_GPR).unwrap(), 173 ) 174 } 175 176 /// Bitset for allocatable floating point registers. 177 pub fn fpr_bit_set() -> RegBitSet { 178 RegBitSet::float( 179 ALLOCATABLE_FPR.into(), 180 NON_ALLOCATABLE_FPR.into(), 181 usize::try_from(MAX_FPR).unwrap(), 182 ) 183 } 184 185 /// Bitset for allocatable scratch general purpose registers. 186 pub fn scratch_gpr_bitset() -> RegBitSet { 187 RegBitSet::int( 188 ALLOCATABLE_SCRATCH_GPR.into(), 189 NON_ALLOCATABLE_SCRATCH_GPR.into(), 190 usize::try_from(MAX_GPR).unwrap(), 191 ) 192 } 193 194 /// Bitset for allocatable scratch floating point registers. 195 pub fn scratch_fpr_bitset() -> RegBitSet { 196 RegBitSet::float( 197 ALLOCATABLE_SCRATCH_FPR.into(), 198 NON_ALLOCATABLE_SCRATCH_FPR.into(), 199 usize::try_from(MAX_FPR).unwrap(), 200 ) 201 } 202