diff --git a/filetests/parser/tiny.cton b/filetests/parser/tiny.cton index 704477e6c2..ff3d386947 100644 --- a/filetests/parser/tiny.cton +++ b/filetests/parser/tiny.cton @@ -98,7 +98,7 @@ ebb0(v90: i32, v91: f32): function %stack() { ss10 = spill_slot 8 ss2 = local 4 - ss3 = incoming_arg 4 + ss3 = incoming_arg 4, offset 8 ss4 = outgoing_arg 4 ebb0: @@ -110,7 +110,7 @@ ebb0: ; sameln: function %stack() { ; nextln: $ss10 = spill_slot 8 ; nextln: $ss2 = local 4 -; nextln: $ss3 = incoming_arg 4 +; nextln: $ss3 = incoming_arg 4, offset 8 ; nextln: $ss4 = outgoing_arg 4 ; check: ebb0: diff --git a/lib/cretonne/src/ir/extfunc.rs b/lib/cretonne/src/ir/extfunc.rs index 57346df5f2..e46ef39a54 100644 --- a/lib/cretonne/src/ir/extfunc.rs +++ b/lib/cretonne/src/ir/extfunc.rs @@ -51,8 +51,8 @@ impl Signature { let bytes = self.argument_types .iter() .filter_map(|arg| match arg.location { - ArgumentLoc::Stack(offset) => { - Some(offset + arg.value_type.bits() as u32 / 8) + ArgumentLoc::Stack(offset) if offset > 0 => { + Some(offset as u32 + arg.value_type.bytes()) } _ => None, }) diff --git a/lib/cretonne/src/ir/stackslot.rs b/lib/cretonne/src/ir/stackslot.rs index a1a5f0cb94..baeba2dd23 100644 --- a/lib/cretonne/src/ir/stackslot.rs +++ b/lib/cretonne/src/ir/stackslot.rs @@ -69,18 +69,33 @@ pub struct StackSlotData { /// Size of stack slot in bytes. 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 { /// Create a stack slot with the specified byte size. pub fn new(kind: StackSlotKind, size: u32) -> StackSlotData { - StackSlotData { kind, size } + StackSlotData { + kind, + size, + offset: 0, + } } } impl fmt::Display for StackSlotData { 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 { /// Create a new spill slot for spilling values of type `ty`. pub fn make_spill_slot(&mut self, ty: Type) -> StackSlot { - let bytes = (ty.bits() as u32 + 7) / 8; - self.push(StackSlotData::new(StackSlotKind::SpillSlot, bytes)) + self.push(StackSlotData::new(StackSlotKind::SpillSlot, ty.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) } } diff --git a/lib/cretonne/src/ir/types.rs b/lib/cretonne/src/ir/types.rs index 550f25804b..e6379be2e6 100644 --- a/lib/cretonne/src/ir/types.rs +++ b/lib/cretonne/src/ir/types.rs @@ -197,6 +197,11 @@ impl Type { 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. /// /// If this is a scalar type, this produces a SIMD type with this as a lane type and `n` lanes. diff --git a/lib/cretonne/src/ir/valueloc.rs b/lib/cretonne/src/ir/valueloc.rs index 83dbcd0010..a582f1d934 100644 --- a/lib/cretonne/src/ir/valueloc.rs +++ b/lib/cretonne/src/ir/valueloc.rs @@ -86,7 +86,7 @@ pub enum ArgumentLoc { /// Argument is passed in a register. Reg(RegUnit), /// Argument is passed on the stack, at the given byte offset into the argument array. - Stack(u32), + Stack(i32), } impl Default for ArgumentLoc { diff --git a/lib/cretonne/src/isa/riscv/abi.rs b/lib/cretonne/src/isa/riscv/abi.rs index ab9e485ed5..66d4c2e6b7 100644 --- a/lib/cretonne/src/isa/riscv/abi.rs +++ b/lib/cretonne/src/isa/riscv/abi.rs @@ -77,8 +77,9 @@ impl ArgAssigner for Args { ArgumentLoc::Reg(reg).into() } else { // Assign a stack location. - let loc = ArgumentLoc::Stack(self.offset); + let loc = ArgumentLoc::Stack(self.offset as i32); self.offset += self.pointer_bytes; + assert!(self.offset <= i32::max_value() as u32); loc.into() } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index ae88e09873..f698b1f54f 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -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. - fn match_uimm32(&mut self, err_msg: &str) -> Result { + fn match_imm32(&mut self, err_msg: &str) -> Result { if let Some(Token::Integer(text)) = self.token() { self.consume(); // 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() - .map_err(|_| self.error("expected u32 decimal immediate")) + .map_err(|_| self.error("expected i32 decimal immediate")) } else { err!(self.loc, err_msg) } @@ -837,7 +837,7 @@ impl<'a> Parser<'a> { } } 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)) } Some(Token::Minus) => { @@ -870,8 +870,9 @@ impl<'a> Parser<'a> { match self.token() { Some(Token::StackSlot(..)) => { self.gather_comments(ctx.function.stack_slots.next_key()); + let loc = self.loc; 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(..)) => { self.gather_comments(ctx.function.dfg.signatures.next_key()); @@ -915,7 +916,15 @@ impl<'a> Parser<'a> { if bytes > u32::MAX as i64 { 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} Ok((number, data))