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