Allow spilling of EBB arguments.
When the spiller needs to make a register available for a conditional branch instruction, it can be necessary to spill some of the EBB arguments on the branch instruction. This is ok because EBB argument values belong to the same virtual register as the corresponding EBB parameter and we spill the whole virtreg to the same slot. Also make sure free_regs() can handle values that are killed by the current instruction *and* spilled.
This commit is contained in:
@@ -145,6 +145,30 @@ ebb1(v10: i32, v11: i32, v12: i32, v13: i32, v14: i32, v15: i32, v16: i32, v17:
|
|||||||
return v33
|
return v33
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Spilling an EBB argument to make room for a branch operand.
|
||||||
|
function %brargs(i32) -> i32 {
|
||||||
|
ebb0(v1: i32):
|
||||||
|
; check: $v1 = spill
|
||||||
|
v2 = iconst.i32 1
|
||||||
|
brnz v1, ebb1(v2, v2, v2, v2, v2, v2, v2, v2, v2, v2, v2, v2)
|
||||||
|
return v1
|
||||||
|
|
||||||
|
ebb1(v10: i32, v11: i32, v12: i32, v13: i32, v14: i32, v15: i32, v16: i32, v17: i32, v18: i32, v19: i32, v20: i32, v21: i32):
|
||||||
|
v22 = iadd v10, v11
|
||||||
|
v23 = iadd v22, v12
|
||||||
|
v24 = iadd v23, v13
|
||||||
|
v25 = iadd v24, v14
|
||||||
|
v26 = iadd v25, v15
|
||||||
|
v27 = iadd v26, v16
|
||||||
|
v28 = iadd v27, v17
|
||||||
|
v29 = iadd v28, v18
|
||||||
|
v30 = iadd v29, v19
|
||||||
|
v31 = iadd v30, v20
|
||||||
|
v32 = iadd v31, v21
|
||||||
|
v33 = iadd v32, v1
|
||||||
|
return v33
|
||||||
|
}
|
||||||
|
|
||||||
; In straight-line code, the first value defined is spilled.
|
; In straight-line code, the first value defined is spilled.
|
||||||
; That is in order:
|
; That is in order:
|
||||||
; 1. The argument v1.
|
; 1. The argument v1.
|
||||||
|
|||||||
@@ -155,11 +155,13 @@ impl<'a> Context<'a> {
|
|||||||
fn free_regs(&mut self, kills: &[LiveValue]) {
|
fn free_regs(&mut self, kills: &[LiveValue]) {
|
||||||
for lv in kills {
|
for lv in kills {
|
||||||
if let Affinity::Reg(rci) = lv.affinity {
|
if let Affinity::Reg(rci) = lv.affinity {
|
||||||
|
if !self.spills.contains(&lv.value) {
|
||||||
let rc = self.reginfo.rc(rci);
|
let rc = self.reginfo.rc(rci);
|
||||||
self.pressure.free(rc);
|
self.pressure.free(rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) {
|
fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) {
|
||||||
let (liveins, args) = tracker.ebb_top(
|
let (liveins, args) = tracker.ebb_top(
|
||||||
@@ -402,8 +404,17 @@ impl<'a> Context<'a> {
|
|||||||
dbg!("Copy of {} reg causes spill", rc);
|
dbg!("Copy of {} reg causes spill", rc);
|
||||||
// Spill a live register that is *not* used by the current instruction.
|
// Spill a live register that is *not* used by the current instruction.
|
||||||
// Spilling a use wouldn't help.
|
// Spilling a use wouldn't help.
|
||||||
|
//
|
||||||
|
// Do allow spilling of EBB arguments on branches. This is safe since we spill
|
||||||
|
// the whole virtual register which includes the matching EBB parameter value
|
||||||
|
// at the branch destination. It is also necessary since there can be
|
||||||
|
// arbitrarily many EBB arguments.
|
||||||
match {
|
match {
|
||||||
let args = self.cur.func.dfg.inst_args(inst);
|
let args = if self.cur.func.dfg[inst].opcode().is_branch() {
|
||||||
|
self.cur.func.dfg.inst_fixed_args(inst)
|
||||||
|
} else {
|
||||||
|
self.cur.func.dfg.inst_args(inst)
|
||||||
|
};
|
||||||
self.spill_candidate(
|
self.spill_candidate(
|
||||||
mask,
|
mask,
|
||||||
tracker.live().iter().filter(|lv| !args.contains(&lv.value)),
|
tracker.live().iter().filter(|lv| !args.contains(&lv.value)),
|
||||||
|
|||||||
Reference in New Issue
Block a user