cranelift_codegen/machinst/
reg.rs

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
5use alloc::{string::String, vec::Vec};
6use core::{fmt::Debug, hash::Hash};
7use regalloc2::{Operand, OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, VReg};
8
9#[cfg(feature = "enable-serde")]
10use 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.
16const PINNED_VREGS: usize = 192;
17
18/// Convert a `VReg` to its pinned `PReg`, if any.
19pub 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`.
28pub 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).
34pub 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))]
49pub struct Reg(u32);
50
51const REG_SPILLSLOT_BIT: u32 = 0x8000_0000;
52const REG_SPILLSLOT_MASK: u32 = !REG_SPILLSLOT_BIT;
53
54impl 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
114impl 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
132impl 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))]
142pub struct RealReg(PReg);
143
144impl 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
156impl 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))]
169pub struct VirtualReg(VReg);
170
171impl 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
182impl 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))]
199pub struct Writable<T> {
200    reg: T,
201}
202
203impl<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 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// Conversions between regalloc2 types (VReg, PReg) and our types
229// (VirtualReg, RealReg, Reg).
230
231impl std::convert::From<regalloc2::VReg> for Reg {
232    fn from(vreg: regalloc2::VReg) -> Reg {
233        Reg(vreg.bits() as u32)
234    }
235}
236
237impl std::convert::From<regalloc2::VReg> for VirtualReg {
238    fn from(vreg: regalloc2::VReg) -> VirtualReg {
239        debug_assert!(pinned_vreg_to_preg(vreg).is_none());
240        VirtualReg(vreg)
241    }
242}
243
244impl std::convert::From<Reg> for regalloc2::VReg {
245    /// Extract the underlying `regalloc2::VReg`. Note that physical
246    /// registers also map to particular (special) VRegs, so this
247    /// method can be used either on virtual or physical `Reg`s.
248    fn from(reg: Reg) -> regalloc2::VReg {
249        reg.0.into()
250    }
251}
252impl std::convert::From<&Reg> for regalloc2::VReg {
253    fn from(reg: &Reg) -> regalloc2::VReg {
254        reg.0.into()
255    }
256}
257
258impl std::convert::From<VirtualReg> for regalloc2::VReg {
259    fn from(reg: VirtualReg) -> regalloc2::VReg {
260        reg.0
261    }
262}
263
264impl std::convert::From<RealReg> for regalloc2::VReg {
265    fn from(reg: RealReg) -> regalloc2::VReg {
266        // This representation is redundant: the class is implied in the vreg
267        // index as well as being in the vreg class field.
268        VReg::new(reg.0.index(), reg.0.class())
269    }
270}
271
272impl std::convert::From<RealReg> for regalloc2::PReg {
273    fn from(reg: RealReg) -> regalloc2::PReg {
274        reg.0
275    }
276}
277
278impl std::convert::From<regalloc2::PReg> for RealReg {
279    fn from(preg: regalloc2::PReg) -> RealReg {
280        RealReg(preg)
281    }
282}
283
284impl std::convert::From<regalloc2::PReg> for Reg {
285    fn from(preg: regalloc2::PReg) -> Reg {
286        RealReg(preg).into()
287    }
288}
289
290impl std::convert::From<RealReg> for Reg {
291    fn from(reg: RealReg) -> Reg {
292        Reg(VReg::from(reg).bits() as u32)
293    }
294}
295
296impl std::convert::From<VirtualReg> for Reg {
297    fn from(reg: VirtualReg) -> Reg {
298        Reg(reg.0.bits() as u32)
299    }
300}
301
302/// A spill slot.
303pub type SpillSlot = regalloc2::SpillSlot;
304
305impl std::convert::From<regalloc2::SpillSlot> for Reg {
306    fn from(spillslot: regalloc2::SpillSlot) -> Reg {
307        Reg(REG_SPILLSLOT_BIT | spillslot.index() as u32)
308    }
309}
310
311/// A register class. Each register in the ISA has one class, and the
312/// classes are disjoint. Most modern ISAs will have just two classes:
313/// the integer/general-purpose registers (GPRs), and the float/vector
314/// registers (typically used for both).
315///
316/// Note that unlike some other compiler backend/register allocator
317/// designs, we do not allow for overlapping classes, i.e. registers
318/// that belong to more than one class, because doing so makes the
319/// allocation problem significantly more complex. Instead, when a
320/// register can be addressed under different names for different
321/// sizes (for example), the backend author should pick classes that
322/// denote some fundamental allocation unit that encompasses the whole
323/// register. For example, always allocate 128-bit vector registers
324/// `v0`..`vN`, even though `f32` and `f64` values may use only the
325/// low 32/64 bits of those registers and name them differently.
326pub type RegClass = regalloc2::RegClass;
327
328/// An OperandCollector is a wrapper around a Vec of Operands
329/// (flattened array for a whole sequence of instructions) that
330/// gathers operands from a single instruction and provides the range
331/// in the flattened array.
332#[derive(Debug)]
333pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
334    operands: &'a mut Vec<Operand>,
335    clobbers: PRegSet,
336
337    /// The subset of physical registers that are allocatable.
338    allocatable: PRegSet,
339
340    renamer: F,
341}
342
343impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
344    /// Start gathering operands into one flattened operand array.
345    pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
346        Self {
347            operands,
348            clobbers: PRegSet::default(),
349            allocatable,
350            renamer,
351        }
352    }
353
354    /// Finish the operand collection and return the tuple giving the
355    /// range of indices in the flattened operand array, and the
356    /// clobber set.
357    pub fn finish(self) -> (usize, PRegSet) {
358        let end = self.operands.len();
359        (end, self.clobbers)
360    }
361}
362
363pub trait OperandVisitor {
364    fn add_operand(
365        &mut self,
366        reg: &mut Reg,
367        constraint: OperandConstraint,
368        kind: OperandKind,
369        pos: OperandPos,
370    );
371
372    fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}
373
374    /// Add a register clobber set. This is a set of registers that
375    /// are written by the instruction, so must be reserved (not used)
376    /// for the whole instruction, but are not used afterward.
377    fn reg_clobbers(&mut self, _regs: PRegSet) {}
378}
379
380pub trait OperandVisitorImpl: OperandVisitor {
381    /// Add a use of a fixed, nonallocatable physical register.
382    fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
383        self.debug_assert_is_allocatable_preg(preg, false);
384        // Since this operand does not participate in register allocation,
385        // there's nothing to do here.
386    }
387
388    /// Add a register use, at the start of the instruction (`Before`
389    /// position).
390    fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {
391        self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);
392    }
393
394    /// Add a register use, at the end of the instruction (`After` position).
395    fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
396        self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);
397    }
398
399    /// Add a register def, at the end of the instruction (`After`
400    /// position). Use only when this def will be written after all
401    /// uses are read.
402    fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
403        self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late);
404    }
405
406    /// Add a register "early def", which logically occurs at the
407    /// beginning of the instruction, alongside all uses. Use this
408    /// when the def may be written before all uses are read; the
409    /// regalloc will ensure that it does not overwrite any uses.
410    fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
411        self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);
412    }
413
414    /// Add a register "fixed use", which ties a vreg to a particular
415    /// RealReg at the end of the instruction.
416    fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
417        self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);
418    }
419
420    /// Add a register "fixed use", which ties a vreg to a particular
421    /// RealReg at this point.
422    fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
423        self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);
424    }
425
426    /// Add a register "fixed def", which ties a vreg to a particular
427    /// RealReg at this point.
428    fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) {
429        self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late);
430    }
431
432    /// Add an operand tying a virtual register to a physical register.
433    fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {
434        debug_assert!(reg.is_virtual());
435        let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
436        self.debug_assert_is_allocatable_preg(rreg.into(), true);
437        let constraint = OperandConstraint::FixedReg(rreg.into());
438        self.add_operand(reg, constraint, kind, pos);
439    }
440
441    /// Add an operand which might already be a physical register.
442    fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) {
443        if let Some(rreg) = reg.to_real_reg() {
444            self.reg_fixed_nonallocatable(rreg.into());
445        } else {
446            debug_assert!(reg.is_virtual());
447            self.add_operand(reg, OperandConstraint::Reg, kind, pos);
448        }
449    }
450
451    /// Add a register def that reuses an earlier use-operand's
452    /// allocation. The index of that earlier operand (relative to the
453    /// current instruction's start of operands) must be known.
454    fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) {
455        let reg = reg.reg.as_mut();
456        if let Some(rreg) = reg.to_real_reg() {
457            // In some cases we see real register arguments to a reg_reuse_def
458            // constraint. We assume the creator knows what they're doing
459            // here, though we do also require that the real register be a
460            // fixed-nonallocatable register.
461            self.reg_fixed_nonallocatable(rreg.into());
462        } else {
463            debug_assert!(reg.is_virtual());
464            // The operand we're reusing must not be fixed-nonallocatable, as
465            // that would imply that the register has been allocated to a
466            // virtual register.
467            let constraint = OperandConstraint::Reuse(idx);
468            self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
469        }
470    }
471
472    /// Add a def that can be allocated to either a register or a
473    /// spillslot, at the end of the instruction (`After`
474    /// position). Use only when this def will be written after all
475    /// uses are read.
476    fn any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
477        self.add_operand(
478            reg.reg.as_mut(),
479            OperandConstraint::Any,
480            OperandKind::Def,
481            OperandPos::Late,
482        );
483    }
484}
485
486impl<T: OperandVisitor> OperandVisitorImpl for T {}
487
488impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
489    fn add_operand(
490        &mut self,
491        reg: &mut Reg,
492        constraint: OperandConstraint,
493        kind: OperandKind,
494        pos: OperandPos,
495    ) {
496        debug_assert!(!reg.is_spillslot());
497        reg.0 = (self.renamer)(VReg::from(reg.0)).bits() as u32;
498        self.operands
499            .push(Operand::new(VReg::from(reg.0), constraint, kind, pos));
500    }
501
502    fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) {
503        debug_assert_eq!(
504            self.allocatable.contains(reg),
505            expected,
506            "{reg:?} should{} be allocatable",
507            if expected { "" } else { " not" }
508        );
509    }
510
511    fn reg_clobbers(&mut self, regs: PRegSet) {
512        self.clobbers.union_from(regs);
513    }
514}
515
516impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {
517    fn add_operand(
518        &mut self,
519        reg: &mut Reg,
520        constraint: OperandConstraint,
521        kind: OperandKind,
522        pos: OperandPos,
523    ) {
524        self(reg, constraint, kind, pos)
525    }
526}
527
528/// Pretty-print part of a disassembly, with knowledge of
529/// operand/instruction size, and optionally with regalloc
530/// results. This can be used, for example, to print either `rax` or
531/// `eax` for the register by those names on x86-64, depending on a
532/// 64- or 32-bit context.
533pub trait PrettyPrint {
534    fn pretty_print(&self, size_bytes: u8) -> String;
535
536    fn pretty_print_default(&self) -> String {
537        self.pretty_print(0)
538    }
539}