From 2a600b3632d30536b3352406017831061e0af42c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 28 Jun 2017 14:59:59 -0700 Subject: [PATCH] Assign stack slots to incoming function arguments. Function arguments that don't fit in registers are passed on the stack. Create "incoming_arg" stack slots representing the stack arguments, and assign them to the value arguments during spilling. --- filetests/regalloc/spill.cton | 12 +++++++++++ lib/cretonne/src/ir/stackslot.rs | 6 +++--- lib/cretonne/src/ir/valueloc.rs | 8 ++++++++ lib/cretonne/src/regalloc/coloring.rs | 15 ++------------ lib/cretonne/src/regalloc/spilling.rs | 29 ++++++++++++++++++++++++++- 5 files changed, 53 insertions(+), 17 deletions(-) diff --git a/filetests/regalloc/spill.cton b/filetests/regalloc/spill.cton index 23ddf7129b..d30903dc2d 100644 --- a/filetests/regalloc/spill.cton +++ b/filetests/regalloc/spill.cton @@ -109,3 +109,15 @@ ebb0(v0: i32): ; check: call_indirect $sig0, $v0($v0, $c) return } + +; Two arguments on the stack. +function %stackargs(i32, i32, i32, i32, i32, i32, i32, i32) -> i32 { +; check: ss0 = incoming_arg 4 +; check: ss1 = incoming_arg 4, offset 4 +; not: incoming_arg +ebb0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32, v7: i32): + ; unordered: fill $v6 + ; unordered: fill $v7 + v10 = iadd v6, v7 + return v10 +} diff --git a/lib/cretonne/src/ir/stackslot.rs b/lib/cretonne/src/ir/stackslot.rs index baeba2dd23..c6fd64ce5c 100644 --- a/lib/cretonne/src/ir/stackslot.rs +++ b/lib/cretonne/src/ir/stackslot.rs @@ -155,10 +155,10 @@ impl StackSlots { } /// Create a stack slot representing an incoming function argument. - pub fn make_incoming_arg(&mut self, ty: Type, offset: u32) -> StackSlot { + pub fn make_incoming_arg(&mut self, ty: Type, offset: i32) -> StackSlot { let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes()); - assert!(offset <= i32::max_value() as u32 - data.size); - data.offset = offset as i32; + assert!(offset <= i32::max_value() - data.size as i32); + data.offset = offset; self.push(data) } } diff --git a/lib/cretonne/src/ir/valueloc.rs b/lib/cretonne/src/ir/valueloc.rs index a582f1d934..cae3042170 100644 --- a/lib/cretonne/src/ir/valueloc.rs +++ b/lib/cretonne/src/ir/valueloc.rs @@ -112,6 +112,14 @@ impl ArgumentLoc { } } + /// Is this a stack location? + pub fn is_stack(&self) -> bool { + match *self { + ArgumentLoc::Stack(_) => 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 cfb918d832..c2353de40e 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -253,19 +253,8 @@ impl<'a> Context<'a> { } } - Affinity::Stack => { - if let ArgumentLoc::Stack(_offset) = abi.location { - // TODO: Allocate a stack slot at incoming offset and assign it. - panic!("Unimplemented {}: {} stack allocation", - lv.value, - abi.display(&self.reginfo)); - } else { - // This should have been fixed by the reload pass. - panic!("Entry arg {} has stack affinity, but ABI {}", - lv.value, - abi.display(&self.reginfo)); - } - } + // The spiller will have assigned an incoming stack slot already. + Affinity::Stack => assert!(abi.location.is_stack()), // This is a ghost value, unused in the function. Don't assign it to a location // either. Affinity::None => {} diff --git a/lib/cretonne/src/regalloc/spilling.rs b/lib/cretonne/src/regalloc/spilling.rs index 09559c9d26..c64fd565e3 100644 --- a/lib/cretonne/src/regalloc/spilling.rs +++ b/lib/cretonne/src/regalloc/spilling.rs @@ -17,7 +17,7 @@ use dominator_tree::DominatorTree; use ir::{DataFlowGraph, Layout, Cursor, InstBuilder}; -use ir::{Function, Ebb, Inst, Value, ValueLoc, SigRef}; +use ir::{Function, Ebb, Inst, Value, ValueLoc, ArgumentLoc, Signature, SigRef}; use ir::{InstEncodings, StackSlots, ValueLocations}; use isa::registers::{RegClass, RegClassMask}; use isa::{TargetIsa, RegInfo, EncInfo, RecipeConstraints, ConstraintKind}; @@ -45,6 +45,7 @@ struct Context<'a> { encodings: &'a mut InstEncodings, stack_slots: &'a mut StackSlots, locations: &'a mut ValueLocations, + func_signature: &'a Signature, // References to contextual data structures we need. domtree: &'a DominatorTree, @@ -92,6 +93,7 @@ impl Spilling { encodings: &mut func.encodings, stack_slots: &mut func.stack_slots, locations: &mut func.locations, + func_signature: &func.signature, domtree, liveness, virtregs, @@ -109,12 +111,37 @@ impl<'a> Context<'a> { layout: &mut Layout, dfg: &mut DataFlowGraph, tracker: &mut LiveValueTracker) { + if let Some(entry) = layout.entry_block() { + self.spill_entry_arguments(entry, dfg); + } + self.topo.reset(layout.ebbs()); while let Some(ebb) = self.topo.next(layout, self.domtree) { self.visit_ebb(ebb, layout, dfg, tracker); } } + /// Assign stack slots to incoming function arguments on the stack. + fn spill_entry_arguments(&mut self, entry: Ebb, dfg: &DataFlowGraph) { + for (abi, &arg) in self.func_signature + .argument_types + .iter() + .zip(dfg.ebb_args(entry)) { + if let ArgumentLoc::Stack(offset) = abi.location { + // Function arguments passed on the stack can't be part of a virtual register. We + // would need to write other values to the stack slot, but it belongs to the + // caller. (Not that the caller would care, nobody depends on stack arguments being + // preserved across calls). + assert_eq!(self.virtregs.get(arg), + None, + "Stack argument {} can't be part of a virtual register", + arg); + let ss = self.stack_slots.make_incoming_arg(abi.value_type, offset); + *self.locations.ensure(arg) = ValueLoc::Stack(ss); + } + } + } + fn visit_ebb(&mut self, ebb: Ebb, layout: &mut Layout,