Added parsing of instruction encodings and result registers specifications.
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
5ac57220be
commit
a58ccb1125
@@ -6,7 +6,7 @@
|
||||
// ====--------------------------------------------------------------------------------------====//
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::u32;
|
||||
use std::{u16, u32};
|
||||
use std::mem;
|
||||
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable,
|
||||
JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef,
|
||||
@@ -17,7 +17,7 @@ use cretonne::ir::entities::AnyEntity;
|
||||
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
|
||||
TernaryOverflowData, JumpData, BranchData, CallData,
|
||||
IndirectCallData, ReturnData, ReturnRegData};
|
||||
use cretonne::isa;
|
||||
use cretonne::isa::{self, TargetIsa, Encoding};
|
||||
use cretonne::settings;
|
||||
use testfile::{TestFile, Details, Comment};
|
||||
use error::{Location, Error, Result};
|
||||
@@ -40,11 +40,26 @@ pub fn parse_test<'a>(text: &'a str) -> Result<TestFile<'a>> {
|
||||
let mut parser = Parser::new(text);
|
||||
// Gather the preamble comments as 'Function'.
|
||||
parser.gather_comments(AnyEntity::Function);
|
||||
|
||||
let commands = parser.parse_test_commands();
|
||||
let isa_spec = parser.parse_isa_specs()?;
|
||||
let preamble_comments = parser.take_comments();
|
||||
|
||||
let functions = {
|
||||
let mut unique_isa = None;
|
||||
if let isaspec::IsaSpec::Some(ref isa_vec) = isa_spec {
|
||||
if isa_vec.len() == 1 {
|
||||
unique_isa = Some(&*isa_vec[0]);
|
||||
}
|
||||
}
|
||||
parser.parse_function_list(unique_isa)?
|
||||
};
|
||||
|
||||
Ok(TestFile {
|
||||
commands: parser.parse_test_commands(),
|
||||
isa_spec: parser.parse_isa_specs()?,
|
||||
preamble_comments: parser.take_comments(),
|
||||
functions: parser.parse_function_list()?,
|
||||
commands: commands,
|
||||
isa_spec: isa_spec,
|
||||
preamble_comments: preamble_comments,
|
||||
functions: functions,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,16 +86,35 @@ pub struct Parser<'a> {
|
||||
//
|
||||
// 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 {
|
||||
struct Context<'a> {
|
||||
function: Function,
|
||||
map: SourceMap,
|
||||
|
||||
// Reference to the unique_isa for things like parsing ISA-specific instruction encoding
|
||||
// information. This is only `Some` if exactly one set of `isa` directives were found in the
|
||||
// prologue (it is valid to have directives for multiple different ISAs, but in that case we
|
||||
// couldn't know which ISA the provided encodings are intended for)
|
||||
unique_isa: Option<&'a TargetIsa>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
fn new(f: Function) -> Context {
|
||||
impl<'a> Context<'a> {
|
||||
fn new(f: Function, unique_isa: Option<&'a TargetIsa>) -> Context<'a> {
|
||||
Context {
|
||||
function: f,
|
||||
map: SourceMap::new(),
|
||||
unique_isa: unique_isa
|
||||
}
|
||||
}
|
||||
|
||||
// Get the index of a recipe name if it exists.
|
||||
fn find_recipe_index(&self, recipe_name: &str) -> Option<u16> {
|
||||
if let Some(unique_isa) = self.unique_isa {
|
||||
unique_isa.recipe_names()
|
||||
.iter()
|
||||
.position(|&name| name == recipe_name)
|
||||
.map(|idx| idx as u16)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,6 +488,41 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Match and consume an identifier.
|
||||
fn match_any_identifier(&mut self, err_msg: &str) -> Result<&'a str> {
|
||||
if let Some(Token::Identifier(text)) = self.token() {
|
||||
self.consume();
|
||||
Ok(text)
|
||||
} else {
|
||||
err!(self.loc, err_msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Match and consume a HexSequence that fits into a u16.
|
||||
// This is used for instruction encodings.
|
||||
fn match_hex16(&mut self, err_msg: &str) -> Result<u16> {
|
||||
if let Some(Token::HexSequence(bits_str)) = self.token() {
|
||||
self.consume();
|
||||
// The only error we anticipate from this parse is overflow, the lexer should
|
||||
// already have ensured that the string doesn't contain invalid characters, and
|
||||
// isn't empty or negative.
|
||||
u16::from_str_radix(bits_str, 16)
|
||||
.map_err(|_| self.error("the hex sequence given overflows the u16 type"))
|
||||
} else {
|
||||
err!(self.loc, err_msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Match and consume a name.
|
||||
fn match_name(&mut self, err_msg: &str) -> Result<&'a str> {
|
||||
if let Some(Token::Name(name)) = self.token() {
|
||||
self.consume();
|
||||
Ok(name)
|
||||
} else {
|
||||
err!(self.loc, err_msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a list of test commands.
|
||||
pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> {
|
||||
let mut list = Vec::new();
|
||||
@@ -523,10 +592,11 @@ impl<'a> Parser<'a> {
|
||||
/// Parse a list of function definitions.
|
||||
///
|
||||
/// This is the top-level parse function matching the whole contents of a file.
|
||||
pub fn parse_function_list(&mut self) -> Result<Vec<(Function, Details<'a>)>> {
|
||||
pub fn parse_function_list(&mut self, unique_isa: Option<&TargetIsa>)
|
||||
-> Result<Vec<(Function, Details<'a>)>> {
|
||||
let mut list = Vec::new();
|
||||
while self.token().is_some() {
|
||||
list.push(self.parse_function()?);
|
||||
list.push(self.parse_function(unique_isa)?);
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
@@ -535,7 +605,7 @@ impl<'a> Parser<'a> {
|
||||
//
|
||||
// function ::= * function-spec "{" preamble function-body "}"
|
||||
//
|
||||
fn parse_function(&mut self) -> Result<(Function, Details<'a>)> {
|
||||
fn parse_function(&mut self, unique_isa: Option<&TargetIsa>) -> Result<(Function, Details<'a>)> {
|
||||
// Begin gathering comments.
|
||||
// Make sure we don't include any comments before the `function` keyword.
|
||||
self.token();
|
||||
@@ -543,7 +613,7 @@ impl<'a> Parser<'a> {
|
||||
self.gather_comments(AnyEntity::Function);
|
||||
|
||||
let (location, name, sig) = self.parse_function_spec()?;
|
||||
let mut ctx = Context::new(Function::with_name_signature(name, sig));
|
||||
let mut ctx = Context::new(Function::with_name_signature(name, sig), unique_isa);
|
||||
|
||||
// function ::= function-spec * "{" preamble function-body "}"
|
||||
self.match_token(Token::LBrace, "expected '{' before function body")?;
|
||||
@@ -894,6 +964,42 @@ impl<'a> Parser<'a> {
|
||||
ctx.map.def_value(vx, value, &vx_location)
|
||||
}
|
||||
|
||||
fn parse_instruction_encoding(&mut self, ctx: &Context)
|
||||
-> Result<(Option<Encoding>, Option<Vec<&'a str>>)> {
|
||||
let (mut encoding, mut result_registers) = (None, None);
|
||||
|
||||
// encoding ::= "[" encoding_literal result_registers "]"
|
||||
if self.optional(Token::LBracket) {
|
||||
// encoding_literal ::= "-" | Identifier HexSequence
|
||||
if !self.optional(Token::Minus) {
|
||||
let recipe = self.match_any_identifier("expected instruction encoding or '-'")?;
|
||||
let bits = self.match_hex16("expected a hex sequence")?;
|
||||
|
||||
if let Some(recipe_index) = ctx.find_recipe_index(recipe) {
|
||||
encoding = Some(Encoding::new(recipe_index, bits));
|
||||
}
|
||||
}
|
||||
|
||||
// result_registers ::= ("," ( "-" | names ) )?
|
||||
// names ::= Name { "," Name }
|
||||
if self.optional(Token::Comma) && !self.optional(Token::Minus) {
|
||||
let mut result = Vec::new();
|
||||
|
||||
result.push(self.match_name("expected register")?);
|
||||
while self.optional(Token::Comma) {
|
||||
result.push(self.match_name("expected register")?);
|
||||
}
|
||||
|
||||
result_registers = Some(result);
|
||||
}
|
||||
|
||||
self.match_token(Token::RBracket,
|
||||
"expected ']' to terminate instruction encoding")?;
|
||||
}
|
||||
|
||||
Ok((encoding, result_registers))
|
||||
}
|
||||
|
||||
// Parse an instruction, append it to `ebb`.
|
||||
//
|
||||
// instruction ::= [inst-results "="] Opcode(opc) ["." Type] ...
|
||||
@@ -903,6 +1009,8 @@ impl<'a> Parser<'a> {
|
||||
// Collect comments for the next instruction to be allocated.
|
||||
self.gather_comments(ctx.function.dfg.next_inst());
|
||||
|
||||
let (encoding, result_registers) = self.parse_instruction_encoding(ctx)?;
|
||||
|
||||
// Result value numbers.
|
||||
let mut results = Vec::new();
|
||||
|
||||
@@ -921,6 +1029,13 @@ impl<'a> Parser<'a> {
|
||||
self.match_token(Token::Equal, "expected '=' before opcode")?;
|
||||
}
|
||||
|
||||
if let Some(ref result_registers) = result_registers {
|
||||
if result_registers.len() != results.len() {
|
||||
return err!(self.loc,
|
||||
"must have same number of result registers as results");
|
||||
}
|
||||
}
|
||||
|
||||
// instruction ::= [inst-results "="] * Opcode(opc) ["." Type] ...
|
||||
let opcode = if let Some(Token::Identifier(text)) = self.token() {
|
||||
match text.parse() {
|
||||
@@ -955,6 +1070,10 @@ impl<'a> Parser<'a> {
|
||||
ctx.function.layout.append_inst(inst, ebb);
|
||||
ctx.map.def_entity(inst.into(), &opcode_loc).expect("duplicate inst references created");
|
||||
|
||||
if let Some(encoding) = encoding {
|
||||
*ctx.function.encodings.ensure(inst) = encoding;
|
||||
}
|
||||
|
||||
if results.len() != num_results {
|
||||
return err!(self.loc,
|
||||
"instruction produces {} result values, {} given",
|
||||
|
||||
Reference in New Issue
Block a user