Parse stack slot decls.
Add a stack slot array to repr::Function, use repr::StackSlot to reference them. Parse stack slot declarations in the function preamble, add them to the function. Add a new `Context` struct which keeps track of mappings between identifiers used in the file and real references.
This commit is contained in:
@@ -74,6 +74,10 @@ impl Imm64 {
|
|||||||
pub fn from_bits(x: u64) -> Imm64 {
|
pub fn from_bits(x: u64) -> Imm64 {
|
||||||
Imm64(x as i64)
|
Imm64(x as i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_bits(&self) -> u64 {
|
||||||
|
self.0 as u64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Imm64 {
|
impl Display for Imm64 {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use types::{Type, FunctionName, Signature};
|
use types::{Type, FunctionName, Signature};
|
||||||
use immediates::*;
|
use immediates::*;
|
||||||
use std::fmt::{self, Display, Formatter, Write};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
|
use std::ops::Index;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
// ====--------------------------------------------------------------------------------------====//
|
// ====--------------------------------------------------------------------------------------====//
|
||||||
@@ -33,18 +34,29 @@ pub struct Value(u32);
|
|||||||
/// A guaranteed invalid value reference.
|
/// A guaranteed invalid value reference.
|
||||||
pub const NO_VALUE: Value = Value(u32::MAX);
|
pub const NO_VALUE: Value = Value(u32::MAX);
|
||||||
|
|
||||||
|
/// An opaque reference to a stack slot.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct StackSlot(u32);
|
||||||
|
|
||||||
|
/// A guaranteed invalid stack slot reference.
|
||||||
|
pub const NO_STACK_SLOT: StackSlot = StackSlot(u32::MAX);
|
||||||
|
|
||||||
/// A function.
|
/// A function.
|
||||||
///
|
///
|
||||||
/// The `Function` struct owns all of its instructions and extended basic blocks, and it works as a
|
/// The `Function` struct owns all of its instructions and extended basic blocks, and it works as a
|
||||||
/// container for those objects by implementing both `Index<Inst>` and `Index<Ebb>`.
|
/// container for those objects by implementing both `Index<Inst>` and `Index<Ebb>`.
|
||||||
///
|
///
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
/// Name of this function. Mostly used by `.cton` files.
|
/// Name of this function. Mostly used by `.cton` files.
|
||||||
name: FunctionName,
|
pub name: FunctionName,
|
||||||
|
|
||||||
/// Signature of this function.
|
/// Signature of this function.
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
|
|
||||||
|
/// Stack slots allocated in this function.
|
||||||
|
stack_slots: Vec<StackSlotData>,
|
||||||
|
|
||||||
/// Data about all of the instructions in the function. The instructions in this vector is not
|
/// Data about all of the instructions in the function. The instructions in this vector is not
|
||||||
/// necessarily in program order. The `Inst` reference indexes into this vector.
|
/// necessarily in program order. The `Inst` reference indexes into this vector.
|
||||||
instructions: Vec<InstructionData>,
|
instructions: Vec<InstructionData>,
|
||||||
@@ -61,7 +73,15 @@ pub struct Function {
|
|||||||
pub return_types: Vec<Type>,
|
pub return_types: Vec<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contents of a stack slot.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StackSlotData {
|
||||||
|
/// Size of stack slot in bytes.
|
||||||
|
pub size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Contents of an extended basic block.
|
/// Contents of an extended basic block.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct EbbData {
|
pub struct EbbData {
|
||||||
/// Arguments for this extended basic block. These values dominate everything in the EBB.
|
/// Arguments for this extended basic block. These values dominate everything in the EBB.
|
||||||
/// All branches to this EBB must provide matching arguments, and the arguments to the entry
|
/// All branches to this EBB must provide matching arguments, and the arguments to the entry
|
||||||
@@ -75,6 +95,7 @@ pub struct EbbData {
|
|||||||
/// value should have its `ty` field set to `VOID`. The size of `InstructionData` should be kept at
|
/// value should have its `ty` field set to `VOID`. The size of `InstructionData` should be kept at
|
||||||
/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a
|
/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a
|
||||||
/// `Box<AuxData>` to store the additional information out of line.
|
/// `Box<AuxData>` to store the additional information out of line.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum InstructionData {
|
pub enum InstructionData {
|
||||||
Nullary {
|
Nullary {
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
@@ -109,6 +130,7 @@ pub enum InstructionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Payload of a call instruction.
|
/// Payload of a call instruction.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CallData {
|
pub struct CallData {
|
||||||
// Number of result values.
|
// Number of result values.
|
||||||
results: u8,
|
results: u8,
|
||||||
@@ -119,6 +141,72 @@ pub struct CallData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ====--------------------------------------------------------------------------------------====//
|
||||||
|
//
|
||||||
|
// Stack slot implementation.
|
||||||
|
//
|
||||||
|
// ====--------------------------------------------------------------------------------------====//
|
||||||
|
|
||||||
|
impl StackSlot {
|
||||||
|
fn new(index: usize) -> StackSlot {
|
||||||
|
assert!(index < (u32::MAX as usize));
|
||||||
|
StackSlot(index as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self) -> usize {
|
||||||
|
self.0 as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Display a `StackSlot` reference as "ss12".
|
||||||
|
impl Display for StackSlot {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(fmt, "ss{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackSlotData {
|
||||||
|
/// Create a stack slot with the specified byte size.
|
||||||
|
pub fn new(size: u32) -> StackSlotData {
|
||||||
|
StackSlotData { size: size }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StackSlotData {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(fmt, "stack_slot {}", self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allow immutable access to stack slots via function indexing.
|
||||||
|
impl Index<StackSlot> for Function {
|
||||||
|
type Output = StackSlotData;
|
||||||
|
|
||||||
|
fn index<'a>(&'a self, ss: StackSlot) -> &'a StackSlotData {
|
||||||
|
&self.stack_slots[ss.index()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stack slot iterator visits all stack slots in a function, returning `StackSlot` references.
|
||||||
|
pub struct StackSlotIter {
|
||||||
|
cur: usize,
|
||||||
|
end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for StackSlotIter {
|
||||||
|
type Item = StackSlot;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.cur < self.end {
|
||||||
|
let ss = StackSlot::new(self.cur);
|
||||||
|
self.cur += 1;
|
||||||
|
Some(ss)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ====--------------------------------------------------------------------------------------====//
|
// ====--------------------------------------------------------------------------------------====//
|
||||||
//
|
//
|
||||||
// Extended basic block implementation.
|
// Extended basic block implementation.
|
||||||
@@ -227,6 +315,7 @@ impl Display for Value {
|
|||||||
|
|
||||||
// Most values are simply the first value produced by an instruction.
|
// Most values are simply the first value produced by an instruction.
|
||||||
// Other values have an entry in the value table.
|
// Other values have an entry in the value table.
|
||||||
|
#[derive(Debug)]
|
||||||
enum ValueData {
|
enum ValueData {
|
||||||
// An unused entry in the value table. No instruction should be defining or using this value.
|
// An unused entry in the value table. No instruction should be defining or using this value.
|
||||||
Unused,
|
Unused,
|
||||||
@@ -280,6 +369,7 @@ impl Function {
|
|||||||
Function {
|
Function {
|
||||||
name: name,
|
name: name,
|
||||||
signature: sig,
|
signature: sig,
|
||||||
|
stack_slots: Vec::new(),
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
extended_basic_blocks: Vec::new(),
|
extended_basic_blocks: Vec::new(),
|
||||||
extended_values: Vec::new(),
|
extended_values: Vec::new(),
|
||||||
@@ -292,6 +382,21 @@ impl Function {
|
|||||||
Self::with_name_signature(FunctionName::new(), Signature::new())
|
Self::with_name_signature(FunctionName::new(), Signature::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allocate a new stack slot.
|
||||||
|
pub fn make_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
|
||||||
|
let ss = StackSlot::new(self.stack_slots.len());
|
||||||
|
self.stack_slots.push(data);
|
||||||
|
ss
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over all stack slots in function.
|
||||||
|
pub fn stack_slot_iter(&self) -> StackSlotIter {
|
||||||
|
StackSlotIter {
|
||||||
|
cur: 0,
|
||||||
|
end: self.stack_slots.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve an instruction reference.
|
/// Resolve an instruction reference.
|
||||||
pub fn inst(&self, i: Inst) -> &InstructionData {
|
pub fn inst(&self, i: Inst) -> &InstructionData {
|
||||||
&self.instructions[i.0 as usize]
|
&self.instructions[i.0 as usize]
|
||||||
@@ -351,4 +456,18 @@ mod tests {
|
|||||||
assert_eq!(ins.opcode(), Opcode::Iconst);
|
assert_eq!(ins.opcode(), Opcode::Iconst);
|
||||||
assert_eq!(ins.first_type(), types::I32);
|
assert_eq!(ins.first_type(), types::I32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_slot() {
|
||||||
|
let mut func = Function::new();
|
||||||
|
|
||||||
|
let ss0 = func.make_stack_slot(StackSlotData::new(4));
|
||||||
|
let ss1 = func.make_stack_slot(StackSlotData::new(8));
|
||||||
|
assert_eq!(format!("{}", ss0), "ss0");
|
||||||
|
assert_eq!(format!("{}", ss1), "ss1");
|
||||||
|
|
||||||
|
assert_eq!(func[ss0].size, 4);
|
||||||
|
assert_eq!(func[ss1].size, 8);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,14 @@
|
|||||||
//
|
//
|
||||||
// ====--------------------------------------------------------------------------------------====//
|
// ====--------------------------------------------------------------------------------------====//
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::fmt::{self, Display, Formatter, Write};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
|
use std::u32;
|
||||||
use lexer::{self, Lexer, Token};
|
use lexer::{self, Lexer, Token};
|
||||||
use cretonne::types::{FunctionName, Signature, ArgumentType, ArgumentExtension};
|
use cretonne::types::{FunctionName, Signature, ArgumentType, ArgumentExtension};
|
||||||
use cretonne::repr::Function;
|
use cretonne::immediates::Imm64;
|
||||||
|
use cretonne::repr::{Function, StackSlot, StackSlotData};
|
||||||
|
|
||||||
pub use lexer::Location;
|
pub use lexer::Location;
|
||||||
|
|
||||||
@@ -40,6 +43,35 @@ pub struct Parser<'a> {
|
|||||||
location: Location,
|
location: Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context for resolving references when parsing a single function.
|
||||||
|
//
|
||||||
|
// Many entities like values, stack slots, and function signatures are referenced in the `.cton`
|
||||||
|
// file by number. We need to map these numbers to real references.
|
||||||
|
struct Context {
|
||||||
|
function: Function,
|
||||||
|
stack_slots: HashMap<u32, StackSlot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
fn new(f: Function) -> Context {
|
||||||
|
Context {
|
||||||
|
function: f,
|
||||||
|
stack_slots: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&mut self, number: u32, data: StackSlotData, loc: &Location) -> Result<()> {
|
||||||
|
if self.stack_slots.insert(number, self.function.make_stack_slot(data)).is_some() {
|
||||||
|
Err(Error {
|
||||||
|
location: loc.clone(),
|
||||||
|
message: format!("duplicate stack slot: ss{}", number),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Create a new `Parser` which reads `text`. The referenced text must outlive the parser.
|
/// Create a new `Parser` which reads `text`. The referenced text must outlive the parser.
|
||||||
pub fn new(text: &'a str) -> Parser {
|
pub fn new(text: &'a str) -> Parser {
|
||||||
@@ -111,6 +143,38 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match and consume a specific identifier string.
|
||||||
|
// Used for pseudo-keywords like "stack_slot" that only appear in certain contexts.
|
||||||
|
fn match_identifier(&mut self, want: &'static str, err_msg: &str) -> Result<Token<'a>> {
|
||||||
|
if self.token() == Some(Token::Identifier(want)) {
|
||||||
|
Ok(self.consume())
|
||||||
|
} else {
|
||||||
|
Err(self.error(err_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match and consume a stack slot reference.
|
||||||
|
fn match_ss(&mut self, err_msg: &str) -> Result<u32> {
|
||||||
|
if let Some(Token::StackSlot(ss)) = self.token() {
|
||||||
|
self.consume();
|
||||||
|
Ok(ss)
|
||||||
|
} else {
|
||||||
|
Err(self.error(err_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match and consume an Imm64 immediate.
|
||||||
|
fn match_imm64(&mut self, err_msg: &str) -> Result<Imm64> {
|
||||||
|
if let Some(Token::Integer(text)) = self.token() {
|
||||||
|
self.consume();
|
||||||
|
// Lexer just gives us raw text that looks like an integer.
|
||||||
|
// Parse it as an Imm64 to check for overflow and other issues.
|
||||||
|
text.parse().map_err(|e| self.error(e))
|
||||||
|
} else {
|
||||||
|
Err(self.error(err_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a list of function definitions.
|
/// Parse a list of function definitions.
|
||||||
///
|
///
|
||||||
/// This is the top-level parse function matching the whole contents of a file.
|
/// This is the top-level parse function matching the whole contents of a file.
|
||||||
@@ -128,14 +192,16 @@ impl<'a> Parser<'a> {
|
|||||||
//
|
//
|
||||||
fn parse_function(&mut self) -> Result<Function> {
|
fn parse_function(&mut self) -> Result<Function> {
|
||||||
let (name, sig) = try!(self.parse_function_spec());
|
let (name, sig) = try!(self.parse_function_spec());
|
||||||
let mut func = Function::with_name_signature(name, sig);
|
let mut ctx = Context::new(Function::with_name_signature(name, sig));
|
||||||
|
|
||||||
// function ::= function-spec * "{" preample function-body "}"
|
// function ::= function-spec * "{" preample function-body "}"
|
||||||
try!(self.match_token(Token::LBrace, "expected '{' before function body"));
|
try!(self.match_token(Token::LBrace, "expected '{' before function body"));
|
||||||
|
// function ::= function-spec "{" * preample function-body "}"
|
||||||
|
try!(self.parse_preamble(&mut ctx));
|
||||||
// function ::= function-spec "{" preample function-body * "}"
|
// function ::= function-spec "{" preample function-body * "}"
|
||||||
try!(self.match_token(Token::RBrace, "expected '}' after function body"));
|
try!(self.match_token(Token::RBrace, "expected '}' after function body"));
|
||||||
|
|
||||||
Ok(func)
|
Ok(ctx.function)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a function spec.
|
// Parse a function spec.
|
||||||
@@ -232,6 +298,46 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
Ok(arg)
|
Ok(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the function preamble.
|
||||||
|
//
|
||||||
|
// preamble ::= * { preamble-decl }
|
||||||
|
// preamble-decl ::= * stack-slot-decl
|
||||||
|
// * function-decl
|
||||||
|
// * signature-decl
|
||||||
|
//
|
||||||
|
// The parsed decls are added to `ctx` rather than returned.
|
||||||
|
fn parse_preamble(&mut self, ctx: &mut Context) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
try!(match self.token() {
|
||||||
|
Some(Token::StackSlot(..)) => {
|
||||||
|
self.parse_stack_slot_decl()
|
||||||
|
.and_then(|(num, dat)| ctx.add(num, dat, &self.location))
|
||||||
|
}
|
||||||
|
// More to come..
|
||||||
|
_ => return Ok(()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a stack slot decl, add to `func`.
|
||||||
|
//
|
||||||
|
// stack-slot-decl ::= * StackSlot(ss) "=" "stack_slot" Bytes {"," stack-slot-flag}
|
||||||
|
fn parse_stack_slot_decl(&mut self) -> Result<(u32, StackSlotData)> {
|
||||||
|
let number = try!(self.match_ss("expected stack slot number: ss«n»"));
|
||||||
|
try!(self.match_token(Token::Equal, "expected '=' in stack_slot decl"));
|
||||||
|
try!(self.match_identifier("stack_slot", "expected 'stack_slot'"));
|
||||||
|
|
||||||
|
// stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" * Bytes {"," stack-slot-flag}
|
||||||
|
let bytes = try!(self.match_imm64("expected byte-size in stack_slot decl")).to_bits();
|
||||||
|
if bytes > u32::MAX as u64 {
|
||||||
|
return Err(self.error("stack slot too large"));
|
||||||
|
}
|
||||||
|
let data = StackSlotData::new(bytes as u32);
|
||||||
|
|
||||||
|
// TBD: stack-slot-decl ::= StackSlot(ss) "=" "stack_slot" Bytes * {"," stack-slot-flag}
|
||||||
|
Ok((number, data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -276,4 +382,33 @@ mod tests {
|
|||||||
Parser::new("(i8 -> i8").parse_signature().unwrap_err()),
|
Parser::new("(i8 -> i8").parse_signature().unwrap_err()),
|
||||||
"1: expected ')' after function arguments");
|
"1: expected ')' after function arguments");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_slot_decl() {
|
||||||
|
let func = Parser::new("function foo() {
|
||||||
|
ss3 = stack_slot 13
|
||||||
|
ss1 = stack_slot 1
|
||||||
|
}")
|
||||||
|
.parse_function()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(func.name, "foo");
|
||||||
|
let mut iter = func.stack_slot_iter();
|
||||||
|
let ss0 = iter.next().unwrap();
|
||||||
|
assert_eq!(format!("{}", ss0), "ss0");
|
||||||
|
assert_eq!(func[ss0].size, 13);
|
||||||
|
let ss1 = iter.next().unwrap();
|
||||||
|
assert_eq!(format!("{}", ss1), "ss1");
|
||||||
|
assert_eq!(func[ss1].size, 1);
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
|
||||||
|
// Catch suplicate definitions.
|
||||||
|
assert_eq!(format!("{}",
|
||||||
|
Parser::new("function bar() {
|
||||||
|
ss1 = stack_slot 13
|
||||||
|
ss1 = stack_slot 1
|
||||||
|
}")
|
||||||
|
.parse_function()
|
||||||
|
.unwrap_err()),
|
||||||
|
"3: duplicate stack slot: ss1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user