Add an offset to StackSlotData.

The offset is relative to the stack pointer in the calling function, so
it excludes the return address pushed by the call instruction itself on
Intel ISAs.

Change the ArgumentLoc::Stack offset to an i32, so it matches the stack
slot offsets.
This commit is contained in:
Jakob Stoklund Olesen
2017-06-28 14:05:11 -07:00
parent 1d20c92ffe
commit ed3157f508
7 changed files with 54 additions and 17 deletions

View File

@@ -98,7 +98,7 @@ ebb0(v90: i32, v91: f32):
function %stack() { function %stack() {
ss10 = spill_slot 8 ss10 = spill_slot 8
ss2 = local 4 ss2 = local 4
ss3 = incoming_arg 4 ss3 = incoming_arg 4, offset 8
ss4 = outgoing_arg 4 ss4 = outgoing_arg 4
ebb0: ebb0:
@@ -110,7 +110,7 @@ ebb0:
; sameln: function %stack() { ; sameln: function %stack() {
; nextln: $ss10 = spill_slot 8 ; nextln: $ss10 = spill_slot 8
; nextln: $ss2 = local 4 ; nextln: $ss2 = local 4
; nextln: $ss3 = incoming_arg 4 ; nextln: $ss3 = incoming_arg 4, offset 8
; nextln: $ss4 = outgoing_arg 4 ; nextln: $ss4 = outgoing_arg 4
; check: ebb0: ; check: ebb0:

View File

@@ -51,8 +51,8 @@ impl Signature {
let bytes = self.argument_types let bytes = self.argument_types
.iter() .iter()
.filter_map(|arg| match arg.location { .filter_map(|arg| match arg.location {
ArgumentLoc::Stack(offset) => { ArgumentLoc::Stack(offset) if offset > 0 => {
Some(offset + arg.value_type.bits() as u32 / 8) Some(offset as u32 + arg.value_type.bytes())
} }
_ => None, _ => None,
}) })

View File

@@ -69,18 +69,33 @@ pub struct StackSlotData {
/// Size of stack slot in bytes. /// Size of stack slot in bytes.
pub size: u32, pub size: u32,
/// Offset of stack slot relative to the stack pointer in the caller.
///
/// On Intel ISAs, the base address is the stack pointer *before* the return address was
/// pushed. On RISC ISAs, the base address is the value of the stack pointer on entry to the
/// function.
pub offset: i32,
} }
impl StackSlotData { impl StackSlotData {
/// Create a stack slot with the specified byte size. /// Create a stack slot with the specified byte size.
pub fn new(kind: StackSlotKind, size: u32) -> StackSlotData { pub fn new(kind: StackSlotKind, size: u32) -> StackSlotData {
StackSlotData { kind, size } StackSlotData {
kind,
size,
offset: 0,
}
} }
} }
impl fmt::Display for StackSlotData { impl fmt::Display for StackSlotData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.kind, self.size) write!(f, "{} {}", self.kind, self.size)?;
if self.offset != 0 {
write!(f, ", offset {}", self.offset)?;
}
Ok(())
} }
} }
@@ -136,8 +151,15 @@ impl StackSlots {
impl StackSlots { impl StackSlots {
/// Create a new spill slot for spilling values of type `ty`. /// Create a new spill slot for spilling values of type `ty`.
pub fn make_spill_slot(&mut self, ty: Type) -> StackSlot { pub fn make_spill_slot(&mut self, ty: Type) -> StackSlot {
let bytes = (ty.bits() as u32 + 7) / 8; self.push(StackSlotData::new(StackSlotKind::SpillSlot, ty.bytes()))
self.push(StackSlotData::new(StackSlotKind::SpillSlot, bytes)) }
/// Create a stack slot representing an incoming function argument.
pub fn make_incoming_arg(&mut self, ty: Type, offset: u32) -> StackSlot {
let mut data = StackSlotData::new(StackSlotKind::IncomingArg, ty.bytes());
assert!(offset <= i32::max_value() as u32 - data.size);
data.offset = offset as i32;
self.push(data)
} }
} }

View File

@@ -197,6 +197,11 @@ impl Type {
self.lane_bits() as u16 * self.lane_count() self.lane_bits() as u16 * self.lane_count()
} }
/// Get the number of bytes used to store this type in memory.
pub fn bytes(self) -> u32 {
(self.bits() as u32 + 7) / 8
}
/// Get a SIMD vector type with `n` times more lanes than this one. /// Get a SIMD vector type with `n` times more lanes than this one.
/// ///
/// If this is a scalar type, this produces a SIMD type with this as a lane type and `n` lanes. /// If this is a scalar type, this produces a SIMD type with this as a lane type and `n` lanes.

