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:
Trevor Elliott
2022-11-15 12:49:17 -08:00
committed by GitHub
parent f6ae67f3f0
commit a007e02bd2
13 changed files with 133 additions and 66 deletions

View File

@@ -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| {