From d37126565eaf2b45c2fbd815059bfe1cb03f665f Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 25 Oct 2017 14:28:30 -0700 Subject: [PATCH] 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. --- .../filetests/regalloc/global-fixed.cton | 17 ++++++ lib/cretonne/src/regalloc/coloring.rs | 54 ++++++++++++++++--- 2 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 cranelift/filetests/regalloc/global-fixed.cton diff --git a/cranelift/filetests/regalloc/global-fixed.cton b/cranelift/filetests/regalloc/global-fixed.cton new file mode 100644 index 0000000000..9bdcde3be0 --- /dev/null +++ b/cranelift/filetests/regalloc/global-fixed.cton @@ -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 +} diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index b44860c958..d8607e4bd1 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -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); }