From 688cd8f68727344622e6e496e41ddf4f97dd3655 Mon Sep 17 00:00:00 2001 From: Jamey Sharp Date: Mon, 29 Apr 2024 08:16:05 -0700 Subject: [PATCH] cranelift: Generalize OperandCollector into a trait (#8499) This paves the way for more implementations of this OperandVisitor trait which can do different things with the operands. Of particular note, this commit introduces a second implementation which is used only in the s390x backend and only to implement a debug assertion. Previously, s390x used an OperandCollector::no_reuse_def method to implement this assertion, but I didn't want to require that all implementors of the new trait have to provide that method, so this captures the same check but keeps it local to where it's needed. --- cranelift/codegen/src/isa/aarch64/inst/mod.rs | 19 +--- .../codegen/src/isa/riscv64/inst/args.rs | 5 +- cranelift/codegen/src/isa/riscv64/inst/mod.rs | 19 +--- .../codegen/src/isa/riscv64/inst/vector.rs | 7 +- cranelift/codegen/src/isa/s390x/inst/mod.rs | 62 +++++++++--- cranelift/codegen/src/isa/x64/inst/args.rs | 41 ++------ cranelift/codegen/src/isa/x64/inst/mod.rs | 6 +- cranelift/codegen/src/machinst/mod.rs | 2 +- cranelift/codegen/src/machinst/reg.rs | 99 ++++++++++--------- 9 files changed, 126 insertions(+), 134 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index 98b7abdeeb..7cffbce30e 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -10,7 +10,7 @@ use crate::{settings, CodegenError, CodegenResult}; use crate::machinst::{PrettyPrint, Reg, RegClass, Writable}; use alloc::vec::Vec; -use regalloc2::{PRegSet, VReg}; +use regalloc2::PRegSet; use smallvec::{smallvec, SmallVec}; use std::fmt::Write; use std::string::{String, ToString}; @@ -396,10 +396,7 @@ impl Inst { //============================================================================= // Instructions: get_regs -fn memarg_operands VReg>( - memarg: &mut AMode, - collector: &mut OperandCollector<'_, F>, -) { +fn memarg_operands(memarg: &mut AMode, collector: &mut impl OperandVisitor) { // This should match `AMode::with_allocs()`. match memarg { AMode::Unscaled { rn, .. } | AMode::UnsignedOffset { rn, .. } => { @@ -423,10 +420,7 @@ fn memarg_operands VReg>( } } -fn pairmemarg_operands VReg>( - pairmemarg: &mut PairAMode, - collector: &mut OperandCollector<'_, F>, -) { +fn pairmemarg_operands(pairmemarg: &mut PairAMode, collector: &mut impl OperandVisitor) { // This should match `PairAMode::with_allocs()`. match pairmemarg { PairAMode::SignedOffset { reg, .. } => { @@ -436,10 +430,7 @@ fn pairmemarg_operands VReg>( } } -fn aarch64_get_operands VReg>( - inst: &mut Inst, - collector: &mut OperandCollector<'_, F>, -) { +fn aarch64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { match inst { Inst::AluRRR { rd, rn, rm, .. } => { collector.reg_def(rd); @@ -997,7 +988,7 @@ impl MachInst for Inst { // debugging. const TRAP_OPCODE: &'static [u8] = &0xc11f_u32.to_le_bytes(); - fn get_operands VReg>(&mut self, collector: &mut OperandCollector<'_, F>) { + fn get_operands(&mut self, collector: &mut impl OperandVisitor) { aarch64_get_operands(self, collector); } diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs index 42326222b7..4a04878893 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/args.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs @@ -127,10 +127,7 @@ impl AMode { /// Add the registers referenced by this AMode to `collector`. /// Keep this in sync with `with_allocs`. - pub(crate) fn get_operands regalloc2::VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { AMode::RegOffset(reg, ..) => collector.reg_use(reg), // Registers used in these modes aren't allocatable. diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs index 91c5a759b0..5172b61c75 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs @@ -13,7 +13,7 @@ use crate::{settings, CodegenError, CodegenResult}; pub use crate::ir::condcodes::FloatCC; use alloc::vec::Vec; -use regalloc2::{PRegSet, RegClass, VReg}; +use regalloc2::{PRegSet, RegClass}; use smallvec::{smallvec, SmallVec}; use std::boxed::Box; use std::fmt::Write; @@ -317,10 +317,7 @@ impl Inst { //============================================================================= -fn vec_mask_operands VReg>( - mask: &mut VecOpMasking, - collector: &mut OperandCollector<'_, F>, -) { +fn vec_mask_operands(mask: &mut VecOpMasking, collector: &mut impl OperandVisitor) { match mask { VecOpMasking::Enabled { reg } => { collector.reg_fixed_use(reg, pv_reg(0).into()); @@ -328,10 +325,7 @@ fn vec_mask_operands VReg>( VecOpMasking::Disabled => {} } } -fn vec_mask_late_operands VReg>( - mask: &mut VecOpMasking, - collector: &mut OperandCollector<'_, F>, -) { +fn vec_mask_late_operands(mask: &mut VecOpMasking, collector: &mut impl OperandVisitor) { match mask { VecOpMasking::Enabled { reg } => { collector.reg_fixed_late_use(reg, pv_reg(0).into()); @@ -340,10 +334,7 @@ fn vec_mask_late_operands VReg>( } } -fn riscv64_get_operands VReg>( - inst: &mut Inst, - collector: &mut OperandCollector<'_, F>, -) { +fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { match inst { Inst::Nop0 | Inst::Nop4 => {} Inst::BrTable { @@ -789,7 +780,7 @@ impl MachInst for Inst { } } - fn get_operands VReg>(&mut self, collector: &mut OperandCollector<'_, F>) { + fn get_operands(&mut self, collector: &mut impl OperandVisitor) { riscv64_get_operands(self, collector); } diff --git a/cranelift/codegen/src/isa/riscv64/inst/vector.rs b/cranelift/codegen/src/isa/riscv64/inst/vector.rs index dc04ba9ca0..496896fb65 100644 --- a/cranelift/codegen/src/isa/riscv64/inst/vector.rs +++ b/cranelift/codegen/src/isa/riscv64/inst/vector.rs @@ -4,7 +4,7 @@ use crate::isa::riscv64::lower::isle::generated_code::{ VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAvl, VecElementWidth, VecLmul, VecMaskMode, VecOpCategory, VecOpMasking, VecTailMode, }; -use crate::machinst::{OperandCollector, RegClass}; +use crate::machinst::{OperandVisitor, RegClass}; use crate::Reg; use core::fmt; @@ -1070,10 +1070,7 @@ impl VecAMode { } } - pub fn get_operands regalloc2::VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { VecAMode::UnitStride { base, .. } => base.get_operands(collector), } diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs index a59c06a93b..1bb6b5fa0f 100644 --- a/cranelift/codegen/src/isa/s390x/inst/mod.rs +++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs @@ -8,7 +8,7 @@ use crate::machinst::*; use crate::{settings, CodegenError, CodegenResult}; use alloc::boxed::Box; use alloc::vec::Vec; -use regalloc2::{PRegSet, VReg}; +use regalloc2::PRegSet; use smallvec::SmallVec; use std::fmt::Write; use std::string::{String, ToString}; @@ -389,10 +389,7 @@ impl Inst { //============================================================================= // Instructions: get_regs -fn memarg_operands VReg>( - memarg: &mut MemArg, - collector: &mut OperandCollector<'_, F>, -) { +fn memarg_operands(memarg: &mut MemArg, collector: &mut impl OperandVisitor) { match memarg { MemArg::BXD12 { base, index, .. } | MemArg::BXD20 { base, index, .. } => { collector.reg_use(base); @@ -409,10 +406,7 @@ fn memarg_operands VReg>( collector.reg_fixed_nonallocatable(gpr_preg(1)); } -fn s390x_get_operands VReg>( - inst: &mut Inst, - collector: &mut OperandCollector<'_, F>, -) { +fn s390x_get_operands(inst: &mut Inst, collector: &mut DenyReuseVisitor) { match inst { Inst::AluRRR { rd, rn, rm, .. } => { collector.reg_def(rd); @@ -952,15 +946,17 @@ fn s390x_get_operands VReg>( memarg_operands(mem, collector); } Inst::Loop { body, .. } => { - for inst in body { - s390x_get_operands(inst, collector); - } - // `reuse_def` constraints can't be permitted in a Loop instruction because the operand // index will always be relative to the Loop instruction, not the individual // instruction in the loop body. However, fixed-nonallocatable registers used with // instructions that would have emitted `reuse_def` constraints are fine. - debug_assert!(collector.no_reuse_def()); + let mut collector = DenyReuseVisitor { + inner: collector.inner, + deny_reuse: true, + }; + for inst in body { + s390x_get_operands(inst, &mut collector); + } } Inst::CondBreak { .. } => {} Inst::VirtualSPOffsetAdj { .. } => {} @@ -971,6 +967,34 @@ fn s390x_get_operands VReg>( } } +struct DenyReuseVisitor<'a, T> { + inner: &'a mut T, + deny_reuse: bool, +} + +impl OperandVisitor for DenyReuseVisitor<'_, T> { + fn add_operand( + &mut self, + reg: &mut Reg, + constraint: regalloc2::OperandConstraint, + kind: regalloc2::OperandKind, + pos: regalloc2::OperandPos, + ) { + debug_assert!( + !self.deny_reuse || !matches!(constraint, regalloc2::OperandConstraint::Reuse(_)) + ); + self.inner.add_operand(reg, constraint, kind, pos); + } + + fn debug_assert_is_allocatable_preg(&self, reg: regalloc2::PReg, expected: bool) { + self.inner.debug_assert_is_allocatable_preg(reg, expected); + } + + fn reg_clobbers(&mut self, regs: PRegSet) { + self.inner.reg_clobbers(regs); + } +} + //============================================================================= // Instructions: misc functions and external interface @@ -979,8 +1003,14 @@ impl MachInst for Inst { type LabelUse = LabelUse; const TRAP_OPCODE: &'static [u8] = &[0, 0]; - fn get_operands VReg>(&mut self, collector: &mut OperandCollector<'_, F>) { - s390x_get_operands(self, collector); + fn get_operands(&mut self, collector: &mut impl OperandVisitor) { + s390x_get_operands( + self, + &mut DenyReuseVisitor { + inner: collector, + deny_reuse: false, + }, + ); } fn is_move(&self) -> Option<(Writable, Reg)> { diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs index 82206958fa..294387945e 100644 --- a/cranelift/codegen/src/isa/x64/inst/args.rs +++ b/cranelift/codegen/src/isa/x64/inst/args.rs @@ -8,7 +8,6 @@ use crate::ir::MemFlags; use crate::isa::x64::inst::regs::pretty_print_reg; use crate::isa::x64::inst::Inst; use crate::machinst::*; -use regalloc2::VReg; use smallvec::{smallvec, SmallVec}; use std::fmt; use std::string::String; @@ -166,10 +165,7 @@ macro_rules! newtype_of_reg { } #[allow(dead_code)] // Used by some newtypes and not others. - pub(crate) fn get_operands VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { self.0.get_operands(collector); } } @@ -234,10 +230,7 @@ macro_rules! newtype_of_reg { } #[allow(dead_code)] // Used by some newtypes and not others. - pub(crate) fn get_operands VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { self.0.get_operands(collector); } } @@ -370,10 +363,7 @@ impl Amode { } /// Add the registers mentioned by `self` to `collector`. - pub(crate) fn get_operands VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { Amode::ImmReg { base, .. } => { if *base != regs::rbp() && *base != regs::rsp() { @@ -395,10 +385,7 @@ impl Amode { } /// Same as `get_operands`, but add the registers in the "late" phase. - pub(crate) fn get_operands_late VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) { match self { Amode::ImmReg { base, .. } => { collector.reg_late_use(base); @@ -535,10 +522,7 @@ impl SyntheticAmode { } /// Add the registers mentioned by `self` to `collector`. - pub(crate) fn get_operands VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { SyntheticAmode::Real(addr) => addr.get_operands(collector), SyntheticAmode::IncomingArg { .. } => { @@ -552,10 +536,7 @@ impl SyntheticAmode { } /// Same as `get_operands`, but add the register in the "late" phase. - pub(crate) fn get_operands_late VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) { match self { SyntheticAmode::Real(addr) => addr.get_operands_late(collector), SyntheticAmode::IncomingArg { .. } => { @@ -686,10 +667,7 @@ impl RegMemImm { } /// Add the regs mentioned by `self` to `collector`. - pub(crate) fn get_operands VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { Self::Reg { reg } => collector.reg_use(reg), Self::Mem { addr } => addr.get_operands(collector), @@ -796,10 +774,7 @@ impl RegMem { } } /// Add the regs mentioned by `self` to `collector`. - pub(crate) fn get_operands VReg>( - &mut self, - collector: &mut OperandCollector<'_, F>, - ) { + pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) { match self { RegMem::Reg { reg } => collector.reg_use(reg), RegMem::Mem { addr, .. } => addr.get_operands(collector), diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 06793ae0b1..ceb0d2962b 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -11,7 +11,7 @@ use crate::isa::{CallConv, FunctionAlignment}; use crate::{machinst::*, trace}; use crate::{settings, CodegenError, CodegenResult}; use alloc::boxed::Box; -use regalloc2::{Allocation, PRegSet, VReg}; +use regalloc2::{Allocation, PRegSet}; use smallvec::{smallvec, SmallVec}; use std::fmt::{self, Write}; use std::string::{String, ToString}; @@ -1895,7 +1895,7 @@ impl fmt::Debug for Inst { } } -fn x64_get_operands VReg>(inst: &mut Inst, collector: &mut OperandCollector<'_, F>) { +fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) { // Note: because we need to statically know the indices of each // reg in the operands list in order to fetch its allocation // later, we put the variable-operand-count bits (the RegMem, @@ -2523,7 +2523,7 @@ fn x64_get_operands VReg>(inst: &mut Inst, collector: &mut Operan impl MachInst for Inst { type ABIMachineSpec = X64ABIMachineSpec; - fn get_operands VReg>(&mut self, collector: &mut OperandCollector<'_, F>) { + fn get_operands(&mut self, collector: &mut impl OperandVisitor) { x64_get_operands(self, collector) } diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index 1055203c30..435d7825d5 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -96,7 +96,7 @@ pub trait MachInst: Clone + Debug { /// Return the registers referenced by this machine instruction along with /// the modes of reference (use, def, modify). - fn get_operands VReg>(&mut self, collector: &mut OperandCollector<'_, F>); + fn get_operands(&mut self, collector: &mut impl OperandVisitor); /// If this is a simple move, return the (source, destination) tuple of registers. fn is_move(&self) -> Option<(Writable, Reg)>; diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs index 40091fede6..5ea6efc379 100644 --- a/cranelift/codegen/src/machinst/reg.rs +++ b/cranelift/codegen/src/machinst/reg.rs @@ -310,45 +310,37 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { } } - /// Returns true if no reuse_def constraints have been added. - pub fn no_reuse_def(&self) -> bool { - !self.operands[self.operands_start..] - .iter() - .any(|operand| match operand.constraint() { - OperandConstraint::Reuse(_) => true, - _ => false, - }) - } - - fn is_allocatable_preg(&self, reg: PReg) -> bool { - self.allocatable.contains(reg) + /// Finish the operand collection and return the tuple giving the + /// range of indices in the flattened operand array, and the + /// clobber set. + pub fn finish(self) -> ((u32, u32), PRegSet) { + let start = self.operands_start as u32; + let end = self.operands.len() as u32; + ((start, end), self.clobbers) } +} - /// Add an operand. +pub trait OperandVisitor { fn add_operand( &mut self, reg: &mut Reg, constraint: OperandConstraint, kind: OperandKind, pos: OperandPos, - ) { - reg.0 = (self.renamer)(reg.0); - self.operands - .push(Operand::new(reg.0, constraint, kind, pos)); - } + ); - /// Finish the operand collection and return the tuple giving the - /// range of indices in the flattened operand array, and the - /// clobber set. - pub fn finish(self) -> ((u32, u32), PRegSet) { - let start = self.operands_start as u32; - let end = self.operands.len() as u32; - ((start, end), self.clobbers) - } + fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {} + /// Add a register clobber set. This is a set of registers that + /// are written by the instruction, so must be reserved (not used) + /// for the whole instruction, but are not used afterward. + fn reg_clobbers(&mut self, _regs: PRegSet) {} +} + +pub trait OperandVisitorImpl: OperandVisitor { /// Add a use of a fixed, nonallocatable physical register. - pub fn reg_fixed_nonallocatable(&mut self, preg: PReg) { - debug_assert!(!self.is_allocatable_preg(preg)); + fn reg_fixed_nonallocatable(&mut self, preg: PReg) { + self.debug_assert_is_allocatable_preg(preg, false); // Magic VReg which does not participate in register allocation. let mut reg = Reg(VReg::new(VReg::MAX, preg.class())); let constraint = OperandConstraint::FixedReg(preg); @@ -358,19 +350,19 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { /// Add a register use, at the start of the instruction (`Before` /// position). - pub fn reg_use(&mut self, reg: &mut impl AsMut) { + fn reg_use(&mut self, reg: &mut impl AsMut) { self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early); } /// Add a register use, at the end of the instruction (`After` position). - pub fn reg_late_use(&mut self, reg: &mut impl AsMut) { + fn reg_late_use(&mut self, reg: &mut impl AsMut) { self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late); } /// Add a register def, at the end of the instruction (`After` /// position). Use only when this def will be written after all /// uses are read. - pub fn reg_def(&mut self, reg: &mut Writable>) { + fn reg_def(&mut self, reg: &mut Writable>) { self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late); } @@ -378,25 +370,25 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { /// beginning of the instruction, alongside all uses. Use this /// when the def may be written before all uses are read; the /// regalloc will ensure that it does not overwrite any uses. - pub fn reg_early_def(&mut self, reg: &mut Writable>) { + fn reg_early_def(&mut self, reg: &mut Writable>) { self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early); } /// Add a register "fixed use", which ties a vreg to a particular /// RealReg at the end of the instruction. - pub fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut, rreg: Reg) { + fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut, rreg: Reg) { self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late); } /// Add a register "fixed use", which ties a vreg to a particular /// RealReg at this point. - pub fn reg_fixed_use(&mut self, reg: &mut impl AsMut, rreg: Reg) { + fn reg_fixed_use(&mut self, reg: &mut impl AsMut, rreg: Reg) { self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early); } /// Add a register "fixed def", which ties a vreg to a particular /// RealReg at this point. - pub fn reg_fixed_def(&mut self, reg: &mut Writable>, rreg: Reg) { + fn reg_fixed_def(&mut self, reg: &mut Writable>, rreg: Reg) { self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late); } @@ -404,10 +396,7 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) { debug_assert!(reg.is_virtual()); let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg"); - debug_assert!( - self.is_allocatable_preg(rreg.into()), - "{rreg:?} is not allocatable" - ); + self.debug_assert_is_allocatable_preg(rreg.into(), true); let constraint = OperandConstraint::FixedReg(rreg.into()); self.add_operand(reg, constraint, kind, pos); } @@ -425,7 +414,7 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { /// Add a register def that reuses an earlier use-operand's /// allocation. The index of that earlier operand (relative to the /// current instruction's start of operands) must be known. - pub fn reg_reuse_def(&mut self, reg: &mut Writable>, idx: usize) { + fn reg_reuse_def(&mut self, reg: &mut Writable>, idx: usize) { let reg = reg.reg.as_mut(); if let Some(rreg) = reg.to_real_reg() { // In some cases we see real register arguments to a reg_reuse_def @@ -442,11 +431,33 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> { self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late); } } +} - /// Add a register clobber set. This is a set of registers that - /// are written by the instruction, so must be reserved (not used) - /// for the whole instruction, but are not used afterward. - pub fn reg_clobbers(&mut self, regs: PRegSet) { +impl OperandVisitorImpl for T {} + +impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> { + fn add_operand( + &mut self, + reg: &mut Reg, + constraint: OperandConstraint, + kind: OperandKind, + pos: OperandPos, + ) { + reg.0 = (self.renamer)(reg.0); + self.operands + .push(Operand::new(reg.0, constraint, kind, pos)); + } + + fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) { + debug_assert_eq!( + self.allocatable.contains(reg), + expected, + "{reg:?} should{} be allocatable", + if expected { "" } else { " not" } + ); + } + + fn reg_clobbers(&mut self, regs: PRegSet) { self.clobbers.union_from(regs); } }