Add support for tied operands.
Include a very basic test using an Intel 'sub' instruction. More to follow.
This commit is contained in:
15
cranelift/filetests/regalloc/constraints.cton
Normal file
15
cranelift/filetests/regalloc/constraints.cton
Normal 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
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user