diff --git a/src/libcretonne/entities.rs b/src/libcretonne/entities.rs index 8513447ab2..dcdff9e822 100644 --- a/src/libcretonne/entities.rs +++ b/src/libcretonne/entities.rs @@ -24,7 +24,7 @@ use std::fmt::{self, Display, Formatter, Write}; use std::u32; /// An opaque reference to an extended basic block in a function. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Ebb(u32); impl Ebb { @@ -33,6 +33,15 @@ impl Ebb { Ebb(index as u32) } + /// Create a new EBB reference from its number. This corresponds to the ebbNN representation. + pub fn with_number(n: u32) -> Option { + if n < u32::MAX { + Some(Ebb(n)) + } else { + None + } + } + pub fn index(&self) -> usize { self.0 as usize } @@ -54,9 +63,8 @@ impl Default for Ebb { } } - /// An opaque reference to an instruction in a function. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Inst(u32); impl Inst { @@ -88,7 +96,7 @@ impl Default for Inst { /// An opaque reference to an SSA value. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct Value(u32); // Value references can either reference an instruction directly, or they can refer to the extended @@ -105,12 +113,29 @@ pub enum ExpandedValue { } impl Value { - pub fn direct_from_number(n: u32) -> Value { - let encoding = n * 2; - assert!(encoding < u32::MAX); - Value(encoding) + /// Create a `Direct` value from its number representation. + /// This is the number in the vNN notation. + pub fn direct_with_number(n: u32) -> Option { + if n < u32::MAX / 2 { + let encoding = n * 2; + assert!(encoding < u32::MAX); + Some(Value(encoding)) + } else { + None + } } + /// Create a `Table` value from its number representation. + /// This is the number in the vxNN notation. + pub fn table_with_number(n: u32) -> Option { + if n < u32::MAX / 2 { + let encoding = n * 2 + 1; + assert!(encoding < u32::MAX); + Some(Value(encoding)) + } else { + None + } + } pub fn new_direct(i: Inst) -> Value { let encoding = i.index() * 2; assert!(encoding < u32::MAX as usize); @@ -160,7 +185,7 @@ impl Default for Value { } /// An opaque reference to a stack slot. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct StackSlot(u32); impl StackSlot { @@ -191,7 +216,7 @@ impl Default for StackSlot { } /// An opaque reference to a jump table. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct JumpTable(u32); impl JumpTable { @@ -220,3 +245,31 @@ impl Default for JumpTable { NO_JUMP_TABLE } } + +#[cfg(test)] +mod tests { + use super::*; + use std::u32; + + #[test] + fn value_with_number() { + assert_eq!(Value::direct_with_number(0).unwrap().to_string(), "v0"); + assert_eq!(Value::direct_with_number(1).unwrap().to_string(), "v1"); + assert_eq!(Value::table_with_number(0).unwrap().to_string(), "vx0"); + assert_eq!(Value::table_with_number(1).unwrap().to_string(), "vx1"); + + assert_eq!(Value::direct_with_number(u32::MAX / 2), None); + assert_eq!(match Value::direct_with_number(u32::MAX / 2 - 1).unwrap().expand() { + ExpandedValue::Direct(i) => i.index() as u32, + _ => u32::MAX, + }, + u32::MAX / 2 - 1); + + assert_eq!(Value::table_with_number(u32::MAX / 2), None); + assert_eq!(match Value::table_with_number(u32::MAX / 2 - 1).unwrap().expand() { + ExpandedValue::Table(i) => i as u32, + _ => u32::MAX, + }, + u32::MAX / 2 - 1); + } +} diff --git a/src/libreader/lexer.rs b/src/libreader/lexer.rs index 3611a70f69..4d3dfdf7c1 100644 --- a/src/libreader/lexer.rs +++ b/src/libreader/lexer.rs @@ -7,6 +7,7 @@ use std::str::CharIndices; use cretonne::types; +use cretonne::entities::{Value, Ebb}; /// The location of a `Token` or `Error`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -35,9 +36,8 @@ pub enum Token<'a> { Float(&'a str), // Floating point immediate Integer(&'a str), // Integer immediate Type(types::Type), // i32, f32, b32x4, ... - ValueDirect(u32), // v12 - ValueTable(u32), // vx7 - Ebb(u32), // ebb3 + Value(Value), // v12, vx7 + Ebb(Ebb), // ebb3 StackSlot(u32), // ss3 Identifier(&'a str), // Unrecognized identifier (opcode, enumerator, ...) } @@ -289,9 +289,9 @@ impl<'a> Lexer<'a> { }; match prefix { - "v" => Some(Token::ValueDirect(value)), - "vx" => Some(Token::ValueTable(value)), - "ebb" => Some(Token::Ebb(value)), + "v" => Value::direct_with_number(value).map(|v| Token::Value(v)), + "vx" => Value::table_with_number(value).map(|v| Token::Value(v)), + "ebb" => Ebb::with_number(value).map(|ebb| Token::Ebb(ebb)), "ss" => Some(Token::StackSlot(value)), _ => None, } @@ -374,6 +374,7 @@ impl<'a> Lexer<'a> { mod tests { use super::*; use cretonne::types; + use cretonne::entities::{Value, Ebb}; fn token<'a>(token: Token<'a>, line: usize) -> Option, LocatedError>> { Some(super::token(token, Location { line_number: line })) @@ -445,14 +446,17 @@ mod tests { fn lex_identifiers() { let mut lex = Lexer::new("v0 v00 vx01 ebb1234567890 ebb5234567890 entry v1x vx1 vxvx4 \ function0 function b1 i32x4 f32x5"); - assert_eq!(lex.next(), token(Token::ValueDirect(0), 1)); + assert_eq!(lex.next(), + token(Token::Value(Value::direct_with_number(0).unwrap()), 1)); assert_eq!(lex.next(), token(Token::Identifier("v00"), 1)); assert_eq!(lex.next(), token(Token::Identifier("vx01"), 1)); - assert_eq!(lex.next(), token(Token::Ebb(1234567890), 1)); + assert_eq!(lex.next(), + token(Token::Ebb(Ebb::with_number(1234567890).unwrap()), 1)); assert_eq!(lex.next(), token(Token::Identifier("ebb5234567890"), 1)); assert_eq!(lex.next(), token(Token::Entry, 1)); assert_eq!(lex.next(), token(Token::Identifier("v1x"), 1)); - assert_eq!(lex.next(), token(Token::ValueTable(1), 1)); + assert_eq!(lex.next(), + token(Token::Value(Value::table_with_number(1).unwrap()), 1)); assert_eq!(lex.next(), token(Token::Identifier("vxvx4"), 1)); assert_eq!(lex.next(), token(Token::Identifier("function0"), 1)); assert_eq!(lex.next(), token(Token::Function, 1)); diff --git a/src/libreader/parser.rs b/src/libreader/parser.rs index 0344b6238f..ff52940941 100644 --- a/src/libreader/parser.rs +++ b/src/libreader/parser.rs @@ -52,9 +52,8 @@ pub struct Parser<'a> { struct Context { function: Function, stack_slots: HashMap, // ssNN - ebbs: HashMap, // ebbNN - value_directs: HashMap, // vNN - value_tables: HashMap, // vxNN + ebbs: HashMap, // ebbNN + values: HashMap, // vNN, vxNN } impl Context { @@ -63,8 +62,7 @@ impl Context { function: f, stack_slots: HashMap::new(), ebbs: HashMap::new(), - value_directs: HashMap::new(), - value_tables: HashMap::new(), + values: HashMap::new(), } } @@ -80,37 +78,25 @@ impl Context { } } - // Allocate a new EBB and add a mapping number -> Ebb. - fn add_ebb(&mut self, number: u32, loc: &Location) -> Result { + // Allocate a new EBB and add a mapping src_ebb -> Ebb. + fn add_ebb(&mut self, src_ebb: Ebb, loc: &Location) -> Result { let ebb = self.function.make_ebb(); - if self.ebbs.insert(number, ebb).is_some() { + if self.ebbs.insert(src_ebb, ebb).is_some() { Err(Error { location: loc.clone(), - message: format!("duplicate EBB: ebb{}", number), + message: format!("duplicate EBB: {}", src_ebb), }) } else { Ok(ebb) } } - // Add a value mapping number -> data for a direct value (vNN). - fn add_v(&mut self, number: u32, data: Value, loc: &Location) -> Result<()> { - if self.value_directs.insert(number, data).is_some() { + // Add a value mapping src_val -> data. + fn add_value(&mut self, src_val: Value, data: Value, loc: &Location) -> Result<()> { + if self.values.insert(src_val, data).is_some() { Err(Error { location: loc.clone(), - message: format!("duplicate value: v{}", number), - }) - } else { - Ok(()) - } - } - - // Add a value mapping number -> data for a table value (vxNN). - fn add_vx(&mut self, number: u32, data: Value, loc: &Location) -> Result<()> { - if self.value_tables.insert(number, data).is_some() { - Err(Error { - location: loc.clone(), - message: format!("duplicate value: vx{}", number), + message: format!("duplicate value: {}", src_val), }) } else { Ok(()) @@ -220,7 +206,7 @@ impl<'a> Parser<'a> { } // Match and consume an ebb reference. - fn match_ebb(&mut self, err_msg: &str) -> Result { + fn match_ebb(&mut self, err_msg: &str) -> Result { if let Some(Token::Ebb(ebb)) = self.token() { self.consume(); Ok(ebb) @@ -229,26 +215,15 @@ impl<'a> Parser<'a> { } } - // Match and consume a vx reference. - fn match_vx(&mut self, err_msg: &str) -> Result { - if let Some(Token::ValueTable(vx)) = self.token() { - self.consume(); - Ok(vx) - } else { - Err(self.error(err_msg)) - } - } - // Match and consume a value reference, direct or vtable. // This does not convert from the source value numbering to our in-memory value numbering. fn match_value(&mut self, err_msg: &str) -> Result { - let val = match self.token() { - Some(Token::ValueDirect(v)) => Value::direct_from_number(v), - Some(Token::ValueTable(vx)) => Value::new_table(vx as usize), - _ => return Err(self.error(err_msg)), - }; - self.consume(); - Ok(val) + if let Some(Token::Value(v)) = self.token() { + self.consume(); + Ok(v) + } else { + Err(self.error(err_msg)) + } } // Match and consume an Imm64 immediate. @@ -484,7 +459,7 @@ impl<'a> Parser<'a> { // extended-basic-block ::= ebb-header * { instruction } while match self.token() { - Some(Token::ValueDirect(_)) => true, + Some(Token::Value(_)) => true, Some(Token::Identifier(_)) => true, _ => false, } { @@ -519,39 +494,39 @@ impl<'a> Parser<'a> { // Parse a single EBB argument declaration, and append it to `ebb`. // - // ebb-arg ::= * ValueTable(vx) ":" Type(t) + // ebb-arg ::= * Value(vx) ":" Type(t) // fn parse_ebb_arg(&mut self, ctx: &mut Context, ebb: Ebb) -> Result<()> { - // ebb-arg ::= * ValueTable(vx) ":" Type(t) - let vx = try!(self.match_vx("EBB argument must be a vx value")); + // ebb-arg ::= * Value(vx) ":" Type(t) + let vx = try!(self.match_value("EBB argument must be a value")); let vx_location = self.location; - // ebb-arg ::= ValueTable(vx) * ":" Type(t) + // ebb-arg ::= Value(vx) * ":" Type(t) try!(self.match_token(Token::Colon, "expected ':' after EBB argument")); - // ebb-arg ::= ValueTable(vx) ":" * Type(t) + // ebb-arg ::= Value(vx) ":" * Type(t) let t = try!(self.match_type("expected EBB argument type")); // Allocate the EBB argument and add the mapping. let value = ctx.function.append_ebb_arg(ebb, t); - ctx.add_vx(vx, value, &vx_location) + ctx.add_value(vx, value, &vx_location) } // Parse an instruction, append it to `ebb`. // // instruction ::= [inst-results "="] Opcode(opc) ... - // inst-results ::= ValueDirect(v) { "," ValueTable(vx) } + // inst-results ::= Value(v) { "," Value(vx) } // fn parse_instruction(&mut self, ctx: &mut Context, ebb: Ebb) -> Result<()> { - // Result value numbers. First is a ValueDirect, remaining are ValueTable. + // Result value numbers. let mut results = Vec::new(); // instruction ::= * [inst-results "="] Opcode(opc) ... - if let Some(Token::ValueDirect(v)) = self.token() { + if let Some(Token::Value(v)) = self.token() { self.consume(); results.push(v); - // inst-results ::= ValueDirect(v) * { "," ValueTable(vx) } + // inst-results ::= Value(v) * { "," Value(vx) } while self.optional(Token::Comma) { - // inst-results ::= ValueDirect(v) { "," * ValueTable(vx) } - results.push(try!(self.match_vx("expected vx result value"))); + // inst-results ::= Value(v) { "," * Value(vx) } + results.push(try!(self.match_value("expected result value"))); } try!(self.match_token(Token::Equal, "expected '=' before opcode")); @@ -576,7 +551,7 @@ impl<'a> Parser<'a> { if !results.is_empty() { assert!(results.len() == 1, "Multiple results not implemented"); let result = ctx.function.first_result(inst); - try!(ctx.add_v(results[0], result, &self.location)); + try!(ctx.add_value(results[0], result, &self.location)); } ctx.function.append_inst(ebb, inst);