Browse Source

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.
pull/8505/head
Jamey Sharp 6 months ago
committed by GitHub
parent
commit
688cd8f687
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 19
      cranelift/codegen/src/isa/aarch64/inst/mod.rs
  2. 5
      cranelift/codegen/src/isa/riscv64/inst/args.rs
  3. 19
      cranelift/codegen/src/isa/riscv64/inst/mod.rs
  4. 7
      cranelift/codegen/src/isa/riscv64/inst/vector.rs
  5. 62
      cranelift/codegen/src/isa/s390x/inst/mod.rs
  6. 41
      cranelift/codegen/src/isa/x64/inst/args.rs
  7. 6
      cranelift/codegen/src/isa/x64/inst/mod.rs
  8. 2
      cranelift/codegen/src/machinst/mod.rs
  9. 99
      cranelift/codegen/src/machinst/reg.rs

19
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<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(
}
}
fn pairmemarg_operands<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(
}
}
fn aarch64_get_operands<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(&mut self, collector: &mut OperandCollector<'_, F>) {
fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
aarch64_get_operands(self, collector);
}

5
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<F: Fn(regalloc2::VReg) -> 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.

19
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<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(
VecOpMasking::Disabled => {}
}
}
fn vec_mask_late_operands<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(
}
}
fn riscv64_get_operands<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(&mut self, collector: &mut OperandCollector<'_, F>) {
fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
riscv64_get_operands(self, collector);
}

7
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<F: Fn(regalloc2::VReg) -> 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),
}

62
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<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(
collector.reg_fixed_nonallocatable(gpr_preg(1));
}
fn s390x_get_operands<F: Fn(VReg) -> VReg>(
inst: &mut Inst,
collector: &mut OperandCollector<'_, F>,
) {
fn s390x_get_operands(inst: &mut Inst, collector: &mut DenyReuseVisitor<impl OperandVisitor>) {
match inst {
Inst::AluRRR { rd, rn, rm, .. } => {
collector.reg_def(rd);
@ -952,15 +946,17 @@ fn s390x_get_operands<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(
}
}
struct DenyReuseVisitor<'a, T> {
inner: &'a mut T,
deny_reuse: bool,
}
impl<T: OperandVisitor> 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<F: Fn(VReg) -> 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>, Reg)> {

41
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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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<F: Fn(VReg) -> 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),

6
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<F: Fn(VReg) -> 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<F: Fn(VReg) -> VReg>(inst: &mut Inst, collector: &mut Operan
impl MachInst for Inst {
type ABIMachineSpec = X64ABIMachineSpec;
fn get_operands<F: Fn(VReg) -> VReg>(&mut self, collector: &mut OperandCollector<'_, F>) {
fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
x64_get_operands(self, collector)
}

2
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<F: Fn(VReg) -> 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>, Reg)>;

99
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<Reg>) {
fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {
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<Reg>) {
fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
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<impl AsMut<Reg>>) {
fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
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<impl AsMut<Reg>>) {
fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
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<Reg>, rreg: Reg) {
fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, 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<Reg>, rreg: Reg) {
fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, 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<impl AsMut<Reg>>, rreg: Reg) {
fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, 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<impl AsMut<Reg>>, idx: usize) {
fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, 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<T: OperandVisitor> 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);
}
}

Loading…
Cancel
Save