Remove register class from SpillSlot (#80)
* Remove register class from `SpillSlot` The register allocator was already allowing moves between spillslots and registers of different classes, so this PR formalizes this by making spillslots independent of register class. This also fixes #79 by properly tracking the register class of an `InsertedMove` with the `to_vreg` field which turns out to never be `None` in practice. Removing the `Option` allows the register class of the `VReg` to be used when building the per-class move lists. Fixes #79 * Address review feedback
This commit is contained in:
@@ -40,14 +40,14 @@ impl Arbitrary<'_> for TestCase {
|
||||
Allocation::reg(PReg::new(reg, RegClass::Int))
|
||||
} else {
|
||||
let slot = u.int_in_range(0..=31)?;
|
||||
Allocation::stack(SpillSlot::new(slot, RegClass::Int))
|
||||
Allocation::stack(SpillSlot::new(slot))
|
||||
};
|
||||
let dst = if bool::arbitrary(u)? {
|
||||
let reg = u.int_in_range(0..=29)?;
|
||||
Allocation::reg(PReg::new(reg, RegClass::Int))
|
||||
} else {
|
||||
let slot = u.int_in_range(0..=31)?;
|
||||
Allocation::stack(SpillSlot::new(slot, RegClass::Int))
|
||||
Allocation::stack(SpillSlot::new(slot))
|
||||
};
|
||||
|
||||
// Stop if we are going to write a reg more than once:
|
||||
@@ -88,7 +88,7 @@ fuzz_target!(|testcase: TestCase| {
|
||||
let get_stackslot = || {
|
||||
let slot = next_slot;
|
||||
next_slot += 1;
|
||||
Allocation::stack(SpillSlot::new(slot, RegClass::Int))
|
||||
Allocation::stack(SpillSlot::new(slot))
|
||||
};
|
||||
let preferred_victim = PReg::new(0, RegClass::Int);
|
||||
let scratch_resolver =
|
||||
|
||||
@@ -440,7 +440,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SpillSlotData {
|
||||
pub ranges: LiveRangeSet,
|
||||
pub class: RegClass,
|
||||
pub slots: u32,
|
||||
pub alloc: Allocation,
|
||||
}
|
||||
|
||||
@@ -580,7 +580,7 @@ pub struct InsertedMove {
|
||||
pub pos_prio: PosWithPrio,
|
||||
pub from_alloc: Allocation,
|
||||
pub to_alloc: Allocation,
|
||||
pub to_vreg: Option<VReg>,
|
||||
pub to_vreg: VReg,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
||||
@@ -573,7 +573,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::MultiFixedRegInitial,
|
||||
Allocation::reg(src_preg),
|
||||
Allocation::reg(dst_preg),
|
||||
Some(dst.vreg()),
|
||||
dst.vreg(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -718,14 +718,14 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::Regular,
|
||||
Allocation::reg(preg),
|
||||
Allocation::reg(preg),
|
||||
Some(dst.vreg()),
|
||||
dst.vreg(),
|
||||
);
|
||||
self.insert_move(
|
||||
ProgPoint::before(inst.next()),
|
||||
InsertMovePrio::MultiFixedRegInitial,
|
||||
Allocation::reg(preg),
|
||||
Allocation::reg(preg),
|
||||
Some(src.vreg()),
|
||||
src.vreg(),
|
||||
);
|
||||
} else {
|
||||
if inst > self.cfginfo.block_entry[block.index()].inst() {
|
||||
@@ -751,7 +751,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::BlockParam,
|
||||
Allocation::reg(preg),
|
||||
Allocation::reg(preg),
|
||||
Some(dst.vreg()),
|
||||
dst.vreg(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -781,7 +781,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::PostRegular,
|
||||
Allocation::reg(preg),
|
||||
Allocation::reg(preg),
|
||||
Some(dst.vreg()),
|
||||
dst.vreg(),
|
||||
);
|
||||
}
|
||||
// Otherwise, if dead, no need to create
|
||||
|
||||
@@ -45,20 +45,21 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
prio: InsertMovePrio,
|
||||
from_alloc: Allocation,
|
||||
to_alloc: Allocation,
|
||||
to_vreg: Option<VReg>,
|
||||
to_vreg: VReg,
|
||||
) {
|
||||
trace!(
|
||||
"insert_move: pos {:?} prio {:?} from_alloc {:?} to_alloc {:?}",
|
||||
"insert_move: pos {:?} prio {:?} from_alloc {:?} to_alloc {:?} to_vreg {:?}",
|
||||
pos,
|
||||
prio,
|
||||
from_alloc,
|
||||
to_alloc
|
||||
to_alloc,
|
||||
to_vreg
|
||||
);
|
||||
match (from_alloc.as_reg(), to_alloc.as_reg()) {
|
||||
(Some(from), Some(to)) => {
|
||||
debug_assert_eq!(from.class(), to.class());
|
||||
}
|
||||
_ => {}
|
||||
if let Some(from) = from_alloc.as_reg() {
|
||||
debug_assert_eq!(from.class(), to_vreg.class());
|
||||
}
|
||||
if let Some(to) = to_alloc.as_reg() {
|
||||
debug_assert_eq!(to.class(), to_vreg.class());
|
||||
}
|
||||
self.inserted_moves.push(InsertedMove {
|
||||
pos_prio: PosWithPrio {
|
||||
@@ -280,7 +281,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::Regular,
|
||||
prev_alloc,
|
||||
alloc,
|
||||
Some(self.vreg(vreg)),
|
||||
self.vreg(vreg),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -720,7 +721,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
prio,
|
||||
src.alloc,
|
||||
dest.alloc,
|
||||
Some(self.vreg(dest.to_vreg())),
|
||||
self.vreg(dest.to_vreg()),
|
||||
);
|
||||
last = Some(dest.alloc);
|
||||
}
|
||||
@@ -741,13 +742,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
FixedRegFixupLevel::Initial => InsertMovePrio::MultiFixedRegInitial,
|
||||
FixedRegFixupLevel::Secondary => InsertMovePrio::MultiFixedRegSecondary,
|
||||
};
|
||||
self.insert_move(
|
||||
fixup.pos,
|
||||
prio,
|
||||
from_alloc,
|
||||
to_alloc,
|
||||
Some(self.vreg(fixup.vreg)),
|
||||
);
|
||||
self.insert_move(fixup.pos, prio, from_alloc, to_alloc, self.vreg(fixup.vreg));
|
||||
self.set_alloc(
|
||||
fixup.pos.inst(),
|
||||
fixup.to_slot as usize,
|
||||
@@ -831,7 +826,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::ReusedInput,
|
||||
input_alloc,
|
||||
output_alloc,
|
||||
Some(input_operand.vreg()),
|
||||
input_operand.vreg(),
|
||||
);
|
||||
self.set_alloc(inst, input_idx, output_alloc);
|
||||
}
|
||||
@@ -871,7 +866,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
InsertMovePrio::Regular,
|
||||
from_alloc,
|
||||
to_alloc,
|
||||
Some(self.vreg(to_vreg)),
|
||||
self.vreg(to_vreg),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -961,13 +956,10 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
let mut float_moves: SmallVec<[InsertedMove; 8]> = smallvec![];
|
||||
|
||||
for m in moves {
|
||||
if m.from_alloc.is_reg() && m.to_alloc.is_reg() {
|
||||
debug_assert_eq!(m.from_alloc.class(), m.to_alloc.class());
|
||||
}
|
||||
if m.from_alloc == m.to_alloc {
|
||||
continue;
|
||||
}
|
||||
match m.from_alloc.class() {
|
||||
match m.to_vreg.class() {
|
||||
RegClass::Int => {
|
||||
int_moves.push(m.clone());
|
||||
}
|
||||
@@ -990,10 +982,8 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
pos_prio.prio
|
||||
);
|
||||
for m in moves {
|
||||
if (m.from_alloc != m.to_alloc) || m.to_vreg.is_some() {
|
||||
trace!(" {} -> {}", m.from_alloc, m.to_alloc,);
|
||||
parallel_moves.add(m.from_alloc, m.to_alloc, m.to_vreg);
|
||||
}
|
||||
trace!(" {} -> {}", m.from_alloc, m.to_alloc);
|
||||
parallel_moves.add(m.from_alloc, m.to_alloc, Some(m.to_vreg));
|
||||
}
|
||||
|
||||
let resolved = parallel_moves.resolve();
|
||||
@@ -1042,7 +1032,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
// these placeholders then allocate the actual
|
||||
// slots if needed with `self.allocate_spillslot`
|
||||
// below.
|
||||
Allocation::stack(SpillSlot::new(SpillSlot::MAX - idx, regclass))
|
||||
Allocation::stack(SpillSlot::new(SpillSlot::MAX - idx))
|
||||
};
|
||||
let is_stack_alloc = |alloc: Allocation| {
|
||||
if let Some(preg) = alloc.as_reg() {
|
||||
@@ -1065,11 +1055,12 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
let mut rewrites = FxHashMap::default();
|
||||
for i in 0..stackslot_idx {
|
||||
if i >= self.extra_spillslots_by_class[regclass as usize].len() {
|
||||
let slot = self.allocate_spillslot(regclass);
|
||||
let slot =
|
||||
self.allocate_spillslot(self.func.spillslot_size(regclass) as u32);
|
||||
self.extra_spillslots_by_class[regclass as usize].push(slot);
|
||||
}
|
||||
rewrites.insert(
|
||||
Allocation::stack(SpillSlot::new(SpillSlot::MAX - i, regclass)),
|
||||
Allocation::stack(SpillSlot::new(SpillSlot::MAX - i)),
|
||||
self.extra_spillslots_by_class[regclass as usize][i],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//! Spillslot allocation.
|
||||
|
||||
use super::{
|
||||
AllocRegResult, Env, LiveRangeKey, LiveRangeSet, PReg, PRegIndex, RegClass, RegTraversalIter,
|
||||
AllocRegResult, Env, LiveRangeKey, LiveRangeSet, PReg, PRegIndex, RegTraversalIter,
|
||||
SpillSetIndex, SpillSlotData, SpillSlotIndex, SpillSlotList,
|
||||
};
|
||||
use crate::{Allocation, Function, SpillSlot};
|
||||
@@ -165,7 +165,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
self.spillslots.push(SpillSlotData {
|
||||
ranges: LiveRangeSet::new(),
|
||||
alloc: Allocation::none(),
|
||||
class: self.spillsets[spillset.index()].class,
|
||||
slots: size as u32,
|
||||
});
|
||||
self.slots_by_size[size].slots.push(spillslot);
|
||||
self.slots_by_size[size].probe_start = self.slots_by_size[size].slots.len() - 1;
|
||||
@@ -176,14 +176,13 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
|
||||
// Assign actual slot indices to spillslots.
|
||||
for i in 0..self.spillslots.len() {
|
||||
self.spillslots[i].alloc = self.allocate_spillslot(self.spillslots[i].class);
|
||||
self.spillslots[i].alloc = self.allocate_spillslot(self.spillslots[i].slots);
|
||||
}
|
||||
|
||||
trace!("spillslot allocator done");
|
||||
}
|
||||
|
||||
pub fn allocate_spillslot(&mut self, class: RegClass) -> Allocation {
|
||||
let size = self.func.spillslot_size(class) as u32;
|
||||
pub fn allocate_spillslot(&mut self, size: u32) -> Allocation {
|
||||
let mut offset = self.num_spillslots;
|
||||
// Align up to `size`.
|
||||
debug_assert!(size.is_power_of_two());
|
||||
@@ -195,6 +194,6 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
};
|
||||
offset += size;
|
||||
self.num_spillslots = offset;
|
||||
Allocation::stack(SpillSlot::new(slot as usize, class))
|
||||
Allocation::stack(SpillSlot::new(slot as usize))
|
||||
}
|
||||
}
|
||||
|
||||
32
src/lib.rs
32
src/lib.rs
@@ -324,13 +324,11 @@ impl SpillSlot {
|
||||
/// The maximum spillslot index.
|
||||
pub const MAX: usize = (1 << 24) - 1;
|
||||
|
||||
/// Create a new SpillSlot of a given class.
|
||||
/// Create a new SpillSlot.
|
||||
#[inline(always)]
|
||||
pub fn new(slot: usize, class: RegClass) -> Self {
|
||||
pub fn new(slot: usize) -> Self {
|
||||
debug_assert!(slot <= Self::MAX);
|
||||
SpillSlot {
|
||||
bits: (slot as u32) | (class as u8 as u32) << 24,
|
||||
}
|
||||
SpillSlot { bits: slot as u32 }
|
||||
}
|
||||
|
||||
/// Get the spillslot index for this spillslot.
|
||||
@@ -339,20 +337,10 @@ impl SpillSlot {
|
||||
(self.bits & 0x00ffffff) as usize
|
||||
}
|
||||
|
||||
/// Get the class for this spillslot.
|
||||
#[inline(always)]
|
||||
pub fn class(self) -> RegClass {
|
||||
match (self.bits >> 24) as u8 {
|
||||
0 => RegClass::Int,
|
||||
1 => RegClass::Float,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the spillslot `offset` slots away.
|
||||
#[inline(always)]
|
||||
pub fn plus(self, offset: usize) -> Self {
|
||||
SpillSlot::new(self.index() + offset, self.class())
|
||||
SpillSlot::new(self.index() + offset)
|
||||
}
|
||||
|
||||
/// Get the invalid spillslot, used for initializing data structures.
|
||||
@@ -965,18 +953,6 @@ pub enum AllocationKind {
|
||||
Stack = 2,
|
||||
}
|
||||
|
||||
impl Allocation {
|
||||
/// Get the register class of an allocation's value.
|
||||
#[inline(always)]
|
||||
pub fn class(self) -> RegClass {
|
||||
match self.kind() {
|
||||
AllocationKind::None => panic!("Allocation::None has no class"),
|
||||
AllocationKind::Reg => self.as_reg().unwrap().class(),
|
||||
AllocationKind::Stack => self.as_stack().unwrap().class(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait defined by the regalloc client to provide access to its
|
||||
/// machine-instruction / CFG representation.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user