Skip to content

Commit 4c2a954

Browse files
authored
Fix fastalloc edge case with nonallocated scratch register (#229)
Resolves the existing fuzzbugs I've found running the fuzzer. I've also de-duplicated some code related to scratch register allocation, which is a nice simplification. I don't have access to the fuzzbug in #217, so I can't check if this PR resolves that issue as well. --------- Co-authored-by: squaaawk <[email protected]>
1 parent 36f34e3 commit 4c2a954

File tree

1 file changed

+56
-126
lines changed

1 file changed

+56
-126
lines changed

src/fastalloc/mod.rs

Lines changed: 56 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ use crate::{
55
AllocationKind, Block, FxHashMap, Inst, InstPosition, Operand, OperandConstraint, OperandKind,
66
OperandPos, PReg, PRegSet, RegClass, SpillSlot, VReg,
77
};
8-
use alloc::vec::Vec;
8+
use alloc::format;
9+
use alloc::{vec, vec::Vec};
910
use core::convert::TryInto;
11+
use core::fmt;
1012
use core::iter::FromIterator;
1113
use core::ops::{Index, IndexMut};
1214

@@ -194,8 +196,6 @@ impl<T> IndexMut<OperandPos> for PartedByOperandPos<T> {
194196
}
195197
}
196198

