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:
Amanieu d'Antras
2022-09-21 05:05:23 +08:00
committed by GitHub
parent 3db1b7199b
commit 906a053208
6 changed files with 40 additions and 74 deletions

View File

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

View File

@@ -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)]

View File

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

View File

@@ -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],
); );
} }

View File

@@ -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))
} }
} }

View File

@@ -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.
/// ///