diff --git a/cranelift/src/libcretonne/immediates.rs b/cranelift/src/libcretonne/immediates.rs index 4fefdbb9d0..072a8dcb91 100644 --- a/cranelift/src/libcretonne/immediates.rs +++ b/cranelift/src/libcretonne/immediates.rs @@ -74,6 +74,10 @@ impl Imm64 { pub fn from_bits(x: u64) -> Imm64 { Imm64(x as i64) } + + pub fn to_bits(&self) -> u64 { + self.0 as u64 + } } impl Display for Imm64 { diff --git a/cranelift/src/libcretonne/repr.rs b/cranelift/src/libcretonne/repr.rs index 7d11b62bd1..e982a65ddd 100644 --- a/cranelift/src/libcretonne/repr.rs +++ b/cranelift/src/libcretonne/repr.rs @@ -4,6 +4,7 @@ use types::{Type, FunctionName, Signature}; use immediates::*; use std::fmt::{self, Display, Formatter, Write}; +use std::ops::Index; use std::u32; // ====--------------------------------------------------------------------------------------====// @@ -33,18 +34,29 @@ pub struct Value(u32); /// A guaranteed invalid value reference. 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. /// /// 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` and `Index`. /// +#[derive(Debug)] pub struct Function { /// Name of this function. Mostly used by `.cton` files. - name: FunctionName, + pub name: FunctionName, /// Signature of this function. signature: Signature, + /// Stack slots allocated in this function. + stack_slots: Vec, + /// 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. instructions: Vec, @@ -61,7 +73,15 @@ pub struct Function { pub return_types: Vec, } +/// 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. +#[derive(Debug)] pub struct EbbData { /// 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 @@ -75,6 +95,7 @@ pub struct EbbData { /// 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 /// `Box` to store the additional information out of line. +#[derive(Debug)] pub enum InstructionData { Nullary { opcode: Opcode, @@ -109,6 +130,7 @@ pub enum InstructionData { } /// Payload of a call instruction. +#[derive(Debug)] pub struct CallData { // Number of result values. 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 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 { + if self.cur < self.end { + let ss = StackSlot::new(self.cur); + self.cur += 1; + Some(ss) + } else { + None + } + } +} + // ====--------------------------------------------------------------------------------------====// // // Extended basic block implementation. @@ -227,6 +315,7 @@ impl Display for Value { // Most values are simply the first value produced by an instruction. // Other values have an entry in the value table. +#[derive(Debug)] enum ValueData { // An unused entry in the value table. No instruction should be defining or using this value. Unused, @@ -280,6 +369,7 @@ impl Function { Function { name: name, signature: sig, + stack_slots: Vec::new(), instructions: Vec::new(), extended_basic_blocks: Vec::new(), extended_values: Vec::new(), @@ -292,6 +382,21 @@ impl Function { 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. pub fn inst(&self, i: Inst) -> &InstructionData { &self.instructions[i.0 as usize] @@ -351,4 +456,18 @@ mod tests { assert_eq!(ins.opcode(), Opcode::Iconst); 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); + } + } diff --git a/cranelift/src/libctonfile/parser.rs b/cranelift/src/libctonfile/parser.rs index 22b9adcc93..a014ca0253 100644 --- a/cranelift/src/libctonfile/parser.rs +++ b/cranelift/src/libctonfile/parser.rs @@ -5,11 +5,14 @@ // // ====--------------------------------------------------------------------------------------====// +use std::collections::HashMap; use std::result; use std::fmt::{self, Display, Formatter, Write}; +use std::u32; use lexer::{self, Lexer, Token}; 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; @@ -40,6 +43,35 @@ pub struct Parser<'a> { 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, +} + +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> { /// Create a new `Parser` which reads `text`. The referenced text must outlive the 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> { + 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 { + 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 { + 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. /// /// 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 { 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 "}" 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 * "}" try!(self.match_token(Token::RBrace, "expected '}' after function body")); - Ok(func) + Ok(ctx.function) } // Parse a function spec. @@ -232,6 +298,46 @@ impl<'a> Parser<'a> { 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)] @@ -276,4 +382,33 @@ mod tests { Parser::new("(i8 -> i8").parse_signature().unwrap_err()), "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"); + } }