197-
use core::fmt;
198-
199199
impl<T: fmt::Display> fmt::Display for PartedByOperandPos<T> {
200200
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201201
write!(f, "{{ early: {}, late: {} }}", self.items[0], self.items[1])
@@ -243,7 +243,6 @@ pub struct Env<'a, F: Function> {
243243

244244
impl<'a, F: Function> Env<'a, F> {
245245
fn new(func: &'a F, env: &'a MachineEnv) -> Self {
246-
use alloc::vec;
247246
let mut regs = [
248247
env.preferred_regs_by_class[RegClass::Int as usize].clone(),
249248
env.preferred_regs_by_class[RegClass::Float as usize].clone(),
@@ -327,47 +326,43 @@ impl<'a, F: Function> Env<'a, F> {
327326
self.edits.scratch_regs = self.edits.dedicated_scratch_regs.clone();
328327
}
329328

330-
fn alloc_scratch_reg(&mut self, inst: Inst, class: RegClass) -> Result<(), RegAllocError> {
331-
use OperandPos::{Early, Late};
332-
let reg = self.get_scratch_reg(
333-
inst,
334-
class,
335-
self.available_pregs[Late] & self.available_pregs[Early],
336-
)?;
337-
self.edits.scratch_regs[class] = Some(reg);
338-
self.available_pregs[OperandPos::Early].remove(reg);
339-
self.available_pregs[OperandPos::Late].remove(reg);
340-
Ok(())
341-
}
342-
343-
fn get_scratch_reg_for_reload(
329+
fn alloc_scratch_reg(
344330
&mut self,
345331
inst: Inst,
346332
class: RegClass,
347-
avail_regs: PRegSet,
348-
) -> Result<PReg, RegAllocError> {
349-
let Some(preg) = self.lrus[class].last(avail_regs) else {
350-
return Err(RegAllocError::TooManyLiveRegs);
351-
};
352-
if self.vreg_in_preg[preg.index()] != VReg::invalid() {
353-
self.evict_vreg_in_preg_before_inst(inst, preg);
333+
pos: InstPosition,
334+
) -> Result<(), RegAllocError> {
335+
let avail_regs =
336+
self.available_pregs[OperandPos::Late] & self.available_pregs[OperandPos::Early];
337+
if let Some(preg) = self.lrus[class].last(avail_regs) {
338+
if self.vreg_in_preg[preg.index()] != VReg::invalid() {
339+
self.evict_vreg_in_preg(inst, preg, pos)?;
340+
}
341+
self.edits.scratch_regs[class] = Some(preg);
342+
self.available_pregs[OperandPos::Early].remove(preg);
343+
self.available_pregs[OperandPos::Late].remove(preg);
344+
Ok(())
345+
} else {
346+
Err(RegAllocError::TooManyLiveRegs)
354347
}
355-
Ok(preg)
356348
}
357349

358-
fn get_scratch_reg(
350+
fn add_move(
359351
&mut self,
360352
inst: Inst,
353+
from: Allocation,
354+
to: Allocation,
361355
class: RegClass,
362-
avail_regs: PRegSet,
363-
) -> Result<PReg, RegAllocError> {
364-
let Some(preg) = self.lrus[class].last(avail_regs) else {
365-
return Err(RegAllocError::TooManyLiveRegs);
366-
};
367-
if self.vreg_in_preg[preg.index()] != VReg::invalid() {
368-
self.evict_vreg_in_preg(inst, preg);
356+
pos: InstPosition,
357+
) -> Result<(), RegAllocError> {
358+
if self.edits.is_stack(from)
359+
&& self.edits.is_stack(to)
360+
&& self.edits.scratch_regs[class].is_none()
361+
{
362+
self.alloc_scratch_reg(inst, class, pos)?;
369363
}
370-
Ok(preg)
364+
self.edits.add_move(inst, from, to, class, pos);
365+
Ok(())
371366
}
372367

373368
fn reserve_reg_for_fixed_operand(
@@ -461,7 +456,12 @@ impl<'a, F: Function> Env<'a, F> {
461456
}
462457
}
463458

464-
fn base_evict_vreg_in_preg(&mut self, inst: Inst, preg: PReg, pos: InstPosition) {
459+
fn evict_vreg_in_preg(
460+
&mut self,
461+
inst: Inst,
462+
preg: PReg,
463+
pos: InstPosition,
464+
) -> Result<(), RegAllocError> {
465465
trace!("Removing the vreg in preg {} for eviction", preg);
466466
let evicted_vreg = self.vreg_in_preg[preg.index()];
467467
trace!("The removed vreg: {}", evicted_vreg);
@@ -472,21 +472,13 @@ impl<'a, F: Function> Env<'a, F> {
472472
let slot = self.vreg_spillslots[evicted_vreg.vreg()];
473473
self.vreg_allocs[evicted_vreg.vreg()] = Allocation::stack(slot);
474474
trace!("Move reason: eviction");
475-
self.edits.add_move(
475+
self.add_move(
476476
inst,
477477
self.vreg_allocs[evicted_vreg.vreg()],
478478
Allocation::reg(preg),
479479
evicted_vreg.class(),
480480
pos,
481-
);
482-
}
483-
484-
fn evict_vreg_in_preg_before_inst(&mut self, inst: Inst, preg: PReg) {
485-
self.base_evict_vreg_in_preg(inst, preg, InstPosition::Before)
486-
}
487-
488-
fn evict_vreg_in_preg(&mut self, inst: Inst, preg: PReg) {
489-
self.base_evict_vreg_in_preg(inst, preg, InstPosition::After)
481+
)
490482
}
491483

492484
fn freealloc(&mut self, vreg: VReg) {
@@ -542,7 +534,7 @@ impl<'a, F: Function> Env<'a, F> {
542534
return Err(RegAllocError::TooManyLiveRegs);
543535
};
544536
if self.vreg_in_preg[preg.index()] != VReg::invalid() {
545-
self.evict_vreg_in_preg(inst, preg);
537+
self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
546538
}
547539
trace!("The allocated register for vreg {}: {}", op.vreg(), preg);
548540
self.lrus[op.class()].poke(preg);
@@ -639,23 +631,9 @@ impl<'a, F: Function> Env<'a, F> {
639631
// used (in `prev_alloc`, that is).
640632
else {
641633
trace!("Move reason: Prev allocation doesn't meet constraints");
642-
if self.edits.is_stack(new_alloc)
643-
&& self.edits.is_stack(curr_alloc)
644-
&& self.edits.scratch_regs[op.class()].is_none()
645-
{
646-
self.alloc_scratch_reg(inst, op.class())?;
647-
}
648634
if op.kind() == OperandKind::Def {
649635
trace!("Adding edit from {new_alloc:?} to {curr_alloc:?} after inst {inst:?} for {op}");
650-
self.edits.add_move(
651-
inst,
652-
new_alloc,
653-
curr_alloc,
654-
op.class(),
655-
InstPosition::After,
656-
);
657-
// No need to set vreg_in_preg because it will be set during
658-
// `freealloc` if needed.
636+
self.add_move(inst, new_alloc, curr_alloc, op.class(), InstPosition::After)?;
659637
}
660638
// Edits for use operands are added later to avoid inserting
661639
// edits out of order.
@@ -729,7 +707,6 @@ impl<'a, F: Function> Env<'a, F> {
729707
/// this function places branch arguments in the spillslots
730708
/// expected by the destination blocks.
731709
fn process_branch(&mut self, block: Block, inst: Inst) -> Result<(), RegAllocError> {
732-
use OperandPos::*;
733710
trace!("Processing branch instruction {inst:?} in block {block:?}");
734711

735712
let mut int_parallel_moves = ParallelMoves::new();
@@ -758,25 +735,13 @@ impl<'a, F: Function> Env<'a, F> {
758735
self.live_vregs.insert(*vreg);
759736
self.vreg_to_live_inst_range[vreg.vreg()].1 = ProgPoint::before(inst);
760737
} else if curr_alloc != vreg_spill {
761-
if self.edits.is_stack(curr_alloc)
762-
&& self.edits.scratch_regs[vreg.class()].is_none()
763-
{
764-
let reg = self.get_scratch_reg_for_reload(
765-
inst,
766-
vreg.class(),
767-
self.available_pregs[Early] & self.available_pregs[Late],
768-
)?;
769-
self.edits.scratch_regs[vreg.class()] = Some(reg);
770-
self.available_pregs[OperandPos::Early].remove(reg);
771-
self.available_pregs[OperandPos::Late].remove(reg);
772-
}
773-
self.edits.add_move(
738+
self.add_move(
774739
inst,
775740
vreg_spill,
776741
curr_alloc,
777742
vreg.class(),
778743
InstPosition::Before,
779-
);
744+
)?;
780745
}
781746
self.vreg_allocs[vreg.vreg()] = vreg_spill;
782747
let parallel_moves = match vreg.class() {
@@ -796,7 +761,8 @@ impl<'a, F: Function> Env<'a, F> {
796761
let resolved_vec = vec_parallel_moves.resolve();
797762
let mut scratch_regs = self.edits.scratch_regs.clone();
798763
let mut num_spillslots = self.stack.num_spillslots;
799-
let mut avail_regs = self.available_pregs[Early] & self.available_pregs[Late];
764+
let mut avail_regs =
765+
self.available_pregs[OperandPos::Early] & self.available_pregs[OperandPos::Late];
800766

801767
trace!("Resolving parallel moves");
802768
for (resolved, class) in [
@@ -886,12 +852,7 @@ impl<'a, F: Function> Env<'a, F> {
886852
"Evicting {} from fixed register {preg}",
887853
self.vreg_in_preg[preg.index()]
888854
);
889-
if self.fixed_stack_slots.contains(preg)
890-
&& self.edits.scratch_regs[preg.class()].is_none()
891-
{
892-
self.alloc_scratch_reg(inst, preg.class())?;
893-
}
894-
self.evict_vreg_in_preg(inst, preg);
855+
self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
895856
self.vreg_in_preg[preg.index()] = VReg::invalid();
896857
}
897858
}
@@ -902,12 +863,7 @@ impl<'a, F: Function> Env<'a, F> {
902863
"Evicting {} from clobber {preg}",
903864
self.vreg_in_preg[preg.index()]
904865
);
905-
if self.fixed_stack_slots.contains(preg)
906-
&& self.edits.scratch_regs[preg.class()].is_none()
907-
{
908-
self.alloc_scratch_reg(inst, preg.class())?;
909-
}
910-
self.evict_vreg_in_preg(inst, preg);
866+
self.evict_vreg_in_preg(inst, preg, InstPosition::After)?;
911867
self.vreg_in_preg[preg.index()] = VReg::invalid();
912868
}
913869
}
@@ -926,24 +882,9 @@ impl<'a, F: Function> Env<'a, F> {
926882
if slot.is_valid() {
927883
self.vreg_to_live_inst_range[op.vreg().vreg()].2 = Allocation::stack(slot);
928884
let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
929-
let vreg_slot = self.vreg_spillslots[op.vreg().vreg()];
930-
let (is_stack_to_stack, src_and_dest_are_same) =
931-
if let Some(curr_alloc) = curr_alloc.as_stack() {
932-
(true, curr_alloc == vreg_slot)
933-
} else {
934-
(self.edits.is_stack(curr_alloc), false)
935-
};
936-
if !src_and_dest_are_same {
937-
if is_stack_to_stack && self.edits.scratch_regs[op.class()].is_none() {
938-
self.alloc_scratch_reg(inst, op.class())?;
939-
};
940-
self.edits.add_move(
941-
inst,
942-
self.vreg_allocs[op.vreg().vreg()],
943-
Allocation::stack(self.vreg_spillslots[op.vreg().vreg()]),
944-
op.class(),
945-
InstPosition::After,
946-
);
885+
let new_alloc = Allocation::stack(self.vreg_spillslots[op.vreg().vreg()]);
886+
if curr_alloc != new_alloc {
887+
self.add_move(inst, curr_alloc, new_alloc, op.class(), InstPosition::After)?;
947888
}
948889
}
949890
self.vreg_to_live_inst_range[op.vreg().vreg()].0 = ProgPoint::after(inst);
@@ -970,17 +911,17 @@ impl<'a, F: Function> Env<'a, F> {
970911
if op.as_fixed_nonallocatable().is_some() {
971912
continue;
972913
}
973-
if self.vreg_allocs[op.vreg().vreg()] != self.allocs[(inst.index(), op_idx)] {
974-
let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
975-
let new_alloc = self.allocs[(inst.index(), op_idx)];
914+
let curr_alloc = self.vreg_allocs[op.vreg().vreg()];
915+
let new_alloc = self.allocs[(inst.index(), op_idx)];
916+
if curr_alloc != new_alloc {
976917
trace!("Adding edit from {curr_alloc:?} to {new_alloc:?} before inst {inst:?} for {op}");
977-
self.edits.add_move(
918+
self.add_move(
978919
inst,
979920
curr_alloc,
980921
new_alloc,
981922
op.class(),
982923
InstPosition::Before,
983-
);
924+
)?;
984925
}
985926
}
986927
if self.func.is_branch(inst) {
@@ -1059,21 +1000,13 @@ impl<'a, F: Function> Env<'a, F> {
10591000
"Move reason: reload {} at begin - move from its spillslot",
10601001
vreg
10611002
);
1062-
if self.edits.is_stack(prev_alloc) && self.edits.scratch_regs[vreg.class()].is_none() {
1063-
let reg = self.get_scratch_reg_for_reload(
1064-
first_inst,
1065-
vreg.class(),
1066-
avail_regs_for_scratch,
1067-
)?;
1068-
self.edits.scratch_regs[vreg.class()] = Some(reg);
1069-
}
1070-
self.edits.add_move(
1003+
self.add_move(
10711004
self.func.block_insns(block).first(),
10721005
slot,
10731006
prev_alloc,
10741007
vreg.class(),
10751008
InstPosition::Before,
1076-
);
1009+
)?;
10771010
}
10781011
for vreg in self.live_vregs.iter() {
10791012
trace!("Processing {}", vreg);
@@ -1149,7 +1082,6 @@ impl<'a, F: Function> Env<'a, F> {
11491082
}
11501083

11511084
fn log_post_reload_at_begin_state(&self, block: Block) {
1152-
use alloc::format;
11531085
trace!("");
11541086
trace!("State after instruction reload_at_begin of {:?}", block);
11551087
let mut map = FxHashMap::default();
@@ -1172,7 +1104,6 @@ impl<'a, F: Function> Env<'a, F> {
11721104
}
11731105

11741106
fn log_post_inst_processing_state(&self, inst: Inst) {
1175-
use alloc::format;
11761107
trace!("");
11771108
trace!("State after instruction {:?}", inst);
11781109
let mut map = FxHashMap::default();
@@ -1266,7 +1197,6 @@ fn log_function<F: Function>(func: &F) {
12661197

12671198
fn log_output<'a, F: Function>(env: &Env<'a, F>) {
12681199
trace!("Done!");
1269-
use alloc::format;
12701200
let mut v = Vec::new();
12711201
for i in 0..env.func.num_vregs() {
12721202
if env.vreg_spillslots[i].is_valid() {

0 commit comments

Comments
 (0)