Track stack slot kinds.

Add a StackSlotKind enumeration to help keep track of the different
kinds of stack slots supported:

- Incoming and outgoing function arguments on the stack.
- Spill slots and locals.

Change the text format syntax for declaring a stack slot to use a kind
keyword rather than just 'stack_slot'.
This commit is contained in:
Jakob Stoklund Olesen
2017-06-16 10:35:01 -07:00
parent 66bc0a9c8b
commit 91d919c11a
9 changed files with 110 additions and 38 deletions

View File

@@ -1,7 +1,7 @@
test verifier test verifier
function %average(i32, i32) -> f32 { 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): ebb1(v1: i32, v2: i32):
v3 = f64const 0x0.0 v3 = f64const 0x0.0

View File

@@ -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 allocated in the :term:`function preamble`. Stack slots are not typed, they
simply represent a contiguous sequence of bytes in the stack frame. 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 If no alignment is specified, Cretonne will pick an appropriate alignment
for the stack slot based on its size and access patterns. for the stack slot based on its size and access patterns.

View File

@@ -96,8 +96,10 @@ ebb0(v90: i32, v91: f32):
; Stack slot references ; Stack slot references
function %stack() { function %stack() {
ss10 = stack_slot 8 ss10 = spill_slot 8
ss2 = stack_slot 4 ss2 = local 4
ss3 = incoming_arg 4
ss4 = outgoing_arg 4
ebb0: ebb0:
v1 = stack_load.i32 ss10 v1 = stack_load.i32 ss10
@@ -106,8 +108,10 @@ ebb0:
stack_store v2, ss2 stack_store v2, ss2
} }
; sameln: function %stack() { ; sameln: function %stack() {
; nextln: $ss10 = stack_slot 8 ; nextln: $ss10 = spill_slot 8
; nextln: $ss2 = stack_slot 4 ; nextln: $ss2 = local 4
; nextln: $ss3 = incoming_arg 4
; nextln: $ss4 = outgoing_arg 4
; check: ebb0: ; check: ebb0:
; nextln: $v1 = stack_load.i32 $ss10 ; nextln: $v1 = stack_load.i32 $ss10

View File

@@ -22,7 +22,7 @@ pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension, ArgumentPurpos
pub use ir::types::Type; pub use ir::types::Type;
pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef}; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef};
pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; 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::jumptable::JumpTableData;
pub use ir::valueloc::{ValueLoc, ArgumentLoc}; pub use ir::valueloc::{ValueLoc, ArgumentLoc};
pub use ir::dfg::{DataFlowGraph, ValueDef}; pub use ir::dfg::{DataFlowGraph, ValueDef};

View File

@@ -3,43 +3,104 @@
//! The `StackSlotData` struct keeps track of a single stack slot in a function. //! 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<StackSlotKind, ()> {
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. /// Contents of a stack slot.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct StackSlotData { pub struct StackSlotData {
/// The kind of stack slot.
pub kind: StackSlotKind,
/// Size of stack slot in bytes. /// Size of stack slot in bytes.
pub size: u32, pub size: u32,
} }
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(size: u32) -> StackSlotData { pub fn new(kind: StackSlotKind, size: u32) -> StackSlotData {
StackSlotData { size: size } StackSlotData { kind, size }
} }
} }
impl Display for StackSlotData { impl fmt::Display for StackSlotData {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "stack_slot {}", self.size) write!(f, "{} {}", self.kind, self.size)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ir::Function; use ir::Function;
use super::StackSlotData; use super::*;
#[test] #[test]
fn stack_slot() { fn stack_slot() {
let mut func = Function::new(); let mut func = Function::new();
let ss0 = func.stack_slots.push(StackSlotData::new(4)); let ss0 = func.stack_slots
let ss1 = func.stack_slots.push(StackSlotData::new(8)); .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!(ss0.to_string(), "ss0");
assert_eq!(ss1.to_string(), "ss1"); assert_eq!(ss1.to_string(), "ss1");
assert_eq!(func.stack_slots[ss0].size, 4); assert_eq!(func.stack_slots[ss0].size, 4);
assert_eq!(func.stack_slots[ss1].size, 8); 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");
} }
} }

View File

@@ -359,7 +359,7 @@ impl<'a> fmt::Display for DisplayValues<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ir::{Function, FunctionName, StackSlotData}; use ir::{Function, FunctionName, StackSlotData, StackSlotKind};
use ir::types; use ir::types;
#[test] #[test]
@@ -370,21 +370,21 @@ mod tests {
f.name = FunctionName::new("foo"); f.name = FunctionName::new("foo");
assert_eq!(f.to_string(), "function %foo() {\n}\n"); assert_eq!(f.to_string(), "function %foo() {\n}\n");
f.stack_slots.push(StackSlotData::new(4)); f.stack_slots
assert_eq!(f.to_string(), .push(StackSlotData::new(StackSlotKind::Local, 4));
"function %foo() {\n ss0 = stack_slot 4\n}\n"); assert_eq!(f.to_string(), "function %foo() {\n ss0 = local 4\n}\n");
let ebb = f.dfg.make_ebb(); let ebb = f.dfg.make_ebb();
f.layout.append_ebb(ebb); f.layout.append_ebb(ebb);
assert_eq!(f.to_string(), 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); f.dfg.append_ebb_arg(ebb, types::I8);
assert_eq!(f.to_string(), 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()); f.dfg.append_ebb_arg(ebb, types::F32.by(4).unwrap());
assert_eq!(f.to_string(), 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");
} }
} }

