Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/machinst/reg.rs
1693 views
1
//! Definitions for registers, operands, etc. Provides a thin
2
//! interface over the register allocator so that we can more easily
3
//! swap it out or shim it when necessary.
4
5
use alloc::{string::String, vec::Vec};
6
use core::{fmt::Debug, hash::Hash};
7
use regalloc2::{Operand, OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, VReg};
8
9
#[cfg(feature = "enable-serde")]
10
use serde_derive::{Deserialize, Serialize};
11
12
/// The first 192 vregs (64 int, 64 float, 64 vec) are "pinned" to
13
/// physical registers. These must not be passed into the regalloc,
14
/// but they are used to represent physical registers in the same
15
/// `Reg` type post-regalloc.
16
const PINNED_VREGS: usize = 192;
17
18
/// Convert a `VReg` to its pinned `PReg`, if any.
19
pub fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> {
20
if vreg.vreg() < PINNED_VREGS {
21
Some(PReg::from_index(vreg.vreg()))
22
} else {
23
None
24
}
25
}
26
27
/// Convert a `PReg` to its pinned `VReg`.
28
pub const fn preg_to_pinned_vreg(preg: PReg) -> VReg {
29
VReg::new(preg.index(), preg.class())
30
}
31
32
/// Give the first available vreg for generated code (i.e., after all
33
/// pinned vregs).
34
pub fn first_user_vreg_index() -> usize {
35
// This is just the constant defined above, but we keep the
36
// constant private and expose only this helper function with the
37
// specific name in order to ensure other parts of the code don't
38
// open-code and depend on the index-space scheme.
39
PINNED_VREGS
40
}
41
42
/// A register named in an instruction. This register can be a virtual
43
/// register, a fixed physical register, or a named spillslot (after
44
/// regalloc). It does not have any constraints applied to it: those
45
/// can be added later in `MachInst::get_operands()` when the `Reg`s
46
/// are converted to `Operand`s.
47
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
48
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
49
pub struct Reg(u32);
50
51
const REG_SPILLSLOT_BIT: u32 = 0x8000_0000;
52
const REG_SPILLSLOT_MASK: u32 = !REG_SPILLSLOT_BIT;
53
54
impl Reg {
55
/// Const constructor: create a new Reg from a regalloc2 VReg.
56
pub const fn from_virtual_reg(vreg: regalloc2::VReg) -> Reg {
57
Reg(vreg.bits() as u32)
58
}
59
60
/// Const constructor: create a new Reg from a regalloc2 PReg.
61
pub const fn from_real_reg(preg: regalloc2::PReg) -> Reg {
62
Reg(preg_to_pinned_vreg(preg).bits() as u32)
63
}
64
65
/// Get the physical register (`RealReg`), if this register is
66
/// one.
67
pub fn to_real_reg(self) -> Option<RealReg> {
68
pinned_vreg_to_preg(self.0.into()).map(RealReg)
69
}
70
71
/// Get the virtual (non-physical) register, if this register is
72
/// one.
73
pub fn to_virtual_reg(self) -> Option<VirtualReg> {
74
if self.to_spillslot().is_some() {
75
None
76
} else if pinned_vreg_to_preg(self.0.into()).is_none() {
77
Some(VirtualReg(self.0.into()))
78
} else {
79
None
80
}
81
}
82
83
/// Get the spillslot, if this register is one.
84
pub fn to_spillslot(self) -> Option<SpillSlot> {
85
if (self.0 & REG_SPILLSLOT_BIT) != 0 {
86
Some(SpillSlot::new((self.0 & REG_SPILLSLOT_MASK) as usize))
87
} else {
88
None
89
}
90
}
91
92
/// Get the class of this register.
93
pub fn class(self) -> RegClass {
94
assert!(!self.to_spillslot().is_some());
95
VReg::from(self.0).class()
96
}
97
98
/// Is this a real (physical) reg?
99
pub fn is_real(self) -> bool {
100
self.to_real_reg().is_some()
101
}
102
103
/// Is this a virtual reg?
104
pub fn is_virtual(self) -> bool {
105
self.to_virtual_reg().is_some()
106
}
107
108
/// Is this a spillslot?
109
pub fn is_spillslot(self) -> bool {
110
self.to_spillslot().is_some()
111
}
112
}
113
114
impl std::fmt::Debug for Reg {
115
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116
if VReg::from(self.0) == VReg::invalid() {
117
write!(f, "<invalid>")
118
} else if let Some(spillslot) = self.to_spillslot() {
119
write!(f, "{spillslot}")
120
} else if let Some(rreg) = self.to_real_reg() {
121
let preg: PReg = rreg.into();
122
write!(f, "{preg}")
123
} else if let Some(vreg) = self.to_virtual_reg() {
124
let vreg: VReg = vreg.into();
125
write!(f, "{vreg}")
126
} else {
127
unreachable!()
128
}
129
}
130
}
131
132
impl AsMut<Reg> for Reg {
133
fn as_mut(&mut self) -> &mut Reg {
134
self
135
}
136
}
137
138
/// A real (physical) register. This corresponds to one of the target
139
/// ISA's named registers and can be used as an instruction operand.
140
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
141
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
142
pub struct RealReg(PReg);
143
144
impl RealReg {
145
/// Get the class of this register.
146
pub fn class(self) -> RegClass {
147
self.0.class()
148
}
149
150
/// The physical register number.
151
pub fn hw_enc(self) -> u8 {
152
self.0.hw_enc() as u8
153
}
154
}
155
156
impl std::fmt::Debug for RealReg {
157
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
158
Reg::from(*self).fmt(f)
159
}
160
}
161
162
/// A virtual register. This can be allocated into a real (physical)
163
/// register of the appropriate register class, but which one is not
164
/// specified. Virtual registers are used when generating `MachInst`s,
165
/// before register allocation occurs, in order to allow us to name as
166
/// many register-carried values as necessary.
167
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
168
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
169
pub struct VirtualReg(VReg);
170
171
impl VirtualReg {
172
/// Get the class of this register.
173
pub fn class(self) -> RegClass {
174
self.0.class()
175
}
176
177
pub fn index(self) -> usize {
178
self.0.vreg()
179
}
180
}
181
182
impl std::fmt::Debug for VirtualReg {
183
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
184
Reg::from(*self).fmt(f)
185
}
186
}
187
188
/// A type wrapper that indicates a register type is writable. The
189
/// underlying register can be extracted, and the type wrapper can be
190
/// built using an arbitrary register. Hence, this type-level wrapper
191
/// is not strictly a guarantee. However, "casting" to a writable
192
/// register is an explicit operation for which we can
193
/// audit. Ordinarily, internal APIs in the compiler backend should
194
/// take a `Writable<Reg>` whenever the register is written, and the
195
/// usual, frictionless way to get one of these is to allocate a new
196
/// temporary.
197
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
198
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
199
pub struct Writable<T> {
200
reg: T,
201
}
202
203
impl<T> Writable<T> {
204
/// Explicitly construct a `Writable<T>` from a `T`. As noted in
205
/// the documentation for `Writable`, this is not hidden or
206
/// disallowed from the outside; anyone can perform the "cast";
207
/// but it is explicit so that we can audit the use sites.
208
pub const fn from_reg(reg: T) -> Writable<T> {
209
Writable { reg }
210
}
211
212
/// Get the underlying register, which can be read.
213
pub fn to_reg(self) -> T {
214
self.reg
215
}
216
217
/// Get a mutable borrow of the underlying register.
218
pub fn reg_mut(&mut self) -> &mut T {
219
&mut self.reg
220
}
221
222
/// Map the underlying register to another value or type.
223
pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> {
224
Writable { reg: f(self.reg) }
225
}
226
}
227
228
// Proxy on assembler trait to the underlying register type.
229
impl<R: cranelift_assembler_x64::AsReg> cranelift_assembler_x64::AsReg for Writable<R> {
230
fn enc(&self) -> u8 {
231
self.reg.enc()
232
}
233
234
fn to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String {
235
self.reg.to_string(size)
236
}
237
238
fn new(_: u8) -> Self {
239
panic!("disallow creation of new assembler registers")
240
}
241
}
242
243
// Conversions between regalloc2 types (VReg, PReg) and our types
244
// (VirtualReg, RealReg, Reg).
245
246
impl std::convert::From<regalloc2::VReg> for Reg {
247
fn from(vreg: regalloc2::VReg) -> Reg {
248
Reg(vreg.bits() as u32)
249
}
250
}
251
252
impl std::convert::From<regalloc2::VReg> for VirtualReg {
253
fn from(vreg: regalloc2::VReg) -> VirtualReg {
254
debug_assert!(pinned_vreg_to_preg(vreg).is_none());
255
VirtualReg(vreg)
256
}
257
}
258
259
impl std::convert::From<Reg> for regalloc2::VReg {
260
/// Extract the underlying `regalloc2::VReg`. Note that physical
261
/// registers also map to particular (special) VRegs, so this
262
/// method can be used either on virtual or physical `Reg`s.
263
fn from(reg: Reg) -> regalloc2::VReg {
264
reg.0.into()
265
}
266
}
267
impl std::convert::From<&Reg> for regalloc2::VReg {
268
fn from(reg: &Reg) -> regalloc2::VReg {
269
reg.0.into()
270
}
271
}
272
273
impl std::convert::From<VirtualReg> for regalloc2::VReg {
274
fn from(reg: VirtualReg) -> regalloc2::VReg {
275
reg.0
276
}
277
}
278
279
impl std::convert::From<RealReg> for regalloc2::VReg {
280
fn from(reg: RealReg) -> regalloc2::VReg {
281
// This representation is redundant: the class is implied in the vreg
282
// index as well as being in the vreg class field.
283
VReg::new(reg.0.index(), reg.0.class())
284
}
285
}
286
287
impl std::convert::From<RealReg> for regalloc2::PReg {
288
fn from(reg: RealReg) -> regalloc2::PReg {
289
reg.0
290
}
291
}
292
293
impl std::convert::From<regalloc2::PReg> for RealReg {
294
fn from(preg: regalloc2::PReg) -> RealReg {
295
RealReg(preg)
296
}
297
}
298
299
impl std::convert::From<regalloc2::PReg> for Reg {
300
fn from(preg: regalloc2::PReg) -> Reg {
301
RealReg(preg).into()
302
}
303
}
304
305
impl std::convert::From<RealReg> for Reg {
306
fn from(reg: RealReg) -> Reg {
307
Reg(VReg::from(reg).bits() as u32)
308
}
309
}
310
311
impl std::convert::From<VirtualReg> for Reg {
312
fn from(reg: VirtualReg) -> Reg {
313
Reg(reg.0.bits() as u32)
314
}
315
}
316
317
/// A spill slot.
318
pub type SpillSlot = regalloc2::SpillSlot;
319
320
impl std::convert::From<regalloc2::SpillSlot> for Reg {
321
fn from(spillslot: regalloc2::SpillSlot) -> Reg {
322
Reg(REG_SPILLSLOT_BIT | spillslot.index() as u32)
323
}
324
}
325
326
/// A register class. Each register in the ISA has one class, and the
327
/// classes are disjoint. Most modern ISAs will have just two classes:
328
/// the integer/general-purpose registers (GPRs), and the float/vector
329
/// registers (typically used for both).
330
///
331
/// Note that unlike some other compiler backend/register allocator
332
/// designs, we do not allow for overlapping classes, i.e. registers
333
/// that belong to more than one class, because doing so makes the
334
/// allocation problem significantly more complex. Instead, when a
335
/// register can be addressed under different names for different
336
/// sizes (for example), the backend author should pick classes that
337
/// denote some fundamental allocation unit that encompasses the whole
338
/// register. For example, always allocate 128-bit vector registers
339
/// `v0`..`vN`, even though `f32` and `f64` values may use only the
340
/// low 32/64 bits of those registers and name them differently.
341
pub type RegClass = regalloc2::RegClass;
342
343
/// An OperandCollector is a wrapper around a Vec of Operands
344
/// (flattened array for a whole sequence of instructions) that
345
/// gathers operands from a single instruction and provides the range
346
/// in the flattened array.
347
#[derive(Debug)]
348
pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
349
operands: &'a mut Vec<Operand>,
350
clobbers: PRegSet,
351
352
/// The subset of physical registers that are allocatable.
353
allocatable: PRegSet,
354
355
renamer: F,
356
}
357
358
impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
359
/// Start gathering operands into one flattened operand array.
360
pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
361
Self {
362
operands,
363
clobbers: PRegSet::default(),
364
allocatable,
365
renamer,
366
}
367
}
368
369
/// Finish the operand collection and return the tuple giving the
370
/// range of indices in the flattened operand array, and the
371
/// clobber set.
372
pub fn finish(self) -> (usize, PRegSet) {
373
let end = self.operands.len();
374
(end, self.clobbers)
375
}
376
}
377
378
pub trait OperandVisitor {
379
fn add_operand(
380
&mut self,
381
reg: &mut Reg,
382
constraint: OperandConstraint,
383
kind: OperandKind,
384
pos: OperandPos,
385
);
386
387
fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}
388
389
/// Add a register clobber set. This is a set of registers that
390
/// are written by the instruction, so must be reserved (not used)
391
/// for the whole instruction, but are not used afterward.
392
fn reg_clobbers(&mut self, _regs: PRegSet) {}
393
}
394
395
pub trait OperandVisitorImpl: OperandVisitor {
396
/// Add a use of a fixed, nonallocatable physical register.
397
fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
398
self.debug_assert_is_allocatable_preg(preg, false);
399
// Since this operand does not participate in register allocation,
400
// there's nothing to do here.
401
}
402
403
/// Add a register use, at the start of the instruction (`Before`
404
/// position).
405
fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {
406
self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);
407
}
408
409
/// Add a register use, at the end of the instruction (`After` position).
410
fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
411
self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);
412
}
413
414
/// Add a register def, at the end of the instruction (`After`
415
/// position). Use only when this def will be written after all
416
/// uses are read.
417
fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
418
self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late);
419
}
420
421
/// Add a register "early def", which logically occurs at the
422
/// beginning of the instruction, alongside all uses. Use this
423
/// when the def may be written before all uses are read; the
424
/// regalloc will ensure that it does not overwrite any uses.
425
fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
426
self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);
427
}
428
429
/// Add a register "fixed use", which ties a vreg to a particular
430
/// RealReg at the end of the instruction.
431
fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
432
self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);
433
}
434
435
/// Add a register "fixed use", which ties a vreg to a particular
436
/// RealReg at this point.
437
fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
438
self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);
439
}
440
441
/// Add a register "fixed def", which ties a vreg to a particular
442
/// RealReg at this point.
443
fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) {
444
self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late);
445
}
446
447
/// Add an operand tying a virtual register to a physical register.
448
fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {
449
debug_assert!(reg.is_virtual());
450
let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
451
self.debug_assert_is_allocatable_preg(rreg.into(), true);
452
let constraint = OperandConstraint::FixedReg(rreg.into());
453
self.add_operand(reg, constraint, kind, pos);
454
}
455
456
/// Add an operand which might already be a physical register.
457
fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) {
458
if let Some(rreg) = reg.to_real_reg() {
459
self.reg_fixed_nonallocatable(rreg.into());
460
} else {
461
debug_assert!(reg.is_virtual());
462
self.add_operand(reg, OperandConstraint::Reg, kind, pos);
463
}
464
}
465
466
/// Add a register def that reuses an earlier use-operand's
467
/// allocation. The index of that earlier operand (relative to the
468
/// current instruction's start of operands) must be known.
469
fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) {
470
let reg = reg.reg.as_mut();
471
if let Some(rreg) = reg.to_real_reg() {
472
// In some cases we see real register arguments to a reg_reuse_def
473
// constraint. We assume the creator knows what they're doing
474
// here, though we do also require that the real register be a
475
// fixed-nonallocatable register.
476
self.reg_fixed_nonallocatable(rreg.into());
477
} else {
478
debug_assert!(reg.is_virtual());
479
// The operand we're reusing must not be fixed-nonallocatable, as
480
// that would imply that the register has been allocated to a
481
// virtual register.
482
let constraint = OperandConstraint::Reuse(idx);
483
self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
484
}
485
}
486
487
/// Add a def that can be allocated to either a register or a
488
/// spillslot, at the end of the instruction (`After`
489
/// position). Use only when this def will be written after all
490
/// uses are read.
491
fn any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
492
self.add_operand(
493
reg.reg.as_mut(),
494
OperandConstraint::Any,
495
OperandKind::Def,
496
OperandPos::Late,
497
);
498
}
499
500
/// Add a use that can be allocated to either a register or a
501
/// spillslot, at the end of the instruction (`After` position).
502
fn any_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
503
self.add_operand(
504
reg.as_mut(),
505
OperandConstraint::Any,
506
OperandKind::Use,
507
OperandPos::Late,
508
);
509
}
510
}
511
512
impl<T: OperandVisitor> OperandVisitorImpl for T {}
513
514
impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
515
fn add_operand(
516
&mut self,
517
reg: &mut Reg,
518
constraint: OperandConstraint,
519
kind: OperandKind,
520
pos: OperandPos,
521
) {
522
debug_assert!(!reg.is_spillslot());
523
reg.0 = (self.renamer)(VReg::from(reg.0)).bits() as u32;
524
self.operands
525
.push(Operand::new(VReg::from(reg.0), constraint, kind, pos));
526
}
527
528
fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) {
529
debug_assert_eq!(
530
self.allocatable.contains(reg),
531
expected,
532
"{reg:?} should{} be allocatable",
533
if expected { "" } else { " not" }
534
);
535
}
536
537
fn reg_clobbers(&mut self, regs: PRegSet) {
538
self.clobbers.union_from(regs);
539
}
540
}
541
542
impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {
543
fn add_operand(
544
&mut self,
545
reg: &mut Reg,
546
constraint: OperandConstraint,
547
kind: OperandKind,
548
pos: OperandPos,
549
) {
550
self(reg, constraint, kind, pos)
551
}
552
}
553
554
/// Pretty-print part of a disassembly, with knowledge of
555
/// operand/instruction size, and optionally with regalloc
556
/// results. This can be used, for example, to print either `rax` or
557
/// `eax` for the register by those names on x86-64, depending on a
558
/// 64- or 32-bit context.
559
pub trait PrettyPrint {
560
fn pretty_print(&self, size_bytes: u8) -> String;
561
562
fn pretty_print_default(&self) -> String {
563
self.pretty_print(0)
564
}
565
}
566
567