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