Make regalloc visit fallthrough_return instructions.

Add an explicit "is_ghost" property to selected instructions, and use
that to determine whether reload and coloring should visit instructions.
This allows them to visit fallthrough_return instructions and insert
fills and register moves as needed.
This commit is contained in:
Dan Gohman
2018-10-26 06:39:44 -07:00
committed by Benjamin Bouvier
parent 681cb5e20a
commit 88bbbca6cd
5 changed files with 101 additions and 60 deletions

View File

@@ -165,8 +165,9 @@ impl<'a> Context<'a> {
self.cur.goto_top(ebb);
while let Some(inst) = self.cur.next_inst() {
self.cur.use_srcloc(inst);
let enc = self.cur.func.encodings[inst];
if let Some(constraints) = self.encinfo.operand_constraints(enc) {
if !self.cur.func.dfg[inst].opcode().is_ghost() {
let enc = self.cur.func.encodings[inst];
let constraints = self.encinfo.operand_constraints(enc);
if self.visit_inst(inst, constraints, tracker, &mut regs) {
self.replace_global_defines(inst, tracker);
// Restore cursor location after `replace_global_defines` moves it.
@@ -290,7 +291,7 @@ impl<'a> Context<'a> {
fn visit_inst(
&mut self,
inst: Inst,
constraints: &RecipeConstraints,
constraints: Option<&RecipeConstraints>,
tracker: &mut LiveValueTracker,
regs: &mut AvailableRegs,
) -> bool {
@@ -306,7 +307,9 @@ impl<'a> Context<'a> {
// Program the solver with register constraints for the input side.
self.solver.reset(&regs.input);
self.program_input_constraints(inst, constraints.ins);
if let Some(constraints) = constraints {
self.program_input_constraints(inst, constraints.ins);
}
let call_sig = self.cur.func.dfg.call_signature(inst);
if let Some(sig) = call_sig {
program_input_abi(
@@ -390,14 +393,16 @@ impl<'a> Context<'a> {
// Program the fixed output constraints before the general defines. This allows us to
// detect conflicts between fixed outputs and tied operands where the input value hasn't
// been converted to a solver variable.
if constraints.fixed_outs {
self.program_fixed_outputs(
constraints.outs,
defs,
throughs,
&mut replace_global_defines,
&regs.global,
);
if let Some(constraints) = constraints {
if constraints.fixed_outs {
self.program_fixed_outputs(
constraints.outs,
defs,
throughs,
&mut replace_global_defines,
&regs.global,
);
}
}
if let Some(sig) = call_sig {
self.program_output_abi(
@@ -408,13 +413,15 @@ impl<'a> Context<'a> {
&regs.global,
);
}
self.program_output_constraints(
inst,
constraints.outs,
defs,
&mut replace_global_defines,
&regs.global,
);
if let Some(constraints) = constraints {
self.program_output_constraints(
inst,
constraints.outs,
defs,
&mut replace_global_defines,
&regs.global,
);
}
// Finally, we've fully programmed the constraint solver.
// We expect a quick solution in most cases.
@@ -440,12 +447,14 @@ impl<'a> Context<'a> {
// Tied defs are not part of the solution above.
// Copy register assignments from tied inputs to tied outputs.
if constraints.tied_ops {
for (op, lv) in constraints.outs.iter().zip(defs) {
if let ConstraintKind::Tied(num) = op.kind {
let arg = self.cur.func.dfg.inst_args(inst)[num as usize];
let reg = self.divert.reg(arg, &self.cur.func.locations);
self.cur.func.locations[lv.value] = ValueLoc::Reg(reg);
if let Some(constraints) = constraints {
if constraints.tied_ops {
for (op, lv) in constraints.outs.iter().zip(defs) {
if let ConstraintKind::Tied(num) = op.kind {
let arg = self.cur.func.dfg.inst_args(inst)[num as usize];
let reg = self.divert.reg(arg, &self.cur.func.locations);
self.cur.func.locations[lv.value] = ValueLoc::Reg(reg);
}
}
}
}

View File

@@ -125,8 +125,8 @@ impl<'a> Context<'a> {
// visit_ebb_header() places us at the first interesting instruction in the EBB.
while let Some(inst) = self.cur.current_inst() {
let encoding = self.cur.func.encodings[inst];
if encoding.is_legal() {
if !self.cur.func.dfg[inst].opcode().is_ghost() {
let encoding = self.cur.func.encodings[inst];
self.visit_inst(ebb, inst, encoding, tracker);
tracker.drop_dead(inst);
} else {
@@ -200,10 +200,7 @@ impl<'a> Context<'a> {
self.cur.use_srcloc(inst);
// Get the operand constraints for `inst` that we are trying to satisfy.
let constraints = self
.encinfo
.operand_constraints(encoding)
.expect("Missing instruction encoding");
let constraints = self.encinfo.operand_constraints(encoding);
// Identify reload candidates.
debug_assert!(self.candidates.is_empty());
@@ -240,27 +237,32 @@ impl<'a> Context<'a> {
// v2 = spill v7
//
// That way, we don't need to rewrite all future uses of v2.
for (lv, op) in defs.iter().zip(constraints.outs) {
if lv.affinity.is_stack() && op.kind != ConstraintKind::Stack {
if let InstructionData::Unary {
opcode: Opcode::Copy,
arg,
} = self.cur.func.dfg[inst]
{
self.cur.func.dfg.replace(inst).spill(arg);
let ok = self.cur.func.update_encoding(inst, self.cur.isa).is_ok();
debug_assert!(ok);
} else {
let value_type = self.cur.func.dfg.value_type(lv.value);
let reg = self.cur.func.dfg.replace_result(lv.value, value_type);
self.liveness.create_dead(reg, inst, Affinity::new(op));
self.insert_spill(ebb, lv.value, reg);
if let Some(constraints) = constraints {
for (lv, op) in defs.iter().zip(constraints.outs) {
if lv.affinity.is_stack() && op.kind != ConstraintKind::Stack {
if let InstructionData::Unary {
opcode: Opcode::Copy,
arg,
} = self.cur.func.dfg[inst]
{
self.cur.func.dfg.replace(inst).spill(arg);
let ok = self.cur.func.update_encoding(inst, self.cur.isa).is_ok();
debug_assert!(ok);
} else {
let value_type = self.cur.func.dfg.value_type(lv.value);
let reg = self.cur.func.dfg.replace_result(lv.value, value_type);
self.liveness.create_dead(reg, inst, Affinity::new(op));
self.insert_spill(ebb, lv.value, reg);
}
}
}
}
// Same thing for spilled call return values.
let retvals = &defs[constraints.outs.len()..];
let retvals = &defs[self.cur.func.dfg[inst]
.opcode()
.constraints()
.fixed_results()..];
if !retvals.is_empty() {
let sig = self
.cur
@@ -342,21 +344,26 @@ impl<'a> Context<'a> {
// Find reload candidates for `inst` and add them to `self.candidates`.
//
// These are uses of spilled values where the operand constraint requires a register.
fn find_candidates(&mut self, inst: Inst, constraints: &RecipeConstraints) {
fn find_candidates(&mut self, inst: Inst, constraints: Option<&RecipeConstraints>) {
let args = self.cur.func.dfg.inst_args(inst);
for (argidx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() {
if op.kind != ConstraintKind::Stack && self.liveness[arg].affinity.is_stack() {
self.candidates.push(ReloadCandidate {
argidx,
value: arg,
regclass: op.regclass,
})
if let Some(constraints) = constraints {
for (argidx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() {
if op.kind != ConstraintKind::Stack && self.liveness[arg].affinity.is_stack() {
self.candidates.push(ReloadCandidate {
argidx,
value: arg,
regclass: op.regclass,
})
}
}
}
// If we only have the fixed arguments, we're done now.
let offset = constraints.ins.len();
let offset = self.cur.func.dfg[inst]
.opcode()
.constraints()
.fixed_value_arguments();
if args.len() == offset {
return;
}