Also consider fixed outputs for replace_global_defines.
Fixes #178. When an instruction with a fixed output operand defines a globally live SSA value, we need to check if the fixed register is available in the `regs.global` set of registers that can be used across EBB boundaries. If the fixed output register is not available in regs.global, set the replace_global_defines flag so the output operands are rewritten as local values.
This commit is contained in:
17
cranelift/filetests/regalloc/global-fixed.cton
Normal file
17
cranelift/filetests/regalloc/global-fixed.cton
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
test compile
|
||||||
|
set is_64bit=1
|
||||||
|
isa intel haswell
|
||||||
|
|
||||||
|
function %foo() native {
|
||||||
|
ebb4:
|
||||||
|
v3 = iconst.i32 0
|
||||||
|
jump ebb3
|
||||||
|
|
||||||
|
ebb3:
|
||||||
|
v9 = udiv v3, v3
|
||||||
|
jump ebb1
|
||||||
|
|
||||||
|
ebb1:
|
||||||
|
v19 = iadd.i32 v9, v9
|
||||||
|
jump ebb3
|
||||||
|
}
|
||||||
@@ -377,23 +377,34 @@ impl<'a> Context<'a> {
|
|||||||
// This aligns with the " from" line at the top of the function.
|
// This aligns with the " from" line at the top of the function.
|
||||||
dbg!(" glob {}", regs.global.display(&self.reginfo));
|
dbg!(" glob {}", regs.global.display(&self.reginfo));
|
||||||
|
|
||||||
|
// This flag is set when the solver failed to find a solution for the global defines that
|
||||||
|
// doesn't interfere with `regs.global`. We need to rewrite all of `inst`s global defines
|
||||||
|
// as local defines followed by copies.
|
||||||
|
let mut replace_global_defines = false;
|
||||||
|
|
||||||
// Program the fixed output constraints before the general defines. This allows us to
|
// 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
|
// detect conflicts between fixed outputs and tied operands where the input value hasn't
|
||||||
// been converted to a solver variable.
|
// been converted to a solver variable.
|
||||||
if constraints.fixed_outs {
|
if constraints.fixed_outs {
|
||||||
self.program_fixed_outputs(constraints.outs, defs, throughs);
|
self.program_fixed_outputs(
|
||||||
|
constraints.outs,
|
||||||
|
defs,
|
||||||
|
throughs,
|
||||||
|
&mut replace_global_defines,
|
||||||
|
®s.global,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(sig) = call_sig {
|
if let Some(sig) = call_sig {
|
||||||
self.program_output_abi(sig, defs, throughs);
|
self.program_output_abi(
|
||||||
|
sig,
|
||||||
|
defs,
|
||||||
|
throughs,
|
||||||
|
&mut replace_global_defines,
|
||||||
|
®s.global,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.program_output_constraints(inst, constraints.outs, defs);
|
self.program_output_constraints(inst, constraints.outs, defs);
|
||||||
|
|
||||||
// This flag is set when the solver failed to find a solution for the global defines that
|
|
||||||
// doesn't interfere with `regs.global`. We need to rewrite all of `inst`s global defines
|
|
||||||
// as local defines followed by copies.
|
|
||||||
let mut replace_global_defines = false;
|
|
||||||
|
|
||||||
// Finally, we've fully programmed the constraint solver.
|
// Finally, we've fully programmed the constraint solver.
|
||||||
// We expect a quick solution in most cases.
|
// We expect a quick solution in most cases.
|
||||||
let output_regs = self.solver.quick_solve(®s.global).unwrap_or_else(|_| {
|
let output_regs = self.solver.quick_solve(®s.global).unwrap_or_else(|_| {
|
||||||
@@ -675,12 +686,23 @@ impl<'a> Context<'a> {
|
|||||||
constraints: &[OperandConstraint],
|
constraints: &[OperandConstraint],
|
||||||
defs: &[LiveValue],
|
defs: &[LiveValue],
|
||||||
throughs: &[LiveValue],
|
throughs: &[LiveValue],
|
||||||
|
replace_global_defines: &mut bool,
|
||||||
|
global_regs: &AllocatableSet,
|
||||||
) {
|
) {
|
||||||
for (op, lv) in constraints.iter().zip(defs) {
|
for (op, lv) in constraints.iter().zip(defs) {
|
||||||
match op.kind {
|
match op.kind {
|
||||||
ConstraintKind::FixedReg(reg) |
|
ConstraintKind::FixedReg(reg) |
|
||||||
ConstraintKind::FixedTied(reg) => {
|
ConstraintKind::FixedTied(reg) => {
|
||||||
self.add_fixed_output(lv.value, op.regclass, reg, throughs);
|
self.add_fixed_output(lv.value, op.regclass, reg, throughs);
|
||||||
|
if !lv.is_local && !global_regs.is_avail(op.regclass, reg) {
|
||||||
|
dbg!(
|
||||||
|
"Fixed output {} in {}:{} is not available in global regs",
|
||||||
|
lv.value,
|
||||||
|
op.regclass,
|
||||||
|
self.reginfo.display_regunit(reg)
|
||||||
|
);
|
||||||
|
*replace_global_defines = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ConstraintKind::Reg |
|
ConstraintKind::Reg |
|
||||||
ConstraintKind::Tied(_) |
|
ConstraintKind::Tied(_) |
|
||||||
@@ -693,7 +715,14 @@ impl<'a> Context<'a> {
|
|||||||
/// Program the output-side ABI constraints for `inst` into the constraint solver.
|
/// Program the output-side ABI constraints for `inst` into the constraint solver.
|
||||||
///
|
///
|
||||||
/// That means return values for a call instruction.
|
/// That means return values for a call instruction.
|
||||||
fn program_output_abi(&mut self, sig: SigRef, defs: &[LiveValue], throughs: &[LiveValue]) {
|
fn program_output_abi(
|
||||||
|
&mut self,
|
||||||
|
sig: SigRef,
|
||||||
|
defs: &[LiveValue],
|
||||||
|
throughs: &[LiveValue],
|
||||||
|
replace_global_defines: &mut bool,
|
||||||
|
global_regs: &AllocatableSet,
|
||||||
|
) {
|
||||||
// It's technically possible for a call instruction to have fixed results before the
|
// It's technically possible for a call instruction to have fixed results before the
|
||||||
// variable list of results, but we have no known instances of that.
|
// variable list of results, but we have no known instances of that.
|
||||||
// Just assume all results are variable return values.
|
// Just assume all results are variable return values.
|
||||||
@@ -704,6 +733,15 @@ impl<'a> Context<'a> {
|
|||||||
if let Affinity::Reg(rci) = lv.affinity {
|
if let Affinity::Reg(rci) = lv.affinity {
|
||||||
let rc = self.reginfo.rc(rci);
|
let rc = self.reginfo.rc(rci);
|
||||||
self.add_fixed_output(lv.value, rc, reg, throughs);
|
self.add_fixed_output(lv.value, rc, reg, throughs);
|
||||||
|
if !lv.is_local && !global_regs.is_avail(rc, reg) {
|
||||||
|
dbg!(
|
||||||
|
"ABI output {} in {}:{} is not available in global regs",
|
||||||
|
lv.value,
|
||||||
|
rc,
|
||||||
|
self.reginfo.display_regunit(reg)
|
||||||
|
);
|
||||||
|
*replace_global_defines = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("ABI argument {} should be in a register", lv.value);
|
panic!("ABI argument {} should be in a register", lv.value);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user