Fix coloring bug with combined constraints and global values.
The Intel instruction "v1 = ushr v2, v2" will implicitly fix the output register for v2 to %rcx because the output is tied to the first input operand and the second input operand is fixed to %rcx. Make sure we handle this transitive constraint when checking for interference with the globally live registers. Fixes #218
This commit is contained in:
@@ -27,3 +27,26 @@ ebb0(v0: i64, v1: i64, v2: i64, v3: i64):
|
||||
ebb1:
|
||||
return v4
|
||||
}
|
||||
|
||||
; Found by the Binaryen fuzzer in PR218.
|
||||
;
|
||||
; This is a similar situation involving combined constraints on the ushr instruction:
|
||||
;
|
||||
; - The %rcx register is already in use by a globally live value.
|
||||
; - The ushr x, x result is also a globally live value.
|
||||
;
|
||||
; Since the ushr x, x result is forced to be placed in %rcx, we must set the replace_global_defines
|
||||
; flag so it can be reassigned to a different global register.
|
||||
function %pr218(i64 [%rdi], i64 [%rsi], i64 [%rdx], i64 [%rcx]) -> i64 [%rax] {
|
||||
ebb0(v0: i64, v1: i64, v2: i64, v3: i64):
|
||||
; check: regmove $v3, %rcx ->
|
||||
v4 = ushr v0, v0
|
||||
; check: $v4 = copy
|
||||
jump ebb1
|
||||
|
||||
ebb1:
|
||||
; v3 is globally live in %rcx.
|
||||
; v4 is also globally live. Needs to be assigned something else for the trip across the CFG edge.
|
||||
v5 = iadd v3, v4
|
||||
return v5
|
||||
}
|
||||
|
||||
@@ -411,7 +411,13 @@ impl<'a> Context<'a> {
|
||||
®s.global,
|
||||
);
|
||||
}
|
||||
self.program_output_constraints(inst, constraints.outs, defs);
|
||||
self.program_output_constraints(
|
||||
inst,
|
||||
constraints.outs,
|
||||
defs,
|
||||
&mut replace_global_defines,
|
||||
®s.global,
|
||||
);
|
||||
|
||||
// Finally, we've fully programmed the constraint solver.
|
||||
// We expect a quick solution in most cases.
|
||||
@@ -794,6 +800,8 @@ impl<'a> Context<'a> {
|
||||
inst: Inst,
|
||||
constraints: &[OperandConstraint],
|
||||
defs: &[LiveValue],
|
||||
replace_global_defines: &mut bool,
|
||||
global_regs: &AllocatableSet,
|
||||
) {
|
||||
for (op, lv) in constraints.iter().zip(defs) {
|
||||
match op.kind {
|
||||
@@ -807,12 +815,26 @@ impl<'a> Context<'a> {
|
||||
// Find the input operand we're tied to.
|
||||
// The solver doesn't care about the output value.
|
||||
let arg = self.cur.func.dfg.inst_args(inst)[num as usize];
|
||||
self.solver.add_tied_input(
|
||||
if let Some(reg) = self.solver.add_tied_input(
|
||||
arg,
|
||||
op.regclass,
|
||||
self.divert.reg(arg, &self.cur.func.locations),
|
||||
!lv.is_local,
|
||||
);
|
||||
)
|
||||
{
|
||||
// The value we're tied to has been assigned to a fixed register.
|
||||
// We need to make sure that fixed output register is compatible with the
|
||||
// global register set.
|
||||
if !lv.is_local && !global_regs.is_avail(op.regclass, reg) {
|
||||
dbg!(
|
||||
"Tied output {} in {}:{} is not available in global regs",
|
||||
lv.value,
|
||||
op.regclass,
|
||||
self.reginfo.display_regunit(reg)
|
||||
);
|
||||
*replace_global_defines = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,14 +760,22 @@ impl Solver {
|
||||
///
|
||||
/// The output value that must have the same register as the input value is not recorded in the
|
||||
/// solver.
|
||||
pub fn add_tied_input(&mut self, value: Value, rc: RegClass, reg: RegUnit, is_global: bool) {
|
||||
///
|
||||
/// If the value has already been assigned to a fixed register, return that.
|
||||
pub fn add_tied_input(
|
||||
&mut self,
|
||||
value: Value,
|
||||
rc: RegClass,
|
||||
reg: RegUnit,
|
||||
is_global: bool,
|
||||
) -> Option<RegUnit> {
|
||||
debug_assert!(self.inputs_done);
|
||||
|
||||
// If a fixed assignment is tied, the `to` register is not available on the output side.
|
||||
if let Some(a) = self.assignments.get(value) {
|
||||
debug_assert_eq!(a.from, reg);
|
||||
self.regs_out.take(a.rc, a.to);
|
||||
return;
|
||||
return Some(a.to);
|
||||
}
|
||||
|
||||
// Check if a variable was created.
|
||||
@@ -775,7 +783,7 @@ impl Solver {
|
||||
assert!(v.is_input);
|
||||
v.is_output = true;
|
||||
v.is_global = is_global;
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
// No variable exists for `value` because its constraints are already satisfied.
|
||||
@@ -790,6 +798,8 @@ impl Solver {
|
||||
} else {
|
||||
self.regs_out.take(rc, reg);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Add a fixed output assignment.
|
||||
|
||||
Reference in New Issue
Block a user