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