Added parsing of instruction encodings and result registers specifications.
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
fd9d7fae76
commit
a081b09c94
@@ -6,7 +6,7 @@
|
|||||||
// ====--------------------------------------------------------------------------------------====//
|
// ====--------------------------------------------------------------------------------------====//
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::u32;
|
use std::{u16, u32};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable,
|
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable,
|
||||||
JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef,
|
JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef,
|
||||||
@@ -17,7 +17,7 @@ use cretonne::ir::entities::AnyEntity;
|
|||||||
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
|
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs,
|
||||||
TernaryOverflowData, JumpData, BranchData, CallData,
|
TernaryOverflowData, JumpData, BranchData, CallData,
|
||||||
IndirectCallData, ReturnData, ReturnRegData};
|
IndirectCallData, ReturnData, ReturnRegData};
|
||||||
use cretonne::isa;
|
use cretonne::isa::{self, TargetIsa, Encoding};
|
||||||
use cretonne::settings;
|
use cretonne::settings;
|
||||||
use testfile::{TestFile, Details, Comment};
|
use testfile::{TestFile, Details, Comment};
|
||||||
use error::{Location, Error, Result};
|
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);
|
let mut parser = Parser::new(text);
|
||||||
// Gather the preamble comments as 'Function'.
|
// Gather the preamble comments as 'Function'.
|
||||||
parser.gather_comments(AnyEntity::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 {
|
Ok(TestFile {
|
||||||
commands: parser.parse_test_commands(),
|
commands: commands,
|
||||||
isa_spec: parser.parse_isa_specs()?,
|
isa_spec: isa_spec,
|
||||||
preamble_comments: parser.take_comments(),
|
preamble_comments: preamble_comments,
|
||||||
functions: parser.parse_function_list()?,
|
functions: functions,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,16 +86,35 @@ pub struct Parser<'a> {
|
|||||||
//
|
//
|
||||||
// Many entities like values, stack slots, and function signatures are referenced in the `.cton`
|
// 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.
|
// file by number. We need to map these numbers to real references.
|
||||||
struct Context {
|
struct Context<'a> {
|
||||||
function: Function,
|
function: Function,
|
||||||
map: SourceMap,
|
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 {
|
impl<'a> Context<'a> {
|
||||||
fn new(f: Function) -> Context {
|
fn new(f: Function, unique_isa: Option<&'a TargetIsa>) -> Context<'a> {
|
||||||
Context {
|
Context {
|
||||||
function: f,
|
function: f,
|
||||||
map: SourceMap::new(),
|
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.
|
/// Parse a list of test commands.
|
||||||
pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> {
|
pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
@@ -523,10 +592,11 @@ impl<'a> Parser<'a> {
|
|||||||
/// 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.
|
||||||
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();
|
let mut list = Vec::new();
|
||||||
while self.token().is_some() {
|
while self.token().is_some() {
|
||||||
list.push(self.parse_function()?);
|
list.push(self.parse_function(unique_isa)?);
|
||||||
}
|
}
|
||||||
Ok(list)
|
Ok(list)
|
||||||
}
|
}
|
||||||
@@ -535,7 +605,7 @@ impl<'a> Parser<'a> {
|
|||||||
//
|
//
|
||||||
// function ::= * function-spec "{" preamble function-body "}"
|
// 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.
|
// Begin gathering comments.
|
||||||
// Make sure we don't include any comments before the `function` keyword.
|
// Make sure we don't include any comments before the `function` keyword.
|
||||||
self.token();
|
self.token();
|
||||||
@@ -543,7 +613,7 @@ impl<'a> Parser<'a> {
|
|||||||
self.gather_comments(AnyEntity::Function);
|
self.gather_comments(AnyEntity::Function);
|
||||||
|
|
||||||
let (location, name, sig) = self.parse_function_spec()?;
|
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 "}"
|
// function ::= function-spec * "{" preamble function-body "}"
|
||||||
self.match_token(Token::LBrace, "expected '{' before 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)
|
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`.
|
// Parse an instruction, append it to `ebb`.
|
||||||
//
|
//
|
||||||
// instruction ::= [inst-results "="] Opcode(opc) ["." Type] ...
|
// instruction ::= [inst-results "="] Opcode(opc) ["." Type] ...
|
||||||
@@ -903,6 +1009,8 @@ impl<'a> Parser<'a> {
|
|||||||
// Collect comments for the next instruction to be allocated.
|
// Collect comments for the next instruction to be allocated.
|
||||||
self.gather_comments(ctx.function.dfg.next_inst());
|
self.gather_comments(ctx.function.dfg.next_inst());
|
||||||
|
|
||||||
|
let (encoding, result_registers) = self.parse_instruction_encoding(ctx)?;
|
||||||
|
|
||||||
// Result value numbers.
|
// Result value numbers.
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
@@ -921,6 +1029,13 @@ impl<'a> Parser<'a> {
|
|||||||
self.match_token(Token::Equal, "expected '=' before opcode")?;
|
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] ...
|
// instruction ::= [inst-results "="] * Opcode(opc) ["." Type] ...
|
||||||
let opcode = if let Some(Token::Identifier(text)) = self.token() {
|
let opcode = if let Some(Token::Identifier(text)) = self.token() {
|
||||||
match text.parse() {
|
match text.parse() {
|
||||||
@@ -955,6 +1070,10 @@ impl<'a> Parser<'a> {
|
|||||||
ctx.function.layout.append_inst(inst, ebb);
|
ctx.function.layout.append_inst(inst, ebb);
|
||||||
ctx.map.def_entity(inst.into(), &opcode_loc).expect("duplicate inst references created");
|
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 {
|
if results.len() != num_results {
|
||||||
return err!(self.loc,
|
return err!(self.loc,
|
||||||
"instruction produces {} result values, {} given",
|
"instruction produces {} result values, {} given",
|
||||||
|
|||||||
Reference in New Issue
Block a user