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