Add fixed constraints for ABI arguments and return values.
We can start adding some real test cases for the move resolver now.
This commit is contained in:
@@ -3,6 +3,8 @@ test regalloc
|
|||||||
; We can add more ISAs once they have defined encodings.
|
; We can add more ISAs once they have defined encodings.
|
||||||
isa riscv
|
isa riscv
|
||||||
|
|
||||||
|
; regex: RX=%x\d+
|
||||||
|
|
||||||
function add(i32, i32) {
|
function add(i32, i32) {
|
||||||
ebb0(v1: i32, v2: i32):
|
ebb0(v1: i32, v2: i32):
|
||||||
v3 = iadd v1, v2
|
v3 = iadd v1, v2
|
||||||
@@ -14,6 +16,27 @@ ebb0(v1: i32, v2: i32):
|
|||||||
; Function with a dead argument.
|
; Function with a dead argument.
|
||||||
function dead_arg(i32, i32) -> i32{
|
function dead_arg(i32, i32) -> i32{
|
||||||
ebb0(v1: i32, v2: i32):
|
ebb0(v1: i32, v2: i32):
|
||||||
|
; not: regmove
|
||||||
; check: return $v1
|
; check: return $v1
|
||||||
return v1
|
return v1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Return a value from a different register.
|
||||||
|
function move1(i32, i32) -> i32 {
|
||||||
|
ebb0(v1: i32, v2: i32):
|
||||||
|
; not: regmove
|
||||||
|
; check: regmove $v2, %x11 -> %x10
|
||||||
|
; nextln: return $v2
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
; Swap two registers.
|
||||||
|
function swap(i32, i32) -> i32, i32 {
|
||||||
|
ebb0(v1: i32, v2: i32):
|
||||||
|
; not: regmove
|
||||||
|
; check: regmove $v2, %x11 -> $(tmp=$RX)
|
||||||
|
; nextln: regmove $v1, %x10 -> %x11
|
||||||
|
; nextln: regmove $v2, $tmp -> %x10
|
||||||
|
; nextln: return $v2, $v1
|
||||||
|
return v2, v1
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
|
|
||||||
use entity_map::EntityMap;
|
use entity_map::EntityMap;
|
||||||
use dominator_tree::DominatorTree;
|
use dominator_tree::DominatorTree;
|
||||||
use ir::{Ebb, Inst, Value, Function, Cursor, ValueLoc, DataFlowGraph, Signature, ArgumentLoc};
|
use ir::{Ebb, Inst, Value, Function, Cursor, ValueLoc, DataFlowGraph};
|
||||||
use ir::InstBuilder;
|
use ir::{InstBuilder, Signature, ArgumentType, ArgumentLoc};
|
||||||
use isa::{TargetIsa, Encoding, EncInfo, OperandConstraint, ConstraintKind};
|
use isa::{TargetIsa, Encoding, EncInfo, OperandConstraint, ConstraintKind};
|
||||||
use isa::{RegUnit, RegClass, RegInfo, regs_overlap};
|
use isa::{RegUnit, RegClass, RegInfo, regs_overlap};
|
||||||
use regalloc::affinity::Affinity;
|
use regalloc::affinity::Affinity;
|
||||||
@@ -145,7 +145,8 @@ impl<'a> Context<'a> {
|
|||||||
&mut func.dfg,
|
&mut func.dfg,
|
||||||
tracker,
|
tracker,
|
||||||
&mut regs,
|
&mut regs,
|
||||||
&mut func.locations);
|
&mut func.locations,
|
||||||
|
&func.signature);
|
||||||
tracker.drop_dead(inst);
|
tracker.drop_dead(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +308,8 @@ impl<'a> Context<'a> {
|
|||||||
dfg: &mut DataFlowGraph,
|
dfg: &mut DataFlowGraph,
|
||||||
tracker: &mut LiveValueTracker,
|
tracker: &mut LiveValueTracker,
|
||||||
regs: &mut AllocatableSet,
|
regs: &mut AllocatableSet,
|
||||||
locations: &mut EntityMap<Value, ValueLoc>) {
|
locations: &mut EntityMap<Value, ValueLoc>,
|
||||||
|
func_signature: &Signature) {
|
||||||
dbg!("Coloring [{}] {}",
|
dbg!("Coloring [{}] {}",
|
||||||
self.encinfo.display(encoding),
|
self.encinfo.display(encoding),
|
||||||
dfg.display_inst(inst));
|
dfg.display_inst(inst));
|
||||||
@@ -320,6 +322,12 @@ impl<'a> Context<'a> {
|
|||||||
// Program the solver with register constraints for the input side.
|
// Program the solver with register constraints for the input side.
|
||||||
self.solver.reset(regs);
|
self.solver.reset(regs);
|
||||||
self.program_input_constraints(inst, constraints.ins, dfg, locations);
|
self.program_input_constraints(inst, constraints.ins, dfg, locations);
|
||||||
|
let call_sig = dfg.call_signature(inst);
|
||||||
|
if let Some(sig) = call_sig {
|
||||||
|
self.program_input_abi(inst, &dfg.signatures[sig].argument_types, dfg, locations);
|
||||||
|
} else if dfg[inst].opcode().is_return() {
|
||||||
|
self.program_input_abi(inst, &func_signature.return_types, dfg, locations);
|
||||||
|
}
|
||||||
if self.solver.has_fixed_input_conflicts() {
|
if self.solver.has_fixed_input_conflicts() {
|
||||||
self.divert_fixed_input_conflicts(tracker.live(), locations);
|
self.divert_fixed_input_conflicts(tracker.live(), locations);
|
||||||
}
|
}
|
||||||
@@ -342,12 +350,11 @@ impl<'a> Context<'a> {
|
|||||||
// detect conflicts between fixed outputs and tied operands where the input value hasn't
|
// detect conflicts between fixed outputs and tied operands where the input value hasn't
|
||||||
// been converted to a solver variable.
|
// been converted to a solver variable.
|
||||||
if constraints.fixed_outs {
|
if constraints.fixed_outs {
|
||||||
self.program_fixed_output_constraints(inst,
|
self.program_fixed_outputs(constraints.outs, defs, throughs, locations);
|
||||||
constraints.outs,
|
}
|
||||||
defs,
|
if let Some(sig) = call_sig {
|
||||||
throughs,
|
let abi = &dfg.signatures[sig].return_types;
|
||||||
dfg,
|
self.program_output_abi(abi, defs, throughs, locations);
|
||||||
locations);
|
|
||||||
}
|
}
|
||||||
self.program_output_constraints(inst, constraints.outs, defs, dfg, locations);
|
self.program_output_constraints(inst, constraints.outs, defs, dfg, locations);
|
||||||
|
|
||||||
@@ -384,8 +391,8 @@ impl<'a> Context<'a> {
|
|||||||
fn program_input_constraints(&mut self,
|
fn program_input_constraints(&mut self,
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
constraints: &[OperandConstraint],
|
constraints: &[OperandConstraint],
|
||||||
dfg: &mut DataFlowGraph,
|
dfg: &DataFlowGraph,
|
||||||
locations: &mut EntityMap<Value, ValueLoc>) {
|
locations: &EntityMap<Value, ValueLoc>) {
|
||||||
for (op, &value) in constraints
|
for (op, &value) in constraints
|
||||||
.iter()
|
.iter()
|
||||||
.zip(dfg.inst_args(inst))
|
.zip(dfg.inst_args(inst))
|
||||||
@@ -412,6 +419,33 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Program the input-side ABI constraints for `inst` into the constraint solver.
|
||||||
|
///
|
||||||
|
/// ABI constraints are the fixed register assignments used for calls and returns.
|
||||||
|
fn program_input_abi(&mut self,
|
||||||
|
inst: Inst,
|
||||||
|
abi_types: &[ArgumentType],
|
||||||
|
dfg: &DataFlowGraph,
|
||||||
|
locations: &EntityMap<Value, ValueLoc>) {
|
||||||
|
for (abi, &value) in abi_types.iter().zip(dfg.inst_variable_args(inst)) {
|
||||||
|
if let ArgumentLoc::Reg(reg) = abi.location {
|
||||||
|
let cur_reg = self.divert.reg(value, locations);
|
||||||
|
if reg != cur_reg {
|
||||||
|
if let Affinity::Reg(rci) =
|
||||||
|
self.liveness
|
||||||
|
.get(value)
|
||||||
|
.expect("ABI register must have live range")
|
||||||
|
.affinity {
|
||||||
|
let rc = self.reginfo.rc(rci);
|
||||||
|
self.solver.reassign_in(value, rc, cur_reg, reg);
|
||||||
|
} else {
|
||||||
|
panic!("ABI argument {} should be in a register", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find existing live values that conflict with the fixed input register constraints programmed
|
// Find existing live values that conflict with the fixed input register constraints programmed
|
||||||
// into the constraint solver. Convert them to solver variables so they can be diverted.
|
// into the constraint solver. Convert them to solver variables so they can be diverted.
|
||||||
fn divert_fixed_input_conflicts(&mut self,
|
fn divert_fixed_input_conflicts(&mut self,
|
||||||
@@ -431,12 +465,10 @@ impl<'a> Context<'a> {
|
|||||||
/// Program any fixed-register output constraints into the solver. This may also detect
|
/// Program any fixed-register output constraints into the solver. This may also detect
|
||||||
/// conflicts between live-through registers and fixed output registers. These live-through
|
/// conflicts between live-through registers and fixed output registers. These live-through
|
||||||
/// values need to be turned into solver variables so they can be reassigned.
|
/// values need to be turned into solver variables so they can be reassigned.
|
||||||
fn program_fixed_output_constraints(&mut self,
|
fn program_fixed_outputs(&mut self,
|
||||||
_inst: Inst,
|
|
||||||
constraints: &[OperandConstraint],
|
constraints: &[OperandConstraint],
|
||||||
defs: &[LiveValue],
|
defs: &[LiveValue],
|
||||||
throughs: &[LiveValue],
|
throughs: &[LiveValue],
|
||||||
_dfg: &mut DataFlowGraph,
|
|
||||||
locations: &mut EntityMap<Value, ValueLoc>) {
|
locations: &mut EntityMap<Value, ValueLoc>) {
|
||||||
for (op, lv) in constraints.iter().zip(defs) {
|
for (op, lv) in constraints.iter().zip(defs) {
|
||||||
if let ConstraintKind::FixedReg(reg) = op.kind {
|
if let ConstraintKind::FixedReg(reg) = op.kind {
|
||||||
@@ -445,6 +477,30 @@ 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,
|
||||||
|
abi_types: &[ArgumentType],
|
||||||
|
defs: &[LiveValue],
|
||||||
|
throughs: &[LiveValue],
|
||||||
|
locations: &mut EntityMap<Value, ValueLoc>) {
|
||||||
|
// 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.
|
||||||
|
assert_eq!(defs.len(), abi_types.len());
|
||||||
|
for (abi, lv) in abi_types.iter().zip(defs) {
|
||||||
|
if let ArgumentLoc::Reg(reg) = abi.location {
|
||||||
|
if let Affinity::Reg(rci) = lv.affinity {
|
||||||
|
let rc = self.reginfo.rc(rci);
|
||||||
|
self.add_fixed_output(lv.value, rc, reg, throughs, locations);
|
||||||
|
} else {
|
||||||
|
panic!("ABI argument {} should be in a register", lv.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a single fixed output value to the solver.
|
/// Add a single fixed output value to the solver.
|
||||||
fn add_fixed_output(&mut self,
|
fn add_fixed_output(&mut self,
|
||||||
value: Value,
|
value: Value,
|
||||||
|
|||||||
Reference in New Issue
Block a user