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.
pinned_vreg_to_preg(vreg: VReg) -> Option<PReg>19 pub 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`.
preg_to_pinned_vreg(preg: PReg) -> VReg28 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).
first_user_vreg_index() -> usize34 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.
from_virtual_reg(vreg: regalloc2::VReg) -> Reg56     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.
from_real_reg(preg: regalloc2::PReg) -> Reg63     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.
from_virtual_reg_checked(vreg: regalloc2::VReg) -> Option<Reg>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.
to_real_reg(self) -> Option<RealReg>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.
to_virtual_reg(self) -> Option<VirtualReg>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.
to_spillslot(self) -> Option<SpillSlot>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.
class(self) -> RegClass113     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?
is_real(self) -> bool119     pub fn is_real(self) -> bool {
120         self.to_real_reg().is_some()
121     }
122 
123     /// Is this a virtual reg?
is_virtual(self) -> bool124     pub fn is_virtual(self) -> bool {
125         self.to_virtual_reg().is_some()
126     }
127 
128     /// Is this a spillslot?
is_spillslot(self) -> bool129     pub fn is_spillslot(self) -> bool {
130         self.to_spillslot().is_some()
131     }
132 }
133 
134 impl core::fmt::Debug for Reg {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result135     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 
152 impl AsMut<Reg> for Reg {
as_mut(&mut self) -> &mut Reg153     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))]
162 pub struct RealReg(PReg);
163 
164 impl RealReg {
165     /// Get the class of this register.
class(self) -> RegClass166     pub fn class(self) -> RegClass {
167         self.0.class()
168     }
169 
170     /// The physical register number.
hw_enc(self) -> u8171     pub fn hw_enc(self) -> u8 {
172         self.0.hw_enc() as u8
173     }
174 
175     /// The underlying PReg.
preg(self) -> PReg176     pub const fn preg(self) -> PReg {
177         self.0
178     }
179 }
180 
181 impl core::fmt::Debug for RealReg {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result182     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))]
194 pub struct VirtualReg(VReg);
195 
196 impl VirtualReg {
197     /// Get the class of this register.
class(self) -> RegClass198     pub fn class(self) -> RegClass {
199         self.0.class()
200     }
201 
index(self) -> usize202     pub fn index(self) -> usize {
203         self.0.vreg()
204     }
205 }
206 
207 impl core::fmt::Debug for VirtualReg {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result208     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))]
224 pub struct Writable<T> {
225     reg: T,
226 }
227 
228 impl<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.
from_reg(reg: T) -> Writable<T>233     pub const fn from_reg(reg: T) -> Writable<T> {
234         Writable { reg }
235     }
236 
237     /// Get the underlying register, which can be read.
to_reg(self) -> T238     pub fn to_reg(self) -> T {
239         self.reg
240     }
241 
242     /// Get a mutable borrow of the underlying register.
reg_mut(&mut self) -> &mut T243     pub fn reg_mut(&mut self) -> &mut T {
244         &mut self.reg
245     }
246 
247     /// Map the underlying register to another value or type.
map<U>(self, f: impl Fn(T) -> U) -> Writable<U>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.
254 impl<R: cranelift_assembler_x64::AsReg> cranelift_assembler_x64::AsReg for Writable<R> {
enc(&self) -> u8255     fn enc(&self) -> u8 {
256         self.reg.enc()
257     }
258 
to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String259     fn to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String {
260         self.reg.to_string(size)
261     }
262 
new(_: u8) -> Self263     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 
271 impl core::convert::From<regalloc2::VReg> for Reg {
from(vreg: regalloc2::VReg) -> Reg272     fn from(vreg: regalloc2::VReg) -> Reg {
273         Reg(vreg.bits() as u32)
274     }
275 }
276 
277 impl core::convert::From<regalloc2::VReg> for VirtualReg {
from(vreg: regalloc2::VReg) -> VirtualReg278     fn from(vreg: regalloc2::VReg) -> VirtualReg {
279         debug_assert!(pinned_vreg_to_preg(vreg).is_none());
280         VirtualReg(vreg)
281     }
282 }
283 
284 impl 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.
from(reg: Reg) -> regalloc2::VReg288     fn from(reg: Reg) -> regalloc2::VReg {
289         reg.0.into()
290     }
291 }
292 impl core::convert::From<&Reg> for regalloc2::VReg {
from(reg: &Reg) -> regalloc2::VReg293     fn from(reg: &Reg) -> regalloc2::VReg {
294         reg.0.into()
295     }
296 }
297 
298 impl core::convert::From<VirtualReg> for regalloc2::VReg {
from(reg: VirtualReg) -> regalloc2::VReg299     fn from(reg: VirtualReg) -> regalloc2::VReg {
300         reg.0
301     }
302 }
303 
304 impl core::convert::From<RealReg> for regalloc2::VReg {
from(reg: RealReg) -> regalloc2::VReg305     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 
312 impl core::convert::From<RealReg> for regalloc2::PReg {
from(reg: RealReg) -> regalloc2::PReg313     fn from(reg: RealReg) -> regalloc2::PReg {
314         reg.0
315     }
316 }
317 
318 impl core::convert::From<regalloc2::PReg> for RealReg {
from(preg: regalloc2::PReg) -> RealReg319     fn from(preg: regalloc2::PReg) -> RealReg {
320         RealReg(preg)
321     }
322 }
323 
324 impl core::convert::From<regalloc2::PReg> for Reg {
from(preg: regalloc2::PReg) -> Reg325     fn from(preg: regalloc2::PReg) -> Reg {
326         RealReg(preg).into()
327     }
328 }
329 
330 impl core::convert::From<RealReg> for Reg {
from(reg: RealReg) -> Reg331     fn from(reg: RealReg) -> Reg {
332         Reg(VReg::from(reg).bits() as u32)
333     }
334 }
335 
336 impl core::convert::From<VirtualReg> for Reg {
from(reg: VirtualReg) -> Reg337     fn from(reg: VirtualReg) -> Reg {
338         Reg(reg.0.bits() as u32)
339     }
340 }
341 
342 /// A spill slot.
343 pub type SpillSlot = regalloc2::SpillSlot;
344 
345 impl core::convert::From<regalloc2::SpillSlot> for Reg {
from(spillslot: regalloc2::SpillSlot) -> Reg346     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.
366 pub 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)]
373 pub 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 
383 impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
384     /// Start gathering operands into one flattened operand array.
new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self385     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.
finish(self) -> (usize, PRegSet)397     pub fn finish(self) -> (usize, PRegSet) {
398         let end = self.operands.len();
399         (end, self.clobbers)
400     }
401 }
402 
403 pub trait OperandVisitor {
add_operand( &mut self, reg: &mut Reg, constraint: OperandConstraint, kind: OperandKind, pos: OperandPos, )404     fn add_operand(
405         &mut self,
406         reg: &mut Reg,
407         constraint: OperandConstraint,
408         kind: OperandKind,
409         pos: OperandPos,
410     );
411 
debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool)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.
reg_clobbers(&mut self, _regs: PRegSet)417     fn reg_clobbers(&mut self, _regs: PRegSet) {}
418 }
419 
420 pub trait OperandVisitorImpl: OperandVisitor {
421     /// Add a use of a fixed, nonallocatable physical register.
reg_fixed_nonallocatable(&mut self, preg: PReg)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).
reg_use(&mut self, reg: &mut impl AsMut<Reg>)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).
reg_late_use(&mut self, reg: &mut impl AsMut<Reg>)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.
reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>)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.
reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>)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.
reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg)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.
reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg)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.
reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg)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.
reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos)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.
reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos)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.
reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize)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.
any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>)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).
any_late_use(&mut self, reg: &mut impl AsMut<Reg>)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 
537 impl<T: OperandVisitor> OperandVisitorImpl for T {}
538 
539 impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
add_operand( &mut self, reg: &mut Reg, constraint: OperandConstraint, kind: OperandKind, pos: OperandPos, )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 
debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool)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 
reg_clobbers(&mut self, regs: PRegSet)562     fn reg_clobbers(&mut self, regs: PRegSet) {
563         self.clobbers.union_from(regs);
564     }
565 }
566 
567 impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {
add_operand( &mut self, reg: &mut Reg, constraint: OperandConstraint, kind: OperandKind, pos: OperandPos, )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.
584 pub trait PrettyPrint {
pretty_print(&self, size_bytes: u8) -> String585     fn pretty_print(&self, size_bytes: u8) -> String;
586 
pretty_print_default(&self) -> String587     fn pretty_print_default(&self) -> String {
588         self.pretty_print(0)
589     }
590 }
591