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.
|
||||
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
|
||||
// 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);
|
||||
self.program_fixed_outputs(
|
||||
constraints.outs,
|
||||
defs,
|
||||
throughs,
|
||||
&mut replace_global_defines,
|
||||
®s.global,
|
||||
);
|
||||
}
|
||||
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);
|
||||
|
||||
// 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.
|
||||
// We expect a quick solution in most cases.
|
||||
let output_regs = self.solver.quick_solve(®s.global).unwrap_or_else(|_| {
|
||||
@@ -675,12 +686,23 @@ impl<'a> Context<'a> {
|
||||
constraints: &[OperandConstraint],
|
||||
defs: &[LiveValue],
|
||||
throughs: &[LiveValue],
|
||||
replace_global_defines: &mut bool,
|
||||
global_regs: &AllocatableSet,
|
||||
) {
|
||||
for (op, lv) in constraints.iter().zip(defs) {
|
||||
match op.kind {
|
||||
ConstraintKind::FixedReg(reg) |
|
||||
ConstraintKind::FixedTied(reg) => {
|
||||
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::Tied(_) |
|
||||
@@ -693,7 +715,14 @@ impl<'a> Context<'a> {
|
||||
/// Program the output-side ABI constraints for `inst` into the constraint solver.
|
||||
///
|
||||
/// 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
|
||||
// variable list of results, but we have no known instances of that.
|
||||
// Just assume all results are variable return values.
|
||||
@@ -704,6 +733,15 @@ impl<'a> Context<'a> {
|
||||
if let Affinity::Reg(rci) = lv.affinity {
|
||||
let rc = self.reginfo.rc(rci);
|
||||
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 {
|
||||
panic!("ABI argument {} should be in a register", lv.value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user