Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/winch/codegen/src/isa/aarch64/regs.rs
1692 views
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
203