Use EncCursor for reload.rs.
Same deal as for spilling. Place an EncCursor in the context and use that to reference into the IR function when necessary.
This commit is contained in:
@@ -709,8 +709,7 @@ pub trait CursorBase {
|
|||||||
fn at_first_inst(mut self, ebb: Ebb) -> Self
|
fn at_first_inst(mut self, ebb: Ebb) -> Self
|
||||||
where Self: Sized
|
where Self: Sized
|
||||||
{
|
{
|
||||||
let inst = self.layout().ebbs[ebb].first_inst.expect("Empty EBB");
|
self.goto_first_inst(ebb);
|
||||||
self.goto_inst(inst);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,6 +739,12 @@ pub trait CursorBase {
|
|||||||
self.set_position(CursorPosition::At(inst));
|
self.set_position(CursorPosition::At(inst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Go to the first instruction in `ebb`.
|
||||||
|
fn goto_first_inst(&mut self, ebb: Ebb) {
|
||||||
|
let inst = self.layout().ebbs[ebb].first_inst.expect("Empty EBB");
|
||||||
|
self.set_position(CursorPosition::At(inst));
|
||||||
|
}
|
||||||
|
|
||||||
/// Go to the top of `ebb` which must be inserted into the layout.
|
/// Go to the top of `ebb` which must be inserted into the layout.
|
||||||
/// At this position, instructions cannot be inserted, but `next_inst()` will move to the first
|
/// At this position, instructions cannot be inserted, but `next_inst()` will move to the first
|
||||||
/// instruction in `ebb`.
|
/// instruction in `ebb`.
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
//! possible to minimize the number of `fill` instructions needed. This must not cause the register
|
//! possible to minimize the number of `fill` instructions needed. This must not cause the register
|
||||||
//! pressure limits to be exceeded.
|
//! pressure limits to be exceeded.
|
||||||
|
|
||||||
|
use cursor::{Cursor, EncCursor};
|
||||||
use dominator_tree::DominatorTree;
|
use dominator_tree::DominatorTree;
|
||||||
use ir::{Ebb, Inst, Value, Function, Signature, DataFlowGraph, InstEncodings};
|
use ir::{Ebb, Inst, Value, Function};
|
||||||
use ir::layout::{Cursor, CursorBase, CursorPosition};
|
use ir::{InstBuilder, ArgumentType, ArgumentLoc};
|
||||||
use ir::{InstBuilder, Opcode, ArgumentType, ArgumentLoc};
|
|
||||||
use isa::RegClass;
|
use isa::RegClass;
|
||||||
use isa::{TargetIsa, Encoding, EncInfo, RecipeConstraints, ConstraintKind};
|
use isa::{TargetIsa, Encoding, EncInfo, RecipeConstraints, ConstraintKind};
|
||||||
use regalloc::affinity::Affinity;
|
use regalloc::affinity::Affinity;
|
||||||
@@ -29,7 +29,8 @@ pub struct Reload {
|
|||||||
|
|
||||||
/// Context data structure that gets instantiated once per pass.
|
/// Context data structure that gets instantiated once per pass.
|
||||||
struct Context<'a> {
|
struct Context<'a> {
|
||||||
isa: &'a TargetIsa,
|
cur: EncCursor<'a>,
|
||||||
|
|
||||||
// Cached ISA information.
|
// Cached ISA information.
|
||||||
// We save it here to avoid frequent virtual function calls on the `TargetIsa` trait object.
|
// We save it here to avoid frequent virtual function calls on the `TargetIsa` trait object.
|
||||||
encinfo: EncInfo,
|
encinfo: EncInfo,
|
||||||
@@ -62,7 +63,7 @@ impl Reload {
|
|||||||
tracker: &mut LiveValueTracker) {
|
tracker: &mut LiveValueTracker) {
|
||||||
dbg!("Reload for:\n{}", func.display(isa));
|
dbg!("Reload for:\n{}", func.display(isa));
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
isa,
|
cur: EncCursor::new(func, isa),
|
||||||
encinfo: isa.encoding_info(),
|
encinfo: isa.encoding_info(),
|
||||||
domtree,
|
domtree,
|
||||||
liveness,
|
liveness,
|
||||||
@@ -70,7 +71,7 @@ impl Reload {
|
|||||||
candidates: &mut self.candidates,
|
candidates: &mut self.candidates,
|
||||||
reloads: &mut self.reloads,
|
reloads: &mut self.reloads,
|
||||||
};
|
};
|
||||||
ctx.run(func, tracker)
|
ctx.run(tracker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,82 +99,63 @@ impl SparseMapValue<Value> for ReloadedValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
fn run(&mut self, func: &mut Function, tracker: &mut LiveValueTracker) {
|
fn run(&mut self, tracker: &mut LiveValueTracker) {
|
||||||
self.topo.reset(func.layout.ebbs());
|
self.topo.reset(self.cur.func.layout.ebbs());
|
||||||
while let Some(ebb) = self.topo.next(&func.layout, self.domtree) {
|
while let Some(ebb) = self.topo.next(&self.cur.func.layout, self.domtree) {
|
||||||
self.visit_ebb(ebb, func, tracker);
|
self.visit_ebb(ebb, tracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_ebb(&mut self, ebb: Ebb, func: &mut Function, tracker: &mut LiveValueTracker) {
|
fn visit_ebb(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) {
|
||||||
dbg!("Reloading {}:", ebb);
|
dbg!("Reloading {}:", ebb);
|
||||||
let start_from = self.visit_ebb_header(ebb, func, tracker);
|
self.visit_ebb_header(ebb, tracker);
|
||||||
tracker.drop_dead_args();
|
tracker.drop_dead_args();
|
||||||
|
|
||||||
let mut pos = Cursor::new(&mut func.layout);
|
// visit_ebb_header() places us at the first interesting instruction in the EBB.
|
||||||
pos.set_position(start_from);
|
while let Some(inst) = self.cur.current_inst() {
|
||||||
while let Some(inst) = pos.current_inst() {
|
let encoding = self.cur.func.encodings[inst];
|
||||||
let encoding = func.encodings[inst];
|
|
||||||
if encoding.is_legal() {
|
if encoding.is_legal() {
|
||||||
self.visit_inst(ebb,
|
self.visit_inst(ebb, inst, encoding, tracker);
|
||||||
inst,
|
|
||||||
encoding,
|
|
||||||
&mut pos,
|
|
||||||
&mut func.dfg,
|
|
||||||
&mut func.encodings,
|
|
||||||
&func.signature,
|
|
||||||
tracker);
|
|
||||||
tracker.drop_dead(inst);
|
tracker.drop_dead(inst);
|
||||||
} else {
|
} else {
|
||||||
pos.next_inst();
|
self.cur.next_inst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process the EBB parameters. Return the next instruction in the EBB to be processed
|
/// Process the EBB parameters. Move to the next instruction in the EBB to be processed
|
||||||
fn visit_ebb_header(&mut self,
|
fn visit_ebb_header(&mut self, ebb: Ebb, tracker: &mut LiveValueTracker) {
|
||||||
ebb: Ebb,
|
let (liveins, args) = tracker.ebb_top(ebb,
|
||||||
func: &mut Function,
|
&self.cur.func.dfg,
|
||||||
tracker: &mut LiveValueTracker)
|
self.liveness,
|
||||||
-> CursorPosition {
|
&self.cur.func.layout,
|
||||||
let (liveins, args) =
|
self.domtree);
|
||||||
tracker.ebb_top(ebb, &func.dfg, self.liveness, &func.layout, self.domtree);
|
|
||||||
|
|
||||||
if func.layout.entry_block() == Some(ebb) {
|
if self.cur.func.layout.entry_block() == Some(ebb) {
|
||||||
assert_eq!(liveins.len(), 0);
|
assert_eq!(liveins.len(), 0);
|
||||||
self.visit_entry_args(ebb, func, args)
|
self.visit_entry_args(ebb, args);
|
||||||
} else {
|
} else {
|
||||||
self.visit_ebb_args(ebb, func, args)
|
self.visit_ebb_args(ebb, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit the arguments to the entry block.
|
/// Visit the arguments to the entry block.
|
||||||
/// These values have ABI constraints from the function signature.
|
/// These values have ABI constraints from the function signature.
|
||||||
fn visit_entry_args(&mut self,
|
fn visit_entry_args(&mut self, ebb: Ebb, args: &[LiveValue]) {
|
||||||
ebb: Ebb,
|
assert_eq!(self.cur.func.signature.argument_types.len(), args.len());
|
||||||
func: &mut Function,
|
self.cur.goto_first_inst(ebb);
|
||||||
args: &[LiveValue])
|
|
||||||
-> CursorPosition {
|
|
||||||
assert_eq!(func.signature.argument_types.len(), args.len());
|
|
||||||
let mut pos = Cursor::new(&mut func.layout);
|
|
||||||
pos.goto_top(ebb);
|
|
||||||
pos.next_inst();
|
|
||||||
|
|
||||||
for (abi, arg) in func.signature.argument_types.iter().zip(args) {
|
for (arg_idx, arg) in args.iter().enumerate() {
|
||||||
|
let abi = self.cur.func.signature.argument_types[arg_idx];
|
||||||
match abi.location {
|
match abi.location {
|
||||||
ArgumentLoc::Reg(_) => {
|
ArgumentLoc::Reg(_) => {
|
||||||
if arg.affinity.is_stack() {
|
if arg.affinity.is_stack() {
|
||||||
// An incoming register parameter was spilled. Replace the parameter value
|
// An incoming register parameter was spilled. Replace the parameter value
|
||||||
// with a temporary register value that is immediately spilled.
|
// with a temporary register value that is immediately spilled.
|
||||||
let reg = func.dfg.replace_ebb_arg(arg.value, abi.value_type);
|
let reg = self.cur.func.dfg.replace_ebb_arg(arg.value, abi.value_type);
|
||||||
let affinity = Affinity::abi(abi, self.isa);
|
let affinity = Affinity::abi(&abi, self.cur.isa);
|
||||||
self.liveness.create_dead(reg, ebb, affinity);
|
self.liveness.create_dead(reg, ebb, affinity);
|
||||||
self.insert_spill(ebb,
|
self.insert_spill(ebb, arg.value, reg);
|
||||||
arg.value,
|
|
||||||
reg,
|
|
||||||
&mut pos,
|
|
||||||
&mut func.encodings,
|
|
||||||
&mut func.dfg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ArgumentLoc::Stack(_) => {
|
ArgumentLoc::Stack(_) => {
|
||||||
@@ -182,14 +164,10 @@ impl<'a> Context<'a> {
|
|||||||
ArgumentLoc::Unassigned => panic!("Unexpected ABI location"),
|
ArgumentLoc::Unassigned => panic!("Unexpected ABI location"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos.position()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_ebb_args(&self, ebb: Ebb, func: &mut Function, _args: &[LiveValue]) -> CursorPosition {
|
fn visit_ebb_args(&mut self, ebb: Ebb, _args: &[LiveValue]) {
|
||||||
let mut pos = Cursor::new(&mut func.layout);
|
self.cur.goto_first_inst(ebb);
|
||||||
pos.goto_top(ebb);
|
|
||||||
pos.next_inst();
|
|
||||||
pos.position()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process the instruction pointed to by `pos`, and advance the cursor to the next instruction
|
/// Process the instruction pointed to by `pos`, and advance the cursor to the next instruction
|
||||||
@@ -198,10 +176,6 @@ impl<'a> Context<'a> {
|
|||||||
ebb: Ebb,
|
ebb: Ebb,
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
encoding: Encoding,
|
encoding: Encoding,
|
||||||
pos: &mut Cursor,
|
|
||||||
dfg: &mut DataFlowGraph,
|
|
||||||
encodings: &mut InstEncodings,
|
|
||||||
func_signature: &Signature,
|
|
||||||
tracker: &mut LiveValueTracker) {
|
tracker: &mut LiveValueTracker) {
|
||||||
// Get the operand constraints for `inst` that we are trying to satisfy.
|
// Get the operand constraints for `inst` that we are trying to satisfy.
|
||||||
let constraints = self.encinfo
|
let constraints = self.encinfo
|
||||||
@@ -210,7 +184,7 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Identify reload candidates.
|
// Identify reload candidates.
|
||||||
assert!(self.candidates.is_empty());
|
assert!(self.candidates.is_empty());
|
||||||
self.find_candidates(inst, constraints, func_signature, dfg);
|
self.find_candidates(inst, constraints);
|
||||||
|
|
||||||
// Insert fill instructions before `inst`.
|
// Insert fill instructions before `inst`.
|
||||||
while let Some(cand) = self.candidates.pop() {
|
while let Some(cand) = self.candidates.pop() {
|
||||||
@@ -218,12 +192,8 @@ impl<'a> Context<'a> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg = dfg.ins(pos).fill(cand.value);
|
let reg = self.cur.ins().fill(cand.value);
|
||||||
let fill = dfg.value_def(reg).unwrap_inst();
|
let fill = self.cur.built_inst();
|
||||||
match self.isa.encode(dfg, &dfg[fill], dfg.value_type(reg)) {
|
|
||||||
Ok(e) => *encodings.ensure(fill) = e,
|
|
||||||
Err(_) => panic!("Can't encode fill {}", cand.value),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.reloads
|
self.reloads
|
||||||
.insert(ReloadedValue {
|
.insert(ReloadedValue {
|
||||||
@@ -233,12 +203,13 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Create a live range for the new reload.
|
// Create a live range for the new reload.
|
||||||
let affinity = Affinity::Reg(cand.regclass.into());
|
let affinity = Affinity::Reg(cand.regclass.into());
|
||||||
self.liveness.create_dead(reg, dfg.value_def(reg), affinity);
|
self.liveness.create_dead(reg, fill, affinity);
|
||||||
self.liveness.extend_locally(reg, ebb, inst, pos.layout);
|
self.liveness
|
||||||
|
.extend_locally(reg, ebb, inst, &self.cur.func.layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite arguments.
|
// Rewrite arguments.
|
||||||
for arg in dfg.inst_args_mut(inst) {
|
for arg in self.cur.func.dfg.inst_args_mut(inst) {
|
||||||
if let Some(reload) = self.reloads.get(*arg) {
|
if let Some(reload) = self.reloads.get(*arg) {
|
||||||
*arg = reload.reg;
|
*arg = reload.reg;
|
||||||
}
|
}
|
||||||
@@ -247,10 +218,11 @@ impl<'a> Context<'a> {
|
|||||||
// TODO: Reuse reloads for future instructions.
|
// TODO: Reuse reloads for future instructions.
|
||||||
self.reloads.clear();
|
self.reloads.clear();
|
||||||
|
|
||||||
let (_throughs, _kills, defs) = tracker.process_inst(inst, dfg, self.liveness);
|
let (_throughs, _kills, defs) = tracker
|
||||||
|
.process_inst(inst, &self.cur.func.dfg, self.liveness);
|
||||||
|
|
||||||
// Advance to the next instruction so we can insert any spills after the instruction.
|
// Advance to the next instruction so we can insert any spills after the instruction.
|
||||||
pos.next_inst();
|
self.cur.next_inst();
|
||||||
|
|
||||||
// Rewrite register defs that need to be spilled.
|
// Rewrite register defs that need to be spilled.
|
||||||
//
|
//
|
||||||
@@ -266,10 +238,10 @@ impl<'a> Context<'a> {
|
|||||||
// That way, we don't need to rewrite all future uses of v2.
|
// That way, we don't need to rewrite all future uses of v2.
|
||||||
for (lv, op) in defs.iter().zip(constraints.outs) {
|
for (lv, op) in defs.iter().zip(constraints.outs) {
|
||||||
if lv.affinity.is_stack() && op.kind != ConstraintKind::Stack {
|
if lv.affinity.is_stack() && op.kind != ConstraintKind::Stack {
|
||||||
let value_type = dfg.value_type(lv.value);
|
let value_type = self.cur.func.dfg.value_type(lv.value);
|
||||||
let reg = dfg.replace_result(lv.value, value_type);
|
let reg = self.cur.func.dfg.replace_result(lv.value, value_type);
|
||||||
self.liveness.create_dead(reg, inst, Affinity::new(op));
|
self.liveness.create_dead(reg, inst, Affinity::new(op));
|
||||||
self.insert_spill(ebb, lv.value, reg, pos, encodings, dfg);
|
self.insert_spill(ebb, lv.value, reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,12 +249,8 @@ impl<'a> Context<'a> {
|
|||||||
// Find reload candidates for `inst` and add them to `self.condidates`.
|
// Find reload candidates for `inst` and add them to `self.condidates`.
|
||||||
//
|
//
|
||||||
// These are uses of spilled values where the operand constraint requires a register.
|
// These are uses of spilled values where the operand constraint requires a register.
|
||||||
fn find_candidates(&mut self,
|
fn find_candidates(&mut self, inst: Inst, constraints: &RecipeConstraints) {
|
||||||
inst: Inst,
|
let args = self.cur.func.dfg.inst_args(inst);
|
||||||
constraints: &RecipeConstraints,
|
|
||||||
func_signature: &Signature,
|
|
||||||
dfg: &DataFlowGraph) {
|
|
||||||
let args = dfg.inst_args(inst);
|
|
||||||
|
|
||||||
for (op, &arg) in constraints.ins.iter().zip(args) {
|
for (op, &arg) in constraints.ins.iter().zip(args) {
|
||||||
if op.kind != ConstraintKind::Stack {
|
if op.kind != ConstraintKind::Stack {
|
||||||
@@ -303,30 +271,18 @@ impl<'a> Context<'a> {
|
|||||||
let var_args = &args[constraints.ins.len()..];
|
let var_args = &args[constraints.ins.len()..];
|
||||||
|
|
||||||
// Handle ABI arguments.
|
// Handle ABI arguments.
|
||||||
if let Some(sig) = dfg.call_signature(inst) {
|
if let Some(sig) = self.cur.func.dfg.call_signature(inst) {
|
||||||
self.handle_abi_args(&dfg.signatures[sig].argument_types, var_args);
|
handle_abi_args(self.candidates,
|
||||||
} else if dfg[inst].opcode().is_return() {
|
&self.cur.func.dfg.signatures[sig].argument_types,
|
||||||
self.handle_abi_args(&func_signature.return_types, var_args);
|
var_args,
|
||||||
}
|
self.cur.isa,
|
||||||
}
|
self.liveness);
|
||||||
|
} else if self.cur.func.dfg[inst].opcode().is_return() {
|
||||||
/// Find reload candidates in the instruction's ABI variable arguments. This handles both
|
handle_abi_args(self.candidates,
|
||||||
/// return values and call arguments.
|
&self.cur.func.signature.return_types,
|
||||||
fn handle_abi_args(&mut self, abi_types: &[ArgumentType], var_args: &[Value]) {
|
var_args,
|
||||||
assert_eq!(abi_types.len(), var_args.len());
|
self.cur.isa,
|
||||||
for (abi, &arg) in abi_types.iter().zip(var_args) {
|
self.liveness);
|
||||||
if abi.location.is_reg() {
|
|
||||||
let lv = self.liveness
|
|
||||||
.get(arg)
|
|
||||||
.expect("Missing live range for ABI arg");
|
|
||||||
if lv.affinity.is_stack() {
|
|
||||||
self.candidates
|
|
||||||
.push(ReloadCandidate {
|
|
||||||
value: arg,
|
|
||||||
regclass: self.isa.regclass_for_abi_type(abi.value_type),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,30 +291,34 @@ impl<'a> Context<'a> {
|
|||||||
/// - Insert `stack = spill reg` at `pos`, and assign an encoding.
|
/// - Insert `stack = spill reg` at `pos`, and assign an encoding.
|
||||||
/// - Move the `stack` live range starting point to the new instruction.
|
/// - Move the `stack` live range starting point to the new instruction.
|
||||||
/// - Extend the `reg` live range to reach the new instruction.
|
/// - Extend the `reg` live range to reach the new instruction.
|
||||||
fn insert_spill(&mut self,
|
fn insert_spill(&mut self, ebb: Ebb, stack: Value, reg: Value) {
|
||||||
ebb: Ebb,
|
self.cur.ins().with_result(stack).spill(reg);
|
||||||
stack: Value,
|
let inst = self.cur.built_inst();
|
||||||
reg: Value,
|
|
||||||
pos: &mut Cursor,
|
|
||||||
encodings: &mut InstEncodings,
|
|
||||||
dfg: &mut DataFlowGraph) {
|
|
||||||
let ty = dfg.value_type(reg);
|
|
||||||
|
|
||||||
// Insert spill instruction. Use the low-level `Unary` constructor because it returns an
|
|
||||||
// instruction reference directly rather than a result value (which we know is equal to
|
|
||||||
// `stack`).
|
|
||||||
let (inst, _) = dfg.ins(pos)
|
|
||||||
.with_result(stack)
|
|
||||||
.Unary(Opcode::Spill, ty, reg);
|
|
||||||
|
|
||||||
// Give it an encoding.
|
|
||||||
match self.isa.encode(dfg, &dfg[inst], ty) {
|
|
||||||
Ok(e) => *encodings.ensure(inst) = e,
|
|
||||||
Err(_) => panic!("Can't encode spill.{}", ty),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update live ranges.
|
// Update live ranges.
|
||||||
self.liveness.move_def_locally(stack, inst);
|
self.liveness.move_def_locally(stack, inst);
|
||||||
self.liveness.extend_locally(reg, ebb, inst, pos.layout);
|
self.liveness
|
||||||
|
.extend_locally(reg, ebb, inst, &self.cur.func.layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find reload candidates in the instruction's ABI variable arguments. This handles both
|
||||||
|
/// return values and call arguments.
|
||||||
|
fn handle_abi_args(candidates: &mut Vec<ReloadCandidate>,
|
||||||
|
abi_types: &[ArgumentType],
|
||||||
|
var_args: &[Value],
|
||||||
|
isa: &TargetIsa,
|
||||||
|
liveness: &Liveness) {
|
||||||
|
assert_eq!(abi_types.len(), var_args.len());
|
||||||
|
for (abi, &arg) in abi_types.iter().zip(var_args) {
|
||||||
|
if abi.location.is_reg() {
|
||||||
|
let lv = liveness.get(arg).expect("Missing live range for ABI arg");
|
||||||
|
if lv.affinity.is_stack() {
|
||||||
|
candidates.push(ReloadCandidate {
|
||||||
|
value: arg,
|
||||||
|
regclass: isa.regclass_for_abi_type(abi.value_type),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user