Make spilling visit fallthrough_return instructions too.
This is a followup to af2a952aabd82cf401cc664d0262b139ff92d86b. It teaches the spilling pass to use the is_ghost() property to test whether to visit instructions. This fixes a bug handling multiple return values with fallthrough_return.
This commit is contained in:
committed by
Benjamin Bouvier
parent
ef2e11265c
commit
cd7c57e598
@@ -11,3 +11,13 @@ ebb0:
|
|||||||
; check: v2 = iconst.i64 0
|
; check: v2 = iconst.i64 0
|
||||||
; check: v3 = copy v2
|
; check: v3 = copy v2
|
||||||
; check: return v2, v3
|
; check: return v2, v3
|
||||||
|
|
||||||
|
; Same thing, now with a fallthrough_return.
|
||||||
|
function %multiple_returns() -> i64, i64 {
|
||||||
|
ebb0:
|
||||||
|
v2 = iconst.i64 0
|
||||||
|
fallthrough_return v2, v2
|
||||||
|
}
|
||||||
|
; check: v2 = iconst.i64 0
|
||||||
|
; check: v3 = copy v2
|
||||||
|
; check: fallthrough_return v2, v3
|
||||||
|
|||||||
@@ -134,11 +134,8 @@ impl<'a> Context<'a> {
|
|||||||
self.process_spills(tracker);
|
self.process_spills(tracker);
|
||||||
|
|
||||||
while let Some(inst) = self.cur.next_inst() {
|
while let Some(inst) = self.cur.next_inst() {
|
||||||
if let Some(constraints) = self
|
if !self.cur.func.dfg[inst].opcode().is_ghost() {
|
||||||
.encinfo
|
self.visit_inst(inst, ebb, tracker);
|
||||||
.operand_constraints(self.cur.func.encodings[inst])
|
|
||||||
{
|
|
||||||
self.visit_inst(inst, ebb, constraints, tracker);
|
|
||||||
} else {
|
} else {
|
||||||
let (_throughs, kills) = tracker.process_ghost(inst);
|
let (_throughs, kills) = tracker.process_ghost(inst);
|
||||||
self.free_regs(kills);
|
self.free_regs(kills);
|
||||||
@@ -237,17 +234,15 @@ impl<'a> Context<'a> {
|
|||||||
self.free_dead_regs(params);
|
self.free_dead_regs(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_inst(
|
fn visit_inst(&mut self, inst: Inst, ebb: Ebb, tracker: &mut LiveValueTracker) {
|
||||||
&mut self,
|
|
||||||
inst: Inst,
|
|
||||||
ebb: Ebb,
|
|
||||||
constraints: &RecipeConstraints,
|
|
||||||
tracker: &mut LiveValueTracker,
|
|
||||||
) {
|
|
||||||
debug!("Inst {}, {}", self.cur.display_inst(inst), self.pressure);
|
debug!("Inst {}, {}", self.cur.display_inst(inst), self.pressure);
|
||||||
debug_assert_eq!(self.cur.current_inst(), Some(inst));
|
debug_assert_eq!(self.cur.current_inst(), Some(inst));
|
||||||
debug_assert_eq!(self.cur.current_ebb(), Some(ebb));
|
debug_assert_eq!(self.cur.current_ebb(), Some(ebb));
|
||||||
|
|
||||||
|
let constraints = self
|
||||||
|
.encinfo
|
||||||
|
.operand_constraints(self.cur.func.encodings[inst]);
|
||||||
|
|
||||||
// We may need to resolve register constraints if there are any noteworthy uses.
|
// We may need to resolve register constraints if there are any noteworthy uses.
|
||||||
debug_assert!(self.reg_uses.is_empty());
|
debug_assert!(self.reg_uses.is_empty());
|
||||||
self.collect_reg_uses(inst, ebb, constraints);
|
self.collect_reg_uses(inst, ebb, constraints);
|
||||||
@@ -282,23 +277,25 @@ impl<'a> Context<'a> {
|
|||||||
// Make sure we have enough registers for the register defs.
|
// Make sure we have enough registers for the register defs.
|
||||||
// Dead defs are included here. They need a register too.
|
// Dead defs are included here. They need a register too.
|
||||||
// No need to process call return values, they are in fixed registers.
|
// No need to process call return values, they are in fixed registers.
|
||||||
for op in constraints.outs {
|
if let Some(constraints) = constraints {
|
||||||
if op.kind != ConstraintKind::Stack {
|
for op in constraints.outs {
|
||||||
// Add register def to pressure, spill if needed.
|
if op.kind != ConstraintKind::Stack {
|
||||||
while let Err(mask) = self.pressure.take_transient(op.regclass) {
|
// Add register def to pressure, spill if needed.
|
||||||
debug!("Need {} reg from {} throughs", op.regclass, throughs.len());
|
while let Err(mask) = self.pressure.take_transient(op.regclass) {
|
||||||
match self.spill_candidate(mask, throughs) {
|
debug!("Need {} reg from {} throughs", op.regclass, throughs.len());
|
||||||
Some(cand) => self.spill_reg(cand),
|
match self.spill_candidate(mask, throughs) {
|
||||||
None => panic!(
|
Some(cand) => self.spill_reg(cand),
|
||||||
"Ran out of {} registers for {}",
|
None => panic!(
|
||||||
op.regclass,
|
"Ran out of {} registers for {}",
|
||||||
self.cur.display_inst(inst)
|
op.regclass,
|
||||||
),
|
self.cur.display_inst(inst)
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.pressure.reset_transient();
|
||||||
}
|
}
|
||||||
self.pressure.reset_transient();
|
|
||||||
|
|
||||||
// Restore pressure state, compute pressure with affinities from `defs`.
|
// Restore pressure state, compute pressure with affinities from `defs`.
|
||||||
// Exclude dead defs. Includes call return values.
|
// Exclude dead defs. Includes call return values.
|
||||||
@@ -315,35 +312,42 @@ impl<'a> Context<'a> {
|
|||||||
// We are assuming here that if a value is used both by a fixed register operand and a register
|
// We are assuming here that if a value is used both by a fixed register operand and a register
|
||||||
// class operand, they two are compatible. We are also assuming that two register class
|
// class operand, they two are compatible. We are also assuming that two register class
|
||||||
// operands are always compatible.
|
// operands are always compatible.
|
||||||
fn collect_reg_uses(&mut self, inst: Inst, ebb: Ebb, constraints: &RecipeConstraints) {
|
fn collect_reg_uses(&mut self, inst: Inst, ebb: Ebb, constraints: Option<&RecipeConstraints>) {
|
||||||
let args = self.cur.func.dfg.inst_args(inst);
|
let args = self.cur.func.dfg.inst_args(inst);
|
||||||
for (idx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() {
|
let num_fixed_ins = if let Some(constraints) = constraints {
|
||||||
let mut reguse = RegUse::new(arg, idx, op.regclass.into());
|
for (idx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() {
|
||||||
let lr = &self.liveness[arg];
|
let mut reguse = RegUse::new(arg, idx, op.regclass.into());
|
||||||
let ctx = self.liveness.context(&self.cur.func.layout);
|
let lr = &self.liveness[arg];
|
||||||
match op.kind {
|
let ctx = self.liveness.context(&self.cur.func.layout);
|
||||||
ConstraintKind::Stack => continue,
|
match op.kind {
|
||||||
ConstraintKind::FixedReg(_) => reguse.fixed = true,
|
ConstraintKind::Stack => continue,
|
||||||
ConstraintKind::Tied(_) => {
|
ConstraintKind::FixedReg(_) => reguse.fixed = true,
|
||||||
// A tied operand must kill the used value.
|
ConstraintKind::Tied(_) => {
|
||||||
reguse.tied = !lr.killed_at(inst, ebb, ctx);
|
// A tied operand must kill the used value.
|
||||||
|
reguse.tied = !lr.killed_at(inst, ebb, ctx);
|
||||||
|
}
|
||||||
|
ConstraintKind::FixedTied(_) => {
|
||||||
|
reguse.fixed = true;
|
||||||
|
reguse.tied = !lr.killed_at(inst, ebb, ctx);
|
||||||
|
}
|
||||||
|
ConstraintKind::Reg => {}
|
||||||
}
|
}
|
||||||
ConstraintKind::FixedTied(_) => {
|
if lr.affinity.is_stack() {
|
||||||
reguse.fixed = true;
|
reguse.spilled = true;
|
||||||
reguse.tied = !lr.killed_at(inst, ebb, ctx);
|
|
||||||
}
|
}
|
||||||
ConstraintKind::Reg => {}
|
|
||||||
}
|
|
||||||
if lr.affinity.is_stack() {
|
|
||||||
reguse.spilled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only collect the interesting register uses.
|
// Only collect the interesting register uses.
|
||||||
if reguse.fixed || reguse.tied || reguse.spilled {
|
if reguse.fixed || reguse.tied || reguse.spilled {
|
||||||
debug!(" reguse: {}", reguse);
|
debug!(" reguse: {}", reguse);
|
||||||
self.reg_uses.push(reguse);
|
self.reg_uses.push(reguse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
constraints.ins.len()
|
||||||
|
} else {
|
||||||
|
// A non-ghost instruction with no constraints can't have any
|
||||||
|
// fixed operands.
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
// Similarly, for return instructions, collect uses of ABI-defined
|
// Similarly, for return instructions, collect uses of ABI-defined
|
||||||
// return values.
|
// return values.
|
||||||
@@ -356,7 +360,7 @@ impl<'a> Context<'a> {
|
|||||||
for (ret_idx, (ret, &arg)) in
|
for (ret_idx, (ret, &arg)) in
|
||||||
self.cur.func.signature.returns.iter().zip(args).enumerate()
|
self.cur.func.signature.returns.iter().zip(args).enumerate()
|
||||||
{
|
{
|
||||||
let idx = constraints.ins.len() + ret_idx;
|
let idx = num_fixed_ins + ret_idx;
|
||||||
let unit = match ret.location {
|
let unit = match ret.location {
|
||||||
ArgumentLoc::Unassigned => {
|
ArgumentLoc::Unassigned => {
|
||||||
panic!("function return signature should be legalized")
|
panic!("function return signature should be legalized")
|
||||||
|
|||||||
Reference in New Issue
Block a user