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