From 0d2d1ea8cf40eea1473ca06751393e8d7bf26ecd Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 30 Jun 2017 13:34:18 -0700 Subject: [PATCH] Add support for tied operands. Include a very basic test using an Intel 'sub' instruction. More to follow. --- cranelift/filetests/regalloc/constraints.cton | 15 +++++++++++ lib/cretonne/src/regalloc/coloring.rs | 25 +++++++++++++++--- lib/cretonne/src/regalloc/solver.rs | 26 +++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 cranelift/filetests/regalloc/constraints.cton diff --git a/cranelift/filetests/regalloc/constraints.cton b/cranelift/filetests/regalloc/constraints.cton new file mode 100644 index 0000000000..e88bcc514c --- /dev/null +++ b/cranelift/filetests/regalloc/constraints.cton @@ -0,0 +1,15 @@ +test regalloc +isa intel + +; regex: V=v\d+ + +; Tied operands, both are killed at instruction. +function %tied_easy() -> i32 { +ebb0: + v0 = iconst.i32 12 + v1 = iconst.i32 13 + ; not: copy + ; check: isub + v2 = isub v0, v1 + return v2 +} diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 733506ef0d..4ebf3340f9 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -363,6 +363,17 @@ impl<'a> Context<'a> { locations[v.value] = ValueLoc::Reg(v.solution); } + // 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 = dfg.inst_args(inst)[num as usize]; + locations[lv.value] = locations[arg]; + } + } + } + // Update `regs` for the next instruction, remove the dead defs. for lv in defs { if lv.endpoint == inst { @@ -638,11 +649,11 @@ impl<'a> Context<'a> { /// /// It is assumed that all fixed outputs have already been handled. fn program_output_constraints(&mut self, - _inst: Inst, + inst: Inst, constraints: &[OperandConstraint], defs: &[LiveValue], - _dfg: &mut DataFlowGraph, - _locations: &mut ValueLocations) { + dfg: &mut DataFlowGraph, + locations: &mut ValueLocations) { for (op, lv) in constraints.iter().zip(defs) { match op.kind { ConstraintKind::FixedReg(_) | @@ -650,7 +661,13 @@ impl<'a> Context<'a> { ConstraintKind::Reg => { self.solver.add_def(lv.value, op.regclass); } - ConstraintKind::Tied(_) => unimplemented!(), + ConstraintKind::Tied(num) => { + // Find the input operand we're tied to. + // The solver doesn't care about the output value. + let arg = dfg.inst_args(inst)[num as usize]; + self.solver + .add_tied_input(arg, op.regclass, self.divert.reg(arg, locations)); + } } } } diff --git a/lib/cretonne/src/regalloc/solver.rs b/lib/cretonne/src/regalloc/solver.rs index b2e73bf91f..b135f051f3 100644 --- a/lib/cretonne/src/regalloc/solver.rs +++ b/lib/cretonne/src/regalloc/solver.rs @@ -488,6 +488,32 @@ impl Solver { self.regs_out.free(rc, reg); } + /// Record that an input register is tied to an output register. + /// + /// It is assumed that `add_kill` was called previously with the same arguments. + /// + /// 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) { + 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; + } + + // Check if a variable was created. + if let Some(v) = self.vars.iter_mut().find(|v| v.value == value) { + assert!(v.is_input); + v.is_output = true; + return; + } + + self.regs_out.take(rc, reg); + } + /// Add a fixed output assignment. /// /// This means that `to` will not be available for variables on the output side of the