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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<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.
|
||||
#[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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
" Vim syntax file
|
||||
" Language: Cretonne
|
||||
" Maintainer: Jakob Stoklund Olesen <stoklund@2pi.dk
|
||||
" Last Change: Sep 22, 2016
|
||||
" Last Change: Jun 16, 2017
|
||||
|
||||
if version < 600
|
||||
syntax clear
|
||||
@@ -14,7 +14,7 @@ endif
|
||||
syn spell notoplevel
|
||||
|
||||
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 match ctonType /\<[bif]\d\+\(x\d\+\)\?\>/
|
||||
|
||||
Reference in New Issue
Block a user