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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Option<&'a RegInfo>>>(self, regs: R) -> DisplayArgumentLoc<'a> {
|
||||
|
||||
@@ -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 => {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user