Skip to main content

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