From 6381da948fb37f2c8202fe60d576d6ff5279a46f Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 13 Jun 2017 15:13:36 -0700 Subject: [PATCH] Handle ABI arguments correctly in the reload pass. Values passed as arguments to calls and return instructions may also be reload candidates. --- filetests/regalloc/spill.cton | 15 ++++- lib/cretonne/src/ir/valueloc.rs | 8 +++ lib/cretonne/src/regalloc/coloring.rs | 1 + lib/cretonne/src/regalloc/reload.rs | 82 +++++++++++++++++++++------ 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/filetests/regalloc/spill.cton b/filetests/regalloc/spill.cton index a6565b987e..d3da74ddf0 100644 --- a/filetests/regalloc/spill.cton +++ b/filetests/regalloc/spill.cton @@ -14,10 +14,15 @@ test regalloc isa riscv enable_e ; In straight-line code, the first value defined is spilled. -; That is the argument. +; That is in order: +; 1. The argument v1. +; 2. The link register. function %pyramid(i32) -> i32 { ebb0(v1: i32): -; check: $v1 = spill $(rv1=$V) +; check: $ebb0($(rv1=$V): i32, $(rlink=$V): i32) + ; check: $v1 = spill $rv1 + ; nextln: $(link=$V) = spill $rlink + ; not: spill v2 = iadd_imm v1, 12 v3 = iadd_imm v2, 12 v4 = iadd_imm v3, 12 @@ -29,7 +34,9 @@ ebb0(v1: i32): v10 = iadd_imm v9, 12 v11 = iadd_imm v10, 12 v12 = iadd_imm v11, 12 - v31 = iadd v11, v12 + v13 = iadd_imm v12, 12 + v32 = iadd v12, v13 + v31 = iadd v32, v11 v30 = iadd v31, v10 v29 = iadd v30, v9 v28 = iadd v29, v8 @@ -40,5 +47,7 @@ ebb0(v1: i32): v23 = iadd v24, v3 v22 = iadd v23, v2 v21 = iadd v22, v1 + ; check: $(rlink2=$V) = fill $link return v21 + ; check: return $v21, $rlink2 } diff --git a/lib/cretonne/src/ir/valueloc.rs b/lib/cretonne/src/ir/valueloc.rs index 2d6fc644b1..b664fe70e5 100644 --- a/lib/cretonne/src/ir/valueloc.rs +++ b/lib/cretonne/src/ir/valueloc.rs @@ -104,6 +104,14 @@ impl ArgumentLoc { } } + /// Is this a register location? + pub fn is_reg(&self) -> bool { + match self { + &ArgumentLoc::Reg(_) => true, + _ => false, + } + } + /// Return an object that can display this argument location, using the register info from the /// target ISA. pub fn display<'a, R: Into>>(self, regs: R) -> DisplayArgumentLoc<'a> { diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 33013aa309..8fd602fe39 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -101,6 +101,7 @@ impl Coloring { liveness: &mut Liveness, topo: &mut TopoOrder, tracker: &mut LiveValueTracker) { + dbg!("Coloring for:\n{}", func.display(isa)); let mut ctx = Context { reginfo: isa.register_info(), encinfo: isa.encoding_info(), diff --git a/lib/cretonne/src/regalloc/reload.rs b/lib/cretonne/src/regalloc/reload.rs index 1342625380..9786f45b67 100644 --- a/lib/cretonne/src/regalloc/reload.rs +++ b/lib/cretonne/src/regalloc/reload.rs @@ -11,11 +11,11 @@ use dominator_tree::DominatorTree; use entity_map::EntityMap; -use ir::{Ebb, Inst, Value, Function, DataFlowGraph}; +use ir::{Ebb, Inst, Value, Function, Signature, DataFlowGraph}; use ir::layout::{Cursor, CursorPosition}; -use ir::{InstBuilder, ArgumentLoc}; +use ir::{InstBuilder, ArgumentType, ArgumentLoc}; use isa::RegClass; -use isa::{TargetIsa, Encoding, EncInfo, ConstraintKind}; +use isa::{TargetIsa, Encoding, EncInfo, RecipeConstraints, ConstraintKind}; use regalloc::affinity::Affinity; use regalloc::live_value_tracker::{LiveValue, LiveValueTracker}; use regalloc::liveness::Liveness; @@ -61,6 +61,7 @@ impl Reload { liveness: &mut Liveness, topo: &mut TopoOrder, tracker: &mut LiveValueTracker) { + dbg!("Reload for:\n{}", func.display(isa)); let mut ctx = Context { isa, encinfo: isa.encoding_info(), @@ -121,6 +122,7 @@ impl<'a> Context<'a> { &mut pos, &mut func.dfg, &mut func.encodings, + &func.signature, tracker); tracker.drop_dead(inst); } else { @@ -202,27 +204,16 @@ impl<'a> Context<'a> { pos: &mut Cursor, dfg: &mut DataFlowGraph, encodings: &mut EntityMap, + func_signature: &Signature, tracker: &mut LiveValueTracker) { // Get the operand constraints for `inst` that we are trying to satisfy. let constraints = self.encinfo .operand_constraints(encoding) .expect("Missing instruction encoding"); - assert!(self.candidates.is_empty()); - // Identify reload candidates. - for (op, &arg) in constraints.ins.iter().zip(dfg.inst_args(inst)) { - if op.kind != ConstraintKind::Stack { - let lv = self.liveness.get(arg).expect("Missing live range for arg"); - if lv.affinity.is_stack() { - self.candidates - .push(ReloadCandidate { - value: arg, - regclass: op.regclass, - }) - } - } - } + assert!(self.candidates.is_empty()); + self.find_candidates(inst, constraints, func_signature, dfg); // Insert fill instructions before `inst`. while let Some(cand) = self.candidates.pop() { @@ -289,4 +280,61 @@ impl<'a> Context<'a> { } } } + + // Find reload candidates for `inst` and add them to `self.condidates`. + // + // These are uses of spilled values where the operand constraint requires a register. + fn find_candidates(&mut self, + inst: Inst, + constraints: &RecipeConstraints, + func_signature: &Signature, + dfg: &DataFlowGraph) { + let args = dfg.inst_args(inst); + + for (op, &arg) in constraints.ins.iter().zip(args) { + if op.kind != ConstraintKind::Stack { + let lv = self.liveness.get(arg).expect("Missing live range for arg"); + if lv.affinity.is_stack() { + self.candidates + .push(ReloadCandidate { + value: arg, + regclass: op.regclass, + }) + } + } + } + + // If we only have the fixed arguments, we're done now. + if args.len() == constraints.ins.len() { + return; + } + let var_args = &args[constraints.ins.len()..]; + + // Handle ABI arguments. + if let Some(sig) = dfg.call_signature(inst) { + self.handle_abi_args(&dfg.signatures[sig].argument_types, var_args); + } else if dfg[inst].opcode().is_return() { + self.handle_abi_args(&func_signature.return_types, var_args); + } + } + + /// Find reload candidates in the instruction's ABI variable arguments. This handles both + /// return values and call arguments. + fn handle_abi_args(&mut self, abi_types: &[ArgumentType], var_args: &[Value]) { + 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 = 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), + }); + } + } + } + } }