View File

@@ -86,7 +86,7 @@ pub enum ArgumentLoc {
/// Argument is passed in a register. /// Argument is passed in a register.
Reg(RegUnit), Reg(RegUnit),
/// Argument is passed on the stack, at the given byte offset into the argument array. /// Argument is passed on the stack, at the given byte offset into the argument array.
Stack(u32), Stack(i32),
} }
impl Default for ArgumentLoc { impl Default for ArgumentLoc {

View File

@@ -77,8 +77,9 @@ impl ArgAssigner for Args {
ArgumentLoc::Reg(reg).into() ArgumentLoc::Reg(reg).into()
} else { } else {
// Assign a stack location. // Assign a stack location.
let loc = ArgumentLoc::Stack(self.offset); let loc = ArgumentLoc::Stack(self.offset as i32);
self.offset += self.pointer_bytes; self.offset += self.pointer_bytes;
assert!(self.offset <= i32::max_value() as u32);
loc.into() loc.into()
} }
} }

View File

@@ -428,15 +428,15 @@ impl<'a> Parser<'a> {
} }
} }
// Match and consume a u32 immediate. // Match and consume an i32 immediate.
// This is used for stack argument byte offsets. // This is used for stack argument byte offsets.
fn match_uimm32(&mut self, err_msg: &str) -> Result<u32> { fn match_imm32(&mut self, err_msg: &str) -> Result<i32> {
if let Some(Token::Integer(text)) = self.token() { if let Some(Token::Integer(text)) = self.token() {
self.consume(); self.consume();
// Lexer just gives us raw text that looks like an integer. // Lexer just gives us raw text that looks like an integer.
// Parse it as a u32 to check for overflow and other issues. // Parse it as a i32 to check for overflow and other issues.
text.parse() text.parse()
.map_err(|_| self.error("expected u32 decimal immediate")) .map_err(|_| self.error("expected i32 decimal immediate"))
} else { } else {
err!(self.loc, err_msg) err!(self.loc, err_msg)
} }
@@ -837,7 +837,7 @@ impl<'a> Parser<'a> {
} }
} }
Some(Token::Integer(_)) => { Some(Token::Integer(_)) => {
let offset = self.match_uimm32("expected stack argument byte offset")?; let offset = self.match_imm32("expected stack argument byte offset")?;
Ok(ArgumentLoc::Stack(offset)) Ok(ArgumentLoc::Stack(offset))
} }
Some(Token::Minus) => { Some(Token::Minus) => {
@@ -870,8 +870,9 @@ impl<'a> Parser<'a> {
match self.token() { match self.token() {
Some(Token::StackSlot(..)) => { Some(Token::StackSlot(..)) => {
self.gather_comments(ctx.function.stack_slots.next_key()); self.gather_comments(ctx.function.stack_slots.next_key());
let loc = self.loc;
self.parse_stack_slot_decl() self.parse_stack_slot_decl()
.and_then(|(num, dat)| ctx.add_ss(num, dat, &self.loc)) .and_then(|(num, dat)| ctx.add_ss(num, dat, &loc))
} }
Some(Token::SigRef(..)) => { Some(Token::SigRef(..)) => {
self.gather_comments(ctx.function.dfg.signatures.next_key()); self.gather_comments(ctx.function.dfg.signatures.next_key());
@@ -915,7 +916,15 @@ impl<'a> Parser<'a> {
if bytes > u32::MAX as i64 { if bytes > u32::MAX as i64 {
return err!(self.loc, "stack slot too large"); return err!(self.loc, "stack slot too large");
} }
let data = StackSlotData::new(kind, bytes as u32); let mut data = StackSlotData::new(kind, bytes as u32);
// Take additional options.
while self.optional(Token::Comma) {
match self.match_any_identifier("expected stack slot flags")? {
"offset" => data.offset = self.match_imm32("expected byte offset")?,
other => return err!(self.loc, "Unknown stack slot flag '{}'", other),
}
}
// TBD: stack-slot-decl ::= StackSlot(ss) "=" stack-slot-kind Bytes * {"," stack-slot-flag} // TBD: stack-slot-decl ::= StackSlot(ss) "=" stack-slot-kind Bytes * {"," stack-slot-flag}
Ok((number, data)) Ok((number, data))