Add support for tied operands.

Include a very basic test using an Intel 'sub' instruction. More to
follow.
This commit is contained in:
Jakob Stoklund Olesen
2017-06-30 13:34:18 -07:00
parent 811c1059fc
commit 0d2d1ea8cf
3 changed files with 62 additions and 4 deletions

View File

@@ -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
}

View File

@@ -363,6 +363,17 @@ impl<'a> Context<'a> {
locations[v.value] = ValueLoc::Reg(v.solution); 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. // Update `regs` for the next instruction, remove the dead defs.
for lv in defs { for lv in defs {
if lv.endpoint == inst { if lv.endpoint == inst {
@@ -638,11 +649,11 @@ impl<'a> Context<'a> {
/// ///
/// It is assumed that all fixed outputs have already been handled. /// It is assumed that all fixed outputs have already been handled.
fn program_output_constraints(&mut self, fn program_output_constraints(&mut self,
_inst: Inst, inst: Inst,
constraints: &[OperandConstraint], constraints: &[OperandConstraint],
defs: &[LiveValue], defs: &[LiveValue],
_dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
_locations: &mut ValueLocations) { locations: &mut ValueLocations) {
for (op, lv) in constraints.iter().zip(defs) { for (op, lv) in constraints.iter().zip(defs) {
match op.kind { match op.kind {
ConstraintKind::FixedReg(_) | ConstraintKind::FixedReg(_) |
@@ -650,7 +661,13 @@ impl<'a> Context<'a> {
ConstraintKind::Reg => { ConstraintKind::Reg => {
self.solver.add_def(lv.value, op.regclass); 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));
}
} }
} }
} }

View File

@@ -488,6 +488,32 @@ impl Solver {
self.regs_out.free(rc, reg); 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. /// Add a fixed output assignment.
/// ///
/// This means that `to` will not be available for variables on the output side of the /// This means that `to` will not be available for variables on the output side of the