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
|
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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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\+\)\?\>/
|
||||||
|
|||||||
Reference in New Issue
Block a user