WIP
This commit is contained in:
@@ -12,7 +12,7 @@ repository = "https://github.com/bytecodealliance/regalloc2"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = { version = "0.4.8", default-features = false }
|
log = { version = "0.4.8", default-features = false }
|
||||||
smallvec = { version = "1.6.1", features = ["union"] }
|
smallvec = { version = "1.6.1", features = ["union", "const_generics"] }
|
||||||
rustc-hash = { version = "1.1.0", default-features = false }
|
rustc-hash = { version = "1.1.0", default-features = false }
|
||||||
slice-group-by = { version = "0.3.0", default-features = false }
|
slice-group-by = { version = "0.3.0", default-features = false }
|
||||||
hashbrown = "0.13.2"
|
hashbrown = "0.13.2"
|
||||||
|
|||||||
@@ -626,6 +626,19 @@ impl<'a, F: Function> FastAlloc<'a, F> {
|
|||||||
self.livein_locs
|
self.livein_locs
|
||||||
.push(LiveinLoc::init(data.preg(), data.stack_slot()));
|
.push(LiveinLoc::init(data.preg(), data.stack_slot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// also save block params
|
||||||
|
for &vreg in self.func.block_params(block) {
|
||||||
|
let data = &self.vregs[vreg.vreg()];
|
||||||
|
trace!(
|
||||||
|
" -> {} at {:?} and slot {:?} (param)",
|
||||||
|
vreg,
|
||||||
|
data.preg(),
|
||||||
|
data.stack_slot()
|
||||||
|
);
|
||||||
|
self.livein_locs
|
||||||
|
.push(LiveinLoc::init(data.preg(), data.stack_slot()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load livein locations if our predecessor has multiple successors
|
// load livein locations if our predecessor has multiple successors
|
||||||
@@ -670,6 +683,51 @@ impl<'a, F: Function> FastAlloc<'a, F> {
|
|||||||
loc_idx += 1;
|
loc_idx += 1;
|
||||||
}
|
}
|
||||||
self.liveins = liveins;
|
self.liveins = liveins;
|
||||||
|
|
||||||
|
// also restore block params
|
||||||
|
for &vreg in self.func.block_params(block) {
|
||||||
|
let data = &mut self.vregs[vreg.vreg()];
|
||||||
|
let loc = self.livein_locs[loc_idx];
|
||||||
|
trace!(
|
||||||
|
" -> {} at {:?} and slot {:?} (param)",
|
||||||
|
vreg,
|
||||||
|
loc.preg(),
|
||||||
|
loc.slot()
|
||||||
|
);
|
||||||
|
match (data.preg(), loc.preg()) {
|
||||||
|
(Some(cur_preg), Some(loc_preg)) => {
|
||||||
|
if cur_preg != loc_preg {
|
||||||
|
self.clear_preg(cur_preg.index());
|
||||||
|
self.clear_preg(loc_preg.index());
|
||||||
|
self.assign_preg(
|
||||||
|
loc_preg,
|
||||||
|
VReg::new(vreg.vreg(), loc_preg.class()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(cur_preg), None) => {
|
||||||
|
self.clear_preg(cur_preg.index());
|
||||||
|
}
|
||||||
|
(None, Some(loc_preg)) => {
|
||||||
|
self.clear_preg(loc_preg.index());
|
||||||
|
self.assign_preg(loc_preg, VReg::new(vreg.vreg(), loc_preg.class()));
|
||||||
|
}
|
||||||
|
(None, None) => {}
|
||||||
|
}
|
||||||
|
let data = &mut self.vregs[vreg.vreg()];
|
||||||
|
match loc.slot() {
|
||||||
|
Some(slot) => {
|
||||||
|
debug_assert!(data.allocated_stack_slot().is_some());
|
||||||
|
debug_assert_eq!(data.allocated_stack_slot().unwrap(), slot);
|
||||||
|
data.set_stack_slot_valid();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
data.clear_stack_slot(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loc_idx += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.alloc_block_insts(block)?;
|
self.alloc_block_insts(block)?;
|
||||||
@@ -1208,6 +1266,7 @@ impl<'a, F: Function> FastAlloc<'a, F> {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check if the reftype count is correctly managed
|
||||||
fn alloc_block_edge_single_unallocated(
|
fn alloc_block_edge_single_unallocated(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: Block,
|
block: Block,
|
||||||
@@ -1291,6 +1350,7 @@ impl<'a, F: Function> FastAlloc<'a, F> {
|
|||||||
to: Allocation::stack(SpillSlot::new(slot as usize)),
|
to: Allocation::stack(SpillSlot::new(slot as usize)),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
self.vregs[succ_param.vreg()].set_stack_slot_valid();
|
||||||
|
|
||||||
self.vregs[out_vreg.vreg()].cur_use_idx -= 1;
|
self.vregs[out_vreg.vreg()].cur_use_idx -= 1;
|
||||||
continue;
|
continue;
|
||||||
@@ -1349,12 +1409,233 @@ impl<'a, F: Function> FastAlloc<'a, F> {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check if the reftype count is correctly managed
|
||||||
fn alloc_block_edge_multiple(
|
fn alloc_block_edge_multiple(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: Block,
|
block: Block,
|
||||||
succs: &[Block],
|
succs: &[Block],
|
||||||
) -> Result<(), RegAllocError> {
|
) -> Result<(), RegAllocError> {
|
||||||
todo!()
|
trace!(" -> Block has multiple unallocated edges. Copying allocations");
|
||||||
|
let mut slots_made_valid = SmallVec::<[VReg; 4]>::new();
|
||||||
|
let mut slots_made_invalid: SmallVec<[VReg; 4]> = SmallVec::<[VReg; 4]>::new();
|
||||||
|
let mut preg_allocs: SmallVec<[(u16, VReg); PReg::NUM_INDEX + 1]> = SmallVec::new();
|
||||||
|
|
||||||
|
for idx in 0..self.pregs.len() {
|
||||||
|
debug_assert!(idx < PReg::NUM_INDEX);
|
||||||
|
let data = &self.pregs[idx];
|
||||||
|
match data.vreg() {
|
||||||
|
Some(vreg) => preg_allocs.push((idx as u16, vreg)),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preg_allocs.push((u16::MAX, VReg::invalid()));
|
||||||
|
|
||||||
|
let last_inst = self.func.block_insns(block).last();
|
||||||
|
debug_assert!(self.func.is_branch(last_inst));
|
||||||
|
let out_vregs = self.func.branch_blockparams(block, last_inst, 0);
|
||||||
|
|
||||||
|
// we basically run the block_edge_single_unallocated algo but inside the target block and rewind afterwards
|
||||||
|
for &succ in succs {
|
||||||
|
assert_ne!(block, succ);
|
||||||
|
trace!(" -> Allocating edge for block {:?}", succ);
|
||||||
|
|
||||||
|
let succ_params = self.func.block_params(succ);
|
||||||
|
// prevent these from being allocated as tmp regs
|
||||||
|
let mut pregs_allocated = PRegSet::empty();
|
||||||
|
let mut tmp_reg = None;
|
||||||
|
for i in 0..succ_params.len() {
|
||||||
|
let succ_param = succ_params[i];
|
||||||
|
let out_vreg = out_vregs[i];
|
||||||
|
|
||||||
|
if self.vreg_killed(out_vreg.vreg(), block, self.cur_inst_pos + 1, false) {
|
||||||
|
trace!(" -> {} mapping to {} is killed", out_vreg, succ_param);
|
||||||
|
if let Some(slot) = self.vregs[out_vreg.vreg()].stack_slot() {
|
||||||
|
trace!(" -> Transferring slot {} to {}", slot, succ_param);
|
||||||
|
self.vregs[succ_param.vreg()].set_stack_slot_allocated(slot);
|
||||||
|
self.vregs[succ_param.vreg()].set_stack_slot_valid();
|
||||||
|
slots_made_valid.push(succ_param);
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.vregs[out_vreg.vreg()].clear_stack_slot(false);
|
||||||
|
slots_made_invalid.push(out_vreg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(preg) = self.vregs[out_vreg.vreg()].preg() {
|
||||||
|
trace!(" -> Transferring {} to {}", preg, succ_param);
|
||||||
|
self.vregs[succ_param.vreg()].set_preg(preg);
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.vregs[out_vreg.vreg()].clear_preg();
|
||||||
|
}
|
||||||
|
self.pregs[preg.index()].set_vreg(succ_param);
|
||||||
|
pregs_allocated.add(preg);
|
||||||
|
}
|
||||||
|
self.vregs[out_vreg.vreg()].cur_use_idx -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!(" -> {} mapping to {} is not killed", out_vreg, succ_param);
|
||||||
|
// the out_vreg might only be alive until the end of the block but used by another param or still be live after the block
|
||||||
|
if let Some(preg) = self.vregs[out_vreg.vreg()].preg() {
|
||||||
|
// TODO: we could do some fancy calculation here which of the block params should get to stay in the preg but rn we dont
|
||||||
|
if self.next_use(out_vreg.vreg()).unwrap()
|
||||||
|
> self.next_use(succ_param.vreg()).unwrap()
|
||||||
|
|| self.vregs[out_vreg.vreg()].stack_slot().is_some()
|
||||||
|
{
|
||||||
|
trace!(" -> Transferring preg from {} to {}", out_vreg, succ_param);
|
||||||
|
|
||||||
|
if self.vregs[out_vreg.vreg()].stack_slot().is_none() {
|
||||||
|
// save to stack if necessary
|
||||||
|
self.alloc_and_move_to_stack(out_vreg, ProgPoint::before(last_inst));
|
||||||
|
slots_made_valid.push(out_vreg);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vregs[out_vreg.vreg()].clear_preg();
|
||||||
|
self.vregs[succ_param.vreg()].set_preg(preg);
|
||||||
|
self.pregs[preg.index()].set_vreg(succ_param);
|
||||||
|
pregs_allocated.add(preg);
|
||||||
|
|
||||||
|
// don't need to care about stack slot
|
||||||
|
self.vregs[out_vreg.vreg()].cur_use_idx -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let slot = self.alloc_stack_slot(succ_param);
|
||||||
|
trace!(" -> Transferring preg from {} to slot {}", preg, slot);
|
||||||
|
self.edits.push((
|
||||||
|
ProgPoint::before(last_inst),
|
||||||
|
Edit::Move {
|
||||||
|
from: Allocation::reg(preg),
|
||||||
|
to: Allocation::stack(SpillSlot::new(slot as usize)),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
self.vregs[succ_param.vreg()].set_stack_slot_valid();
|
||||||
|
slots_made_valid.push(succ_param);
|
||||||
|
|
||||||
|
self.vregs[out_vreg.vreg()].cur_use_idx -= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dst_slot = self.alloc_stack_slot(succ_param) as usize;
|
||||||
|
let src_slot = self.vregs[out_vreg.vreg()].stack_slot().unwrap() as usize;
|
||||||
|
trace!(
|
||||||
|
" -> Copying {} from slot {} to slot {} for {}",
|
||||||
|
out_vreg,
|
||||||
|
src_slot,
|
||||||
|
dst_slot,
|
||||||
|
succ_param
|
||||||
|
);
|
||||||
|
// out_vreg is on the stack so create a stack slot for succ_param
|
||||||
|
let preg = match tmp_reg {
|
||||||
|
Some(preg) => preg,
|
||||||
|
None => {
|
||||||
|
let preg = self.find_free_reg(
|
||||||
|
succ_param.class(),
|
||||||
|
pregs_allocated,
|
||||||
|
Some(ProgPoint::before(last_inst)),
|
||||||
|
block,
|
||||||
|
self.cur_inst_pos + 1,
|
||||||
|
)?;
|
||||||
|
tmp_reg = Some(preg);
|
||||||
|
preg
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.edits.push((
|
||||||
|
ProgPoint::before(last_inst),
|
||||||
|
Edit::Move {
|
||||||
|
from: Allocation::stack(SpillSlot::new(src_slot)),
|
||||||
|
to: Allocation::reg(preg),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
self.edits.push((
|
||||||
|
ProgPoint::before(last_inst),
|
||||||
|
Edit::Move {
|
||||||
|
to: Allocation::stack(SpillSlot::new(dst_slot)),
|
||||||
|
from: Allocation::reg(preg),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
slots_made_valid.push(succ_param);
|
||||||
|
self.vregs[succ_param.vreg()].set_stack_slot_valid();
|
||||||
|
|
||||||
|
self.vregs[out_vreg.vreg()].cur_use_idx -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save liveins
|
||||||
|
trace!(" -> Saving livein locations");
|
||||||
|
debug_assert_eq!(self.livein_loc_lookup[succ.index()], 0);
|
||||||
|
self.livein_loc_lookup[succ.index()] = self.livein_locs.len() as u32;
|
||||||
|
for vreg in self.liveins[succ.index()].iter() {
|
||||||
|
let data = &self.vregs[vreg];
|
||||||
|
trace!(
|
||||||
|
" -> {} at {:?} and slot {:?}",
|
||||||
|
vreg,
|
||||||
|
data.preg(),
|
||||||
|
data.stack_slot()
|
||||||
|
);
|
||||||
|
self.livein_locs
|
||||||
|
.push(LiveinLoc::init(data.preg(), data.stack_slot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// also save block params
|
||||||
|
for &vreg in self.func.block_params(succ) {
|
||||||
|
let data = &self.vregs[vreg.vreg()];
|
||||||
|
trace!(
|
||||||
|
" -> {} at {:?} and slot {:?} (param)",
|
||||||
|
vreg,
|
||||||
|
data.preg(),
|
||||||
|
data.stack_slot()
|
||||||
|
);
|
||||||
|
self.livein_locs
|
||||||
|
.push(LiveinLoc::init(data.preg(), data.stack_slot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset pregs & slots
|
||||||
|
trace!(" -> Resetting pregs and slots");
|
||||||
|
let mut vec_idx = 0;
|
||||||
|
for idx in 0..self.pregs.len() {
|
||||||
|
debug_assert!(idx < PReg::NUM_INDEX);
|
||||||
|
let data = &self.pregs[idx];
|
||||||
|
let next_preg = &preg_allocs[vec_idx];
|
||||||
|
if next_preg.0 as usize == idx {
|
||||||
|
if data.vreg().is_none() || data.vreg().unwrap() != next_preg.1 {
|
||||||
|
trace!(" -> Restoring {} to {}", idx, next_preg.1);
|
||||||
|
self.clear_preg(idx);
|
||||||
|
if let Some(preg) = self.vregs[next_preg.1.vreg()].preg() {
|
||||||
|
self.clear_preg(preg.index());
|
||||||
|
}
|
||||||
|
let preg = PReg::new(idx, next_preg.1.class());
|
||||||
|
self.assign_preg(preg, next_preg.1);
|
||||||
|
}
|
||||||
|
vec_idx += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clear_preg(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for &vreg in &slots_made_valid {
|
||||||
|
trace!(
|
||||||
|
" -> Setting slot {} for {} valid",
|
||||||
|
self.vregs[vreg.vreg()].allocated_stack_slot().unwrap(),
|
||||||
|
vreg
|
||||||
|
);
|
||||||
|
self.vregs[vreg.vreg()].clear_stack_slot(true);
|
||||||
|
}
|
||||||
|
slots_made_valid.clear();
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
for &vreg in &slots_made_invalid {
|
||||||
|
trace!(
|
||||||
|
" -> Setting slot {} for {} invalid",
|
||||||
|
self.vregs[vreg.vreg()].allocated_stack_slot().unwrap(),
|
||||||
|
vreg
|
||||||
|
);
|
||||||
|
self.vregs[vreg.vreg()].set_stack_slot_valid();
|
||||||
|
}
|
||||||
|
slots_made_invalid.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> Result<(), RegAllocError> {
|
fn run(&mut self) -> Result<(), RegAllocError> {
|
||||||
|
|||||||
@@ -1491,7 +1491,13 @@ pub fn run<F: Function>(
|
|||||||
env: &MachineEnv,
|
env: &MachineEnv,
|
||||||
options: &RegallocOptions,
|
options: &RegallocOptions,
|
||||||
) -> Result<Output, RegAllocError> {
|
) -> Result<Output, RegAllocError> {
|
||||||
ion::run(func, env, options.verbose_log, options.validate_ssa, options.fast_alloc)
|
ion::run(
|
||||||
|
func,
|
||||||
|
env,
|
||||||
|
options.verbose_log,
|
||||||
|
options.validate_ssa,
|
||||||
|
options.fast_alloc,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for allocation.
|
/// Options for allocation.
|
||||||
|
|||||||
Reference in New Issue
Block a user