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 crate::machinst::{PrettyPrint, Reg, RegClass, Writable};
use alloc::vec::Vec; use alloc::vec::Vec;
use regalloc2::{PRegSet, VReg}; use regalloc2::PRegSet;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::fmt::Write; use std::fmt::Write;
use std::string::{String, ToString}; use std::string::{String, ToString};
@ -396,10 +396,7 @@ impl Inst {
//============================================================================= //=============================================================================
// Instructions: get_regs // Instructions: get_regs
fn memarg_operands<F: Fn(VReg) -> VReg>( fn memarg_operands(memarg: &mut AMode, collector: &mut impl OperandVisitor) {
memarg: &mut AMode,
collector: &mut OperandCollector<'_, F>,
) {
// This should match `AMode::with_allocs()`. // This should match `AMode::with_allocs()`.
match memarg { match memarg {
AMode::Unscaled { rn, .. } | AMode::UnsignedOffset { rn, .. } => { AMode::Unscaled { rn, .. } | AMode::UnsignedOffset { rn, .. } => {
@ -423,10 +420,7 @@ fn memarg_operands<F: Fn(VReg) -> VReg>(
} }
} }
fn pairmemarg_operands<F: Fn(VReg) -> VReg>( fn pairmemarg_operands(pairmemarg: &mut PairAMode, collector: &mut impl OperandVisitor) {
pairmemarg: &mut PairAMode,
collector: &mut OperandCollector<'_, F>,
) {
// This should match `PairAMode::with_allocs()`. // This should match `PairAMode::with_allocs()`.
match pairmemarg { match pairmemarg {
PairAMode::SignedOffset { reg, .. } => { PairAMode::SignedOffset { reg, .. } => {
@ -436,10 +430,7 @@ fn pairmemarg_operands<F: Fn(VReg) -> VReg>(
} }
} }
fn aarch64_get_operands<F: Fn(VReg) -> VReg>( fn aarch64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
inst: &mut Inst,
collector: &mut OperandCollector<'_, F>,
) {
match inst { match inst {
Inst::AluRRR { rd, rn, rm, .. } => { Inst::AluRRR { rd, rn, rm, .. } => {
collector.reg_def(rd); collector.reg_def(rd);
@ -997,7 +988,7 @@ impl MachInst for Inst {
// debugging. // debugging.
const TRAP_OPCODE: &'static [u8] = &0xc11f_u32.to_le_bytes(); 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); 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`. /// Add the registers referenced by this AMode to `collector`.
/// Keep this in sync with `with_allocs`. /// Keep this in sync with `with_allocs`.
pub(crate) fn get_operands<F: Fn(regalloc2::VReg) -> regalloc2::VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
AMode::RegOffset(reg, ..) => collector.reg_use(reg), AMode::RegOffset(reg, ..) => collector.reg_use(reg),
// Registers used in these modes aren't allocatable. // 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; pub use crate::ir::condcodes::FloatCC;
use alloc::vec::Vec; use alloc::vec::Vec;
use regalloc2::{PRegSet, RegClass, VReg}; use regalloc2::{PRegSet, RegClass};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::boxed::Box; use std::boxed::Box;
use std::fmt::Write; use std::fmt::Write;
@ -317,10 +317,7 @@ impl Inst {
//============================================================================= //=============================================================================
fn vec_mask_operands<F: Fn(VReg) -> VReg>( fn vec_mask_operands(mask: &mut VecOpMasking, collector: &mut impl OperandVisitor) {
mask: &mut VecOpMasking,
collector: &mut OperandCollector<'_, F>,
) {
match mask { match mask {
VecOpMasking::Enabled { reg } => { VecOpMasking::Enabled { reg } => {
collector.reg_fixed_use(reg, pv_reg(0).into()); collector.reg_fixed_use(reg, pv_reg(0).into());
@ -328,10 +325,7 @@ fn vec_mask_operands<F: Fn(VReg) -> VReg>(
VecOpMasking::Disabled => {} VecOpMasking::Disabled => {}
} }
} }
fn vec_mask_late_operands<F: Fn(VReg) -> VReg>( fn vec_mask_late_operands(mask: &mut VecOpMasking, collector: &mut impl OperandVisitor) {
mask: &mut VecOpMasking,
collector: &mut OperandCollector<'_, F>,
) {
match mask { match mask {
VecOpMasking::Enabled { reg } => { VecOpMasking::Enabled { reg } => {
collector.reg_fixed_late_use(reg, pv_reg(0).into()); 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>( fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
inst: &mut Inst,
collector: &mut OperandCollector<'_, F>,
) {
match inst { match inst {
Inst::Nop0 | Inst::Nop4 => {} Inst::Nop0 | Inst::Nop4 => {}
Inst::BrTable { 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); 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, VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAvl,
VecElementWidth, VecLmul, VecMaskMode, VecOpCategory, VecOpMasking, VecTailMode, VecElementWidth, VecLmul, VecMaskMode, VecOpCategory, VecOpMasking, VecTailMode,
}; };
use crate::machinst::{OperandCollector, RegClass}; use crate::machinst::{OperandVisitor, RegClass};
use crate::Reg; use crate::Reg;
use core::fmt; use core::fmt;
@ -1070,10 +1070,7 @@ impl VecAMode {
} }
} }
pub fn get_operands<F: Fn(regalloc2::VReg) -> regalloc2::VReg>( pub fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
VecAMode::UnitStride { base, .. } => base.get_operands(collector), 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 crate::{settings, CodegenError, CodegenResult};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
use regalloc2::{PRegSet, VReg}; use regalloc2::PRegSet;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt::Write; use std::fmt::Write;
use std::string::{String, ToString}; use std::string::{String, ToString};
@ -389,10 +389,7 @@ impl Inst {
//============================================================================= //=============================================================================
// Instructions: get_regs // Instructions: get_regs
fn memarg_operands<F: Fn(VReg) -> VReg>( fn memarg_operands(memarg: &mut MemArg, collector: &mut impl OperandVisitor) {
memarg: &mut MemArg,
collector: &mut OperandCollector<'_, F>,
) {
match memarg { match memarg {
MemArg::BXD12 { base, index, .. } | MemArg::BXD20 { base, index, .. } => { MemArg::BXD12 { base, index, .. } | MemArg::BXD20 { base, index, .. } => {
collector.reg_use(base); collector.reg_use(base);
@ -409,10 +406,7 @@ fn memarg_operands<F: Fn(VReg) -> VReg>(
collector.reg_fixed_nonallocatable(gpr_preg(1)); collector.reg_fixed_nonallocatable(gpr_preg(1));
} }
fn s390x_get_operands<F: Fn(VReg) -> VReg>( fn s390x_get_operands(inst: &mut Inst, collector: &mut DenyReuseVisitor<impl OperandVisitor>) {
inst: &mut Inst,
collector: &mut OperandCollector<'_, F>,
) {
match inst { match inst {
Inst::AluRRR { rd, rn, rm, .. } => { Inst::AluRRR { rd, rn, rm, .. } => {
collector.reg_def(rd); collector.reg_def(rd);
@ -952,15 +946,17 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(
memarg_operands(mem, collector); memarg_operands(mem, collector);
} }
Inst::Loop { body, .. } => { 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 // `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 // index will always be relative to the Loop instruction, not the individual
// instruction in the loop body. However, fixed-nonallocatable registers used with // instruction in the loop body. However, fixed-nonallocatable registers used with
// instructions that would have emitted `reuse_def` constraints are fine. // 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::CondBreak { .. } => {}
Inst::VirtualSPOffsetAdj { .. } => {} 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 // Instructions: misc functions and external interface
@ -979,8 +1003,14 @@ impl MachInst for Inst {
type LabelUse = LabelUse; type LabelUse = LabelUse;
const TRAP_OPCODE: &'static [u8] = &[0, 0]; const TRAP_OPCODE: &'static [u8] = &[0, 0];
fn get_operands<F: Fn(VReg) -> VReg>(&mut self, collector: &mut OperandCollector<'_, F>) { fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
s390x_get_operands(self, collector); s390x_get_operands(
self,
&mut DenyReuseVisitor {
inner: collector,
deny_reuse: false,
},
);
} }
fn is_move(&self) -> Option<(Writable<Reg>, Reg)> { 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::regs::pretty_print_reg;
use crate::isa::x64::inst::Inst; use crate::isa::x64::inst::Inst;
use crate::machinst::*; use crate::machinst::*;
use regalloc2::VReg;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::fmt; use std::fmt;
use std::string::String; use std::string::String;
@ -166,10 +165,7 @@ macro_rules! newtype_of_reg {
} }
#[allow(dead_code)] // Used by some newtypes and not others. #[allow(dead_code)] // Used by some newtypes and not others.
pub(crate) fn get_operands<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
self.0.get_operands(collector); self.0.get_operands(collector);
} }
} }
@ -234,10 +230,7 @@ macro_rules! newtype_of_reg {
} }
#[allow(dead_code)] // Used by some newtypes and not others. #[allow(dead_code)] // Used by some newtypes and not others.
pub(crate) fn get_operands<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
self.0.get_operands(collector); self.0.get_operands(collector);
} }
} }
@ -370,10 +363,7 @@ impl Amode {
} }
/// Add the registers mentioned by `self` to `collector`. /// Add the registers mentioned by `self` to `collector`.
pub(crate) fn get_operands<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
Amode::ImmReg { base, .. } => { Amode::ImmReg { base, .. } => {
if *base != regs::rbp() && *base != regs::rsp() { 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. /// Same as `get_operands`, but add the registers in the "late" phase.
pub(crate) fn get_operands_late<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
Amode::ImmReg { base, .. } => { Amode::ImmReg { base, .. } => {
collector.reg_late_use(base); collector.reg_late_use(base);
@ -535,10 +522,7 @@ impl SyntheticAmode {
} }
/// Add the registers mentioned by `self` to `collector`. /// Add the registers mentioned by `self` to `collector`.
pub(crate) fn get_operands<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
SyntheticAmode::Real(addr) => addr.get_operands(collector), SyntheticAmode::Real(addr) => addr.get_operands(collector),
SyntheticAmode::IncomingArg { .. } => { SyntheticAmode::IncomingArg { .. } => {
@ -552,10 +536,7 @@ impl SyntheticAmode {
} }
/// Same as `get_operands`, but add the register in the "late" phase. /// Same as `get_operands`, but add the register in the "late" phase.
pub(crate) fn get_operands_late<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
SyntheticAmode::Real(addr) => addr.get_operands_late(collector), SyntheticAmode::Real(addr) => addr.get_operands_late(collector),
SyntheticAmode::IncomingArg { .. } => { SyntheticAmode::IncomingArg { .. } => {
@ -686,10 +667,7 @@ impl RegMemImm {
} }
/// Add the regs mentioned by `self` to `collector`. /// Add the regs mentioned by `self` to `collector`.
pub(crate) fn get_operands<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
Self::Reg { reg } => collector.reg_use(reg), Self::Reg { reg } => collector.reg_use(reg),
Self::Mem { addr } => addr.get_operands(collector), Self::Mem { addr } => addr.get_operands(collector),
@ -796,10 +774,7 @@ impl RegMem {
} }
} }
/// Add the regs mentioned by `self` to `collector`. /// Add the regs mentioned by `self` to `collector`.
pub(crate) fn get_operands<F: Fn(VReg) -> VReg>( pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
&mut self,
collector: &mut OperandCollector<'_, F>,
) {
match self { match self {
RegMem::Reg { reg } => collector.reg_use(reg), RegMem::Reg { reg } => collector.reg_use(reg),
RegMem::Mem { addr, .. } => addr.get_operands(collector), 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::{machinst::*, trace};
use crate::{settings, CodegenError, CodegenResult}; use crate::{settings, CodegenError, CodegenResult};
use alloc::boxed::Box; use alloc::boxed::Box;
use regalloc2::{Allocation, PRegSet, VReg}; use regalloc2::{Allocation, PRegSet};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::string::{String, ToString}; 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 // Note: because we need to statically know the indices of each
// reg in the operands list in order to fetch its allocation // reg in the operands list in order to fetch its allocation
// later, we put the variable-operand-count bits (the RegMem, // 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 { impl MachInst for Inst {
type ABIMachineSpec = X64ABIMachineSpec; 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) 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 /// Return the registers referenced by this machine instruction along with
/// the modes of reference (use, def, modify). /// 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. /// If this is a simple move, return the (source, destination) tuple of registers.
fn is_move(&self) -> Option<(Writable<Reg>, Reg)>; 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. /// Finish the operand collection and return the tuple giving the
pub fn no_reuse_def(&self) -> bool { /// range of indices in the flattened operand array, and the
!self.operands[self.operands_start..] /// clobber set.
.iter() pub fn finish(self) -> ((u32, u32), PRegSet) {
.any(|operand| match operand.constraint() { let start = self.operands_start as u32;
OperandConstraint::Reuse(_) => true, let end = self.operands.len() as u32;
_ => false, ((start, end), self.clobbers)
})
}
fn is_allocatable_preg(&self, reg: PReg) -> bool {
self.allocatable.contains(reg)
} }
}
/// Add an operand. pub trait OperandVisitor {
fn add_operand( fn add_operand(
&mut self, &mut self,
reg: &mut Reg, reg: &mut Reg,
constraint: OperandConstraint, constraint: OperandConstraint,
kind: OperandKind, kind: OperandKind,
pos: OperandPos, 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 fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}
/// 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 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. /// Add a use of a fixed, nonallocatable physical register.
pub fn reg_fixed_nonallocatable(&mut self, preg: PReg) { fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
debug_assert!(!self.is_allocatable_preg(preg)); self.debug_assert_is_allocatable_preg(preg, false);
// Magic VReg which does not participate in register allocation. // Magic VReg which does not participate in register allocation.
let mut reg = Reg(VReg::new(VReg::MAX, preg.class())); let mut reg = Reg(VReg::new(VReg::MAX, preg.class()));
let constraint = OperandConstraint::FixedReg(preg); 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` /// Add a register use, at the start of the instruction (`Before`
/// position). /// 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); self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);
} }
/// Add a register use, at the end of the instruction (`After` position). /// 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); self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);
} }
/// Add a register def, at the end of the instruction (`After` /// Add a register def, at the end of the instruction (`After`
/// position). Use only when this def will be written after all /// position). Use only when this def will be written after all
/// uses are read. /// 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); 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 /// beginning of the instruction, alongside all uses. Use this
/// when the def may be written before all uses are read; the /// when the def may be written before all uses are read; the
/// regalloc will ensure that it does not overwrite any uses. /// 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); self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);
} }
/// Add a register "fixed use", which ties a vreg to a particular /// Add a register "fixed use", which ties a vreg to a particular
/// RealReg at the end of the instruction. /// 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); self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);
} }
/// Add a register "fixed use", which ties a vreg to a particular /// Add a register "fixed use", which ties a vreg to a particular
/// RealReg at this point. /// 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); self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);
} }
/// Add a register "fixed def", which ties a vreg to a particular /// Add a register "fixed def", which ties a vreg to a particular
/// RealReg at this point. /// 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); 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) { fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {
debug_assert!(reg.is_virtual()); debug_assert!(reg.is_virtual());
let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg"); let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
debug_assert!( self.debug_assert_is_allocatable_preg(rreg.into(), true);
self.is_allocatable_preg(rreg.into()),
"{rreg:?} is not allocatable"
);
let constraint = OperandConstraint::FixedReg(rreg.into()); let constraint = OperandConstraint::FixedReg(rreg.into());
self.add_operand(reg, constraint, kind, pos); 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 /// Add a register def that reuses an earlier use-operand's
/// allocation. The index of that earlier operand (relative to the /// allocation. The index of that earlier operand (relative to the
/// current instruction's start of operands) must be known. /// 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(); let reg = reg.reg.as_mut();
if let Some(rreg) = reg.to_real_reg() { if let Some(rreg) = reg.to_real_reg() {
// In some cases we see real register arguments to a reg_reuse_def // 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); self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
} }
} }
}
/// Add a register clobber set. This is a set of registers that impl<T: OperandVisitor> OperandVisitorImpl for T {}
/// are written by the instruction, so must be reserved (not used)
/// for the whole instruction, but are not used afterward. impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
pub fn reg_clobbers(&mut self, regs: PRegSet) { 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); self.clobbers.union_from(regs);
} }
} }

Loading…
Cancel
Save