View File

@@ -896,13 +896,17 @@ impl<'a> Parser<'a> {
// Parse a stack slot decl. // 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)> { fn parse_stack_slot_decl(&mut self) -> Result<(u32, StackSlotData)> {
let number = self.match_ss("expected stack slot number: ss«n»")?; let number = self.match_ss("expected stack slot number: ss«n»")?;
self.match_token(Token::Equal, "expected '=' in stack_slot decl")?; self.match_token(Token::Equal, "expected '=' in stack slot declaration")?;
self.match_identifier("stack_slot", "expected 'stack_slot'")?; 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")? let bytes: i64 = self.match_imm64("expected byte-size in stack_slot decl")?
.into(); .into();
if bytes < 0 { if bytes < 0 {
@@ -911,9 +915,9 @@ 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(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)) Ok((number, data))
} }
@@ -1719,6 +1723,7 @@ mod tests {
use super::*; use super::*;
use cretonne::ir::{ArgumentExtension, ArgumentPurpose}; use cretonne::ir::{ArgumentExtension, ArgumentPurpose};
use cretonne::ir::types; use cretonne::ir::types;
use cretonne::ir::StackSlotKind;
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use testfile::{Details, Comment}; use testfile::{Details, Comment};
use isaspec::IsaSpec; use isaspec::IsaSpec;
@@ -1793,8 +1798,8 @@ mod tests {
#[test] #[test]
fn stack_slot_decl() { fn stack_slot_decl() {
let (func, _) = Parser::new("function %foo() { let (func, _) = Parser::new("function %foo() {
ss3 = stack_slot 13 ss3 = incoming_arg 13
ss1 = stack_slot 1 ss1 = spill_slot 1
}") }")
.parse_function(None) .parse_function(None)
.unwrap(); .unwrap();
@@ -1802,16 +1807,18 @@ mod tests {
let mut iter = func.stack_slots.keys(); let mut iter = func.stack_slots.keys();
let ss0 = iter.next().unwrap(); let ss0 = iter.next().unwrap();
assert_eq!(ss0.to_string(), "ss0"); assert_eq!(ss0.to_string(), "ss0");
assert_eq!(func.stack_slots[ss0].kind, StackSlotKind::IncomingArg);
assert_eq!(func.stack_slots[ss0].size, 13); assert_eq!(func.stack_slots[ss0].size, 13);
let ss1 = iter.next().unwrap(); let ss1 = iter.next().unwrap();
assert_eq!(ss1.to_string(), "ss1"); 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!(func.stack_slots[ss1].size, 1);
assert_eq!(iter.next(), None); assert_eq!(iter.next(), None);
// Catch duplicate definitions. // Catch duplicate definitions.
assert_eq!(Parser::new("function %bar() { assert_eq!(Parser::new("function %bar() {
ss1 = stack_slot 13 ss1 = spill_slot 13
ss1 = stack_slot 1 ss1 = spill_slot 1
}") }")
.parse_function(None) .parse_function(None)
.unwrap_err() .unwrap_err()
@@ -1844,7 +1851,7 @@ mod tests {
fn comments() { fn comments() {
let (func, Details { comments, .. }) = Parser::new("; before let (func, Details { comments, .. }) = Parser::new("; before
function %comment() { ; decl function %comment() { ; decl
ss10 = stack_slot 13 ; stackslot. ss10 = outgoing_arg 13 ; stackslot.
; Still stackslot. ; Still stackslot.
jt10 = jump_table ebb0 jt10 = jump_table ebb0
; Jumptable ; Jumptable

View File

@@ -223,7 +223,7 @@ mod tests {
#[test] #[test]
fn details() { fn details() {
let tf = parse_test("function %detail() { let tf = parse_test("function %detail() {
ss10 = stack_slot 13 ss10 = incoming_arg 13
jt10 = jump_table ebb0 jt10 = jump_table ebb0
ebb0(v4: i32, v7: i32): ebb0(v4: i32, v7: i32):
v10 = iadd v4, v7 v10 = iadd v4, v7

View File

@@ -1,7 +1,7 @@
" Vim syntax file " Vim syntax file
" Language: Cretonne " Language: Cretonne
" Maintainer: Jakob Stoklund Olesen <stoklund@2pi.dk " Maintainer: Jakob Stoklund Olesen <stoklund@2pi.dk
" Last Change: Sep 22, 2016 " Last Change: Jun 16, 2017
if version < 600 if version < 600
syntax clear syntax clear
@@ -14,7 +14,7 @@ endif
syn spell notoplevel syn spell notoplevel
syn keyword ctonHeader test isa set syn keyword ctonHeader test isa set
syn keyword ctonDecl function stack_slot jump_table syn keyword ctonDecl function jump_table incoming_arg outgoing_arg spill_slot local
syn keyword ctonFilecheck check sameln nextln unordered not regex contained syn keyword ctonFilecheck check sameln nextln unordered not regex contained
syn match ctonType /\<[bif]\d\+\(x\d\+\)\?\>/ syn match ctonType /\<[bif]\d\+\(x\d\+\)\?\>/