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