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.
This commit is contained in:
Jakob Stoklund Olesen
2017-06-28 14:59:59 -07:00
parent ed3157f508
commit 2a600b3632
5 changed files with 53 additions and 17 deletions

View File

@@ -109,3 +109,15 @@ ebb0(v0: i32):
; check: call_indirect $sig0, $v0($v0, $c) ; check: call_indirect $sig0, $v0($v0, $c)
return 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
}

View File

@@ -155,10 +155,10 @@ impl StackSlots {
} }
/// Create a stack slot representing an incoming function argument. /// 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()); let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes());
assert!(offset <= i32::max_value() as u32 - data.size); assert!(offset <= i32::max_value() - data.size as i32);
data.offset = offset as i32; data.offset = offset;
self.push(data) self.push(data)
} }
} }

View File

@@ -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 /// Return an object that can display this argument location, using the register info from the
/// target ISA. /// target ISA.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayArgumentLoc<'a> { pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayArgumentLoc<'a> {

View File

@@ -253,19 +253,8 @@ impl<'a> Context<'a> {
} }
} }
Affinity::Stack => { // The spiller will have assigned an incoming stack slot already.
if let ArgumentLoc::Stack(_offset) = abi.location { Affinity::Stack => assert!(abi.location.is_stack()),
// 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));
}
}
// This is a ghost value, unused in the function. Don't assign it to a location // This is a ghost value, unused in the function. Don't assign it to a location
// either. // either.
Affinity::None => {} Affinity::None => {}

View File

@@ -17,7 +17,7 @@
use dominator_tree::DominatorTree; use dominator_tree::DominatorTree;
use ir::{DataFlowGraph, Layout, Cursor, InstBuilder}; 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 ir::{InstEncodings, StackSlots, ValueLocations};
use isa::registers::{RegClass, RegClassMask}; use isa::registers::{RegClass, RegClassMask};
use isa::{TargetIsa, RegInfo, EncInfo, RecipeConstraints, ConstraintKind}; use isa::{TargetIsa, RegInfo, EncInfo, RecipeConstraints, ConstraintKind};
@@ -45,6 +45,7 @@ struct Context<'a> {
encodings: &'a mut InstEncodings, encodings: &'a mut InstEncodings,
stack_slots: &'a mut StackSlots, stack_slots: &'a mut StackSlots,
locations: &'a mut ValueLocations, locations: &'a mut ValueLocations,
func_signature: &'a Signature,
// References to contextual data structures we need. // References to contextual data structures we need.
domtree: &'a DominatorTree, domtree: &'a DominatorTree,
@@ -92,6 +93,7 @@ impl Spilling {
encodings: &mut func.encodings, encodings: &mut func.encodings,
stack_slots: &mut func.stack_slots, stack_slots: &mut func.stack_slots,
locations: &mut func.locations, locations: &mut func.locations,
func_signature: &func.signature,
domtree, domtree,
liveness, liveness,
virtregs, virtregs,
@@ -109,12 +111,37 @@ impl<'a> Context<'a> {
layout: &mut Layout, layout: &mut Layout,
dfg: &mut DataFlowGraph, dfg: &mut DataFlowGraph,
tracker: &mut LiveValueTracker) { tracker: &mut LiveValueTracker) {
if let Some(entry) = layout.entry_block() {
self.spill_entry_arguments(entry, dfg);
}
self.topo.reset(layout.ebbs()); self.topo.reset(layout.ebbs());
while let Some(ebb) = self.topo.next(layout, self.domtree) { while let Some(ebb) = self.topo.next(layout, self.domtree) {
self.visit_ebb(ebb, layout, dfg, tracker); 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, fn visit_ebb(&mut self,
ebb: Ebb, ebb: Ebb,
layout: &mut Layout, layout: &mut Layout,