Add fixed_nonallocatable constraints when appropriate (#5253)
Plumb the set of allocatable registers through the OperandCollector and use it validate uses of fixed-nonallocatable registers, like %rsp on x86_64.
This commit is contained in:
@@ -7,24 +7,31 @@ use crate::timing;
|
||||
use crate::trace;
|
||||
|
||||
use regalloc2::RegallocOptions;
|
||||
use regalloc2::{self, MachineEnv};
|
||||
|
||||
/// Compile the given function down to VCode with allocated registers, ready
|
||||
/// for binary emission.
|
||||
pub fn compile<B: LowerBackend + TargetIsa>(
|
||||
f: &Function,
|
||||
flags: crate::settings::Flags,
|
||||
b: &B,
|
||||
abi: Callee<<<B as LowerBackend>::MInst as MachInst>::ABIMachineSpec>,
|
||||
machine_env: &MachineEnv,
|
||||
emit_info: <B::MInst as MachInstEmit>::Info,
|
||||
sigs: SigSet,
|
||||
) -> CodegenResult<(VCode<B::MInst>, regalloc2::Output)> {
|
||||
let machine_env = b.machine_env();
|
||||
|
||||
// Compute lowered block order.
|
||||
let block_order = BlockLoweringOrder::new(f);
|
||||
|
||||
// Build the lowering context.
|
||||
let lower = crate::machinst::Lower::new(f, flags, abi, emit_info, block_order, sigs)?;
|
||||
let lower = crate::machinst::Lower::new(
|
||||
f,
|
||||
b.flags().clone(),
|
||||
machine_env,
|
||||
abi,
|
||||
emit_info,
|
||||
block_order,
|
||||
sigs,
|
||||
)?;
|
||||
|
||||
// Lower the IR.
|
||||
let vcode = {
|
||||
|
||||
@@ -21,10 +21,11 @@ use crate::machinst::{
|
||||
};
|
||||
use crate::{trace, CodegenResult};
|
||||
use alloc::vec::Vec;
|
||||
use regalloc2::{MachineEnv, PRegSet};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{VCodeBuildDirection, VRegAllocator};
|
||||
use super::{preg_set_from_machine_env, VCodeBuildDirection, VRegAllocator};
|
||||
|
||||
/// An "instruction color" partitions CLIF instructions by side-effecting ops.
|
||||
/// All instructions with the same "color" are guaranteed not to be separated by
|
||||
@@ -149,6 +150,9 @@ pub struct Lower<'func, I: VCodeInst> {
|
||||
/// Machine-independent flags.
|
||||
flags: crate::settings::Flags,
|
||||
|
||||
/// The set of allocatable registers.
|
||||
allocatable: PRegSet,
|
||||
|
||||
/// Lowered machine instructions.
|
||||
vcode: VCodeBuilder<I>,
|
||||
|
||||
@@ -322,11 +326,12 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
pub fn new(
|
||||
f: &'func Function,
|
||||
flags: crate::settings::Flags,
|
||||
machine_env: &MachineEnv,
|
||||
abi: Callee<I::ABIMachineSpec>,
|
||||
emit_info: I::Info,
|
||||
block_order: BlockLoweringOrder,
|
||||
sigs: SigSet,
|
||||
) -> CodegenResult<Lower<'func, I>> {
|
||||
) -> CodegenResult<Self> {
|
||||
let constants = VCodeConstants::with_capacity(f.dfg.constants.len());
|
||||
let vcode = VCodeBuilder::new(
|
||||
sigs,
|
||||
@@ -412,6 +417,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
Ok(Lower {
|
||||
f,
|
||||
flags,
|
||||
allocatable: preg_set_from_machine_env(machine_env),
|
||||
vcode,
|
||||
vregs,
|
||||
value_regs,
|
||||
@@ -1019,7 +1025,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
|
||||
// Now that we've emitted all instructions into the
|
||||
// VCodeBuilder, let's build the VCode.
|
||||
let vcode = self.vcode.build(self.vregs);
|
||||
let vcode = self.vcode.build(self.allocatable, self.vregs);
|
||||
trace!("built vcode: {:?}", vcode);
|
||||
|
||||
Ok(vcode)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::{fmt::Debug, hash::Hash};
|
||||
use regalloc2::{Allocation, Operand, PReg, PRegSet, VReg};
|
||||
use regalloc2::{Allocation, MachineEnv, Operand, PReg, PRegSet, VReg};
|
||||
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -38,6 +38,26 @@ pub fn first_user_vreg_index() -> usize {
|
||||
PINNED_VREGS
|
||||
}
|
||||
|
||||
/// Collect the registers from a regalloc2 MachineEnv into a PRegSet.
|
||||
/// TODO: remove this once it's upstreamed in regalloc2
|
||||
pub fn preg_set_from_machine_env(machine_env: &MachineEnv) -> PRegSet {
|
||||
let mut regs = PRegSet::default();
|
||||
|
||||
for class in machine_env.preferred_regs_by_class.iter() {
|
||||
for reg in class.iter() {
|
||||
regs.add(*reg);
|
||||
}
|
||||
}
|
||||
|
||||
for class in machine_env.non_preferred_regs_by_class.iter() {
|
||||
for reg in class.iter() {
|
||||
regs.add(*reg);
|
||||
}
|
||||
}
|
||||
|
||||
regs
|
||||
}
|
||||
|
||||
/// A register named in an instruction. This register can be either a
|
||||
/// virtual register or a fixed physical register. It does not have
|
||||
/// any constraints applied to it: those can be added later in
|
||||
@@ -289,21 +309,30 @@ pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
|
||||
operands: &'a mut Vec<Operand>,
|
||||
operands_start: usize,
|
||||
clobbers: PRegSet,
|
||||
|
||||
/// The subset of physical registers that are allocatable.
|
||||
allocatable: PRegSet,
|
||||
|
||||
renamer: F,
|
||||
}
|
||||
|
||||
impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
|
||||
/// Start gathering operands into one flattened operand array.
|
||||
pub fn new(operands: &'a mut Vec<Operand>, renamer: F) -> Self {
|
||||
pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
|
||||
let operands_start = operands.len();
|
||||
Self {
|
||||
operands,
|
||||
operands_start,
|
||||
clobbers: PRegSet::default(),
|
||||
allocatable,
|
||||
renamer,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allocatable_preg(&self, reg: PReg) -> bool {
|
||||
self.allocatable.contains(reg)
|
||||
}
|
||||
|
||||
/// Add an operand.
|
||||
fn add_operand(&mut self, operand: Operand) {
|
||||
let vreg = (self.renamer)(operand.vreg());
|
||||
@@ -320,6 +349,12 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
|
||||
((start, end), self.clobbers)
|
||||
}
|
||||
|
||||
/// 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));
|
||||
self.add_operand(Operand::fixed_nonallocatable(preg))
|
||||
}
|
||||
|
||||
/// Add a register use, at the start of the instruction (`Before`
|
||||
/// position).
|
||||
pub fn reg_use(&mut self, reg: Reg) {
|
||||
@@ -434,6 +469,19 @@ impl<'a> AllocationConsumer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_fixed_nonallocatable(&mut self, preg: PReg) {
|
||||
let alloc = self.allocs.next();
|
||||
let alloc = alloc.map(|alloc| {
|
||||
Reg::from(
|
||||
alloc
|
||||
.as_reg()
|
||||
.expect("Should not have gotten a stack allocation"),
|
||||
)
|
||||
});
|
||||
|
||||
assert_eq!(preg, alloc.unwrap().to_real_reg().unwrap().into());
|
||||
}
|
||||
|
||||
pub fn next(&mut self, pre_regalloc_reg: Reg) -> Reg {
|
||||
let alloc = self.allocs.next();
|
||||
let alloc = alloc.map(|alloc| {
|
||||
|
||||
@@ -541,7 +541,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
.sort_unstable_by_key(|(vreg, _, _, _)| *vreg);
|
||||
}
|
||||
|
||||
fn collect_operands(&mut self) {
|
||||
fn collect_operands(&mut self, allocatable: PRegSet) {
|
||||
for (i, insn) in self.vcode.insts.iter().enumerate() {
|
||||
// Push operands from the instruction onto the operand list.
|
||||
//
|
||||
@@ -555,9 +555,10 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
// its register fields (which is slow, branchy code) once.
|
||||
|
||||
let vreg_aliases = &self.vcode.vreg_aliases;
|
||||
let mut op_collector = OperandCollector::new(&mut self.vcode.operands, |vreg| {
|
||||
Self::resolve_vreg_alias_impl(vreg_aliases, vreg)
|
||||
});
|
||||
let mut op_collector =
|
||||
OperandCollector::new(&mut self.vcode.operands, allocatable, |vreg| {
|
||||
Self::resolve_vreg_alias_impl(vreg_aliases, vreg)
|
||||
});
|
||||
insn.get_operands(&mut op_collector);
|
||||
let (ops, clobbers) = op_collector.finish();
|
||||
self.vcode.operand_ranges.push(ops);
|
||||
@@ -586,14 +587,14 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
}
|
||||
|
||||
/// Build the final VCode.
|
||||
pub fn build(mut self, vregs: VRegAllocator<I>) -> VCode<I> {
|
||||
pub fn build(mut self, allocatable: PRegSet, vregs: VRegAllocator<I>) -> VCode<I> {
|
||||
self.vcode.vreg_types = vregs.vreg_types;
|
||||
self.vcode.reftyped_vregs = vregs.reftyped_vregs;
|
||||
|
||||
if self.direction == VCodeBuildDirection::Backward {
|
||||
self.reverse_and_finalize();
|
||||
}
|
||||
self.collect_operands();
|
||||
self.collect_operands(allocatable);
|
||||
|
||||
// Apply register aliases to the `reftyped_vregs` list since this list
|
||||
// will be returned directly to `regalloc2` eventually and all
|
||||
|
||||
Reference in New Issue
Block a user