diff --git a/docs/example.cton b/docs/example.cton index 8b9c43c6b9..493794da50 100644 --- a/docs/example.cton +++ b/docs/example.cton @@ -1,7 +1,7 @@ test verifier function %average(i32, i32) -> f32 { - ss1 = stack_slot 8 ; Stack slot for ``sum``. + ss1 = local 8 ; Stack slot for ``sum``. ebb1(v1: i32, v2: i32): v3 = f64const 0x0.0 diff --git a/docs/langref.rst b/docs/langref.rst index 24b5961ec3..ac20591c36 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -465,9 +465,9 @@ frame. The stack frame is divided into fixed-size stack slots that are allocated in the :term:`function preamble`. Stack slots are not typed, they simply represent a contiguous sequence of bytes in the stack frame. -.. inst:: SS = stack_slot Bytes, Flags... +.. inst:: SS = local Bytes, Flags... - Allocate a stack slot in the preamble. + Allocate a stack slot for a local variable in the preamble. If no alignment is specified, Cretonne will pick an appropriate alignment for the stack slot based on its size and access patterns. diff --git a/filetests/parser/tiny.cton b/filetests/parser/tiny.cton index 1414668a52..704477e6c2 100644 --- a/filetests/parser/tiny.cton +++ b/filetests/parser/tiny.cton @@ -96,8 +96,10 @@ ebb0(v90: i32, v91: f32): ; Stack slot references function %stack() { - ss10 = stack_slot 8 - ss2 = stack_slot 4 + ss10 = spill_slot 8 + ss2 = local 4 + ss3 = incoming_arg 4 + ss4 = outgoing_arg 4 ebb0: v1 = stack_load.i32 ss10 @@ -106,8 +108,10 @@ ebb0: stack_store v2, ss2 } ; sameln: function %stack() { -; nextln: $ss10 = stack_slot 8 -; nextln: $ss2 = stack_slot 4 +; nextln: $ss10 = spill_slot 8 +; nextln: $ss2 = local 4 +; nextln: $ss3 = incoming_arg 4 +; nextln: $ss4 = outgoing_arg 4 ; check: ebb0: ; nextln: $v1 = stack_load.i32 $ss10 diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index 49617165e9..56c972c429 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -22,7 +22,7 @@ pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ArgumentPurpos pub use ir::types::Type; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; -pub use ir::stackslot::StackSlotData; +pub use ir::stackslot::{StackSlotKind, StackSlotData}; pub use ir::jumptable::JumpTableData; pub use ir::valueloc::{ValueLoc, ArgumentLoc}; pub use ir::dfg::{DataFlowGraph, ValueDef}; diff --git a/lib/cretonne/src/ir/stackslot.rs b/lib/cretonne/src/ir/stackslot.rs index b22b82919c..a205f6d410 100644 --- a/lib/cretonne/src/ir/stackslot.rs +++ b/lib/cretonne/src/ir/stackslot.rs @@ -3,43 +3,104 @@ //! The `StackSlotData` struct keeps track of a single stack slot in a function. //! -use std::fmt::{self, Display, Formatter}; +use std::fmt; +use std::str::FromStr; + +/// The kind of a stack slot. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StackSlotKind { + /// A spill slot. This is a stack slot created by the register allocator. + SpillSlot, + + /// A local variable. This is a chunk of local stack memory for use by the `stack_load` and + /// `stack_store` instructions. + Local, + + /// An incoming function argument. + /// + /// If the current function has more arguments than fits in registers, the remaining arguments + /// are passed on the stack by the caller. These incoming arguments are represented as SSA + /// values assigned to incoming stack slots. + IncomingArg, + + /// An outgoing function argument. + /// + /// When preparing to call a function whose arguments don't fit in registers, outgoing argument + /// stack slots are used to represent individual arguments in the outgoing call frame. These + /// stack slots are only valid while setting up a call. + OutgoingArg, +} + +impl FromStr for StackSlotKind { + type Err = (); + + fn from_str(s: &str) -> Result { + use self::StackSlotKind::*; + match s { + "local" => Ok(Local), + "spill_slot" => Ok(SpillSlot), + "incoming_arg" => Ok(IncomingArg), + "outgoing_arg" => Ok(OutgoingArg), + _ => Err(()), + } + } +} + +impl fmt::Display for StackSlotKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::StackSlotKind::*; + f.write_str(match *self { + Local => "local", + SpillSlot => "spill_slot", + IncomingArg => "incoming_arg", + OutgoingArg => "outgoing_arg", + }) + } +} /// Contents of a stack slot. #[derive(Clone, Debug)] pub struct StackSlotData { + /// The kind of stack slot. + pub kind: StackSlotKind, + /// Size of stack slot in bytes. pub size: u32, } impl StackSlotData { /// Create a stack slot with the specified byte size. - pub fn new(size: u32) -> StackSlotData { - StackSlotData { size: size } + pub fn new(kind: StackSlotKind, size: u32) -> StackSlotData { + StackSlotData { kind, size } } } -impl Display for StackSlotData { - fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - write!(fmt, "stack_slot {}", self.size) +impl fmt::Display for StackSlotData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.kind, self.size) } } #[cfg(test)] mod tests { use ir::Function; - use super::StackSlotData; + use super::*; #[test] fn stack_slot() { let mut func = Function::new(); - let ss0 = func.stack_slots.push(StackSlotData::new(4)); - let ss1 = func.stack_slots.push(StackSlotData::new(8)); + let ss0 = func.stack_slots + .push(StackSlotData::new(StackSlotKind::IncomingArg, 4)); + let ss1 = func.stack_slots + .push(StackSlotData::new(StackSlotKind::SpillSlot, 8)); assert_eq!(ss0.to_string(), "ss0"); assert_eq!(ss1.to_string(), "ss1"); assert_eq!(func.stack_slots[ss0].size, 4); assert_eq!(func.stack_slots[ss1].size, 8); + + assert_eq!(func.stack_slots[ss0].to_string(), "incoming_arg 4"); + assert_eq!(func.stack_slots[ss1].to_string(), "spill_slot 8"); } } diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 818e4d9700..f015b7d6df 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -359,7 +359,7 @@ impl<'a> fmt::Display for DisplayValues<'a> { #[cfg(test)] mod tests { - use ir::{Function, FunctionName, StackSlotData}; + use ir::{Function, FunctionName, StackSlotData, StackSlotKind}; use ir::types; #[test] @@ -370,21 +370,21 @@ mod tests { f.name = FunctionName::new("foo"); assert_eq!(f.to_string(), "function %foo() {\n}\n"); - f.stack_slots.push(StackSlotData::new(4)); - assert_eq!(f.to_string(), - "function %foo() {\n ss0 = stack_slot 4\n}\n"); + f.stack_slots + .push(StackSlotData::new(StackSlotKind::Local, 4)); + assert_eq!(f.to_string(), "function %foo() {\n ss0 = local 4\n}\n"); let ebb = f.dfg.make_ebb(); f.layout.append_ebb(ebb); assert_eq!(f.to_string(), - "function %foo() {\n ss0 = stack_slot 4\n\nebb0:\n}\n"); + "function %foo() {\n ss0 = local 4\n\nebb0:\n}\n"); f.dfg.append_ebb_arg(ebb, types::I8); assert_eq!(f.to_string(), - "function %foo() {\n ss0 = stack_slot 4\n\nebb0(v0: i8):\n}\n"); + "function %foo() {\n ss0 = local 4\n\nebb0(v0: i8):\n}\n"); f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap()); assert_eq!(f.to_string(), - "function %foo() {\n ss0 = stack_slot 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"); + "function %foo() {\n ss0 = local 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"); } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 3529bc95a3..bc8ebcfa0d 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -896,13 +896,17 @@ impl<'a> Parser<'a> { // Parse a stack slot decl. // - // stack-slot-decl ::= * StackSlot(ss) "=" "stack_slot" Bytes {"," stack-slot-flag} + // stack-slot-decl ::= * StackSlot(ss) "=" stack-slot-kind Bytes {"," stack-slot-flag} + // stack-slot-kind ::= "local" + // | "spill_slot" + // | "incoming_arg" + // | "outgoing_arg" fn parse_stack_slot_decl(&mut self) -> Result<(u32, StackSlotData)> { let number = self.match_ss("expected stack slot number: ss«n»")?; - self.match_token(Token::Equal, "expected '=' in stack_slot decl")?; - self.match_identifier("stack_slot", "expected 'stack_slot'")?; + self.match_token(Token::Equal, "expected '=' in stack slot declaration")?; + let kind = self.match_enum("expected stack slot kind")?; - // stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" * Bytes {"," stack-slot-flag} + // stack-slot-decl ::= StackSlot(ss) "=" stack-slot-kind * Bytes {"," stack-slot-flag} let bytes: i64 = self.match_imm64("expected byte-size in stack_slot decl")? .into(); if bytes < 0 { @@ -911,9 +915,9 @@ impl<'a> Parser<'a> { if bytes > u32::MAX as i64 { return err!(self.loc, "stack slot too large"); } - let data = StackSlotData::new(bytes as u32); + let data = StackSlotData::new(kind, bytes as u32); - // TBD: stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" Bytes * {"," stack-slot-flag} + // TBD: stack-slot-decl ::= StackSlot(ss) "=" stack-slot-kind Bytes * {"," stack-slot-flag} Ok((number, data)) } @@ -1719,6 +1723,7 @@ mod tests { use super::*; use cretonne::ir::{ArgumentExtension, ArgumentPurpose}; use cretonne::ir::types; + use cretonne::ir::StackSlotKind; use cretonne::ir::entities::AnyEntity; use testfile::{Details, Comment}; use isaspec::IsaSpec; @@ -1793,8 +1798,8 @@ mod tests { #[test] fn stack_slot_decl() { let (func, _) = Parser::new("function %foo() { - ss3 = stack_slot 13 - ss1 = stack_slot 1 + ss3 = incoming_arg 13 + ss1 = spill_slot 1 }") .parse_function(None) .unwrap(); @@ -1802,16 +1807,18 @@ mod tests { let mut iter = func.stack_slots.keys(); let ss0 = iter.next().unwrap(); assert_eq!(ss0.to_string(), "ss0"); + assert_eq!(func.stack_slots[ss0].kind, StackSlotKind::IncomingArg); assert_eq!(func.stack_slots[ss0].size, 13); let ss1 = iter.next().unwrap(); assert_eq!(ss1.to_string(), "ss1"); + assert_eq!(func.stack_slots[ss1].kind, StackSlotKind::SpillSlot); assert_eq!(func.stack_slots[ss1].size, 1); assert_eq!(iter.next(), None); // Catch duplicate definitions. assert_eq!(Parser::new("function %bar() { - ss1 = stack_slot 13 - ss1 = stack_slot 1 + ss1 = spill_slot 13 + ss1 = spill_slot 1 }") .parse_function(None) .unwrap_err() @@ -1844,7 +1851,7 @@ mod tests { fn comments() { let (func, Details { comments, .. }) = Parser::new("; before function %comment() { ; decl - ss10 = stack_slot 13 ; stackslot. + ss10 = outgoing_arg 13 ; stackslot. ; Still stackslot. jt10 = jump_table ebb0 ; Jumptable diff --git a/lib/reader/src/sourcemap.rs b/lib/reader/src/sourcemap.rs index 824eb97621..d905cdb9ab 100644 --- a/lib/reader/src/sourcemap.rs +++ b/lib/reader/src/sourcemap.rs @@ -223,7 +223,7 @@ mod tests { #[test] fn details() { let tf = parse_test("function %detail() { - ss10 = stack_slot 13 + ss10 = incoming_arg 13 jt10 = jump_table ebb0 ebb0(v4: i32, v7: i32): v10 = iadd v4, v7 diff --git a/misc/vim/syntax/cton.vim b/misc/vim/syntax/cton.vim index 86162d8a50..003029e1bb 100644 --- a/misc/vim/syntax/cton.vim +++ b/misc/vim/syntax/cton.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Cretonne " Maintainer: Jakob Stoklund Olesen /