Parse ISA specifications between test commands and functions.
Some tests are only applicable to specific ISAs. This can be indicated
with an ISA specification:
test legalizer
isa riscv
function foo() {
....
}
The ISA specifications have the same format as the test lines: The name
of the ISA following by optional settings until the end of the line.
Also parse `set` commands mixed in with the `isa` commands. These are
used to set ISA-independent settings as defined in
meta/cretonne/settings.py.
This commit is contained in:
@@ -88,6 +88,9 @@ impl settings::Configurable for Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait TargetIsa {
|
pub trait TargetIsa {
|
||||||
|
/// Get the name of this ISA.
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Encode an instruction after determining it is legal.
|
/// Encode an instruction after determining it is legal.
|
||||||
///
|
///
|
||||||
/// If `inst` can legally be encoded in this ISA, produce the corresponding `Encoding` object.
|
/// If `inst` can legally be encoded in this ISA, produce the corresponding `Encoding` object.
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ fn isa_constructor(shared_flags: shared_settings::Flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TargetIsa for Isa {
|
impl TargetIsa for Isa {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"riscv"
|
||||||
|
}
|
||||||
|
|
||||||
fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Option<Encoding> {
|
fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Option<Encoding> {
|
||||||
lookup_enclist(inst.first_type(),
|
lookup_enclist(inst.first_type(),
|
||||||
inst.opcode(),
|
inst.opcode(),
|
||||||
|
|||||||
49
src/libreader/isaspec.rs
Normal file
49
src/libreader/isaspec.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//! Parsed representation of `set` and `isa` commands.
|
||||||
|
//!
|
||||||
|
//! A test case file can contain `set` commands that set ISA-independent settings, and it can
|
||||||
|
//! contain `isa` commands that select an ISA and applies ISA-specific settings.
|
||||||
|
//!
|
||||||
|
//! If a test case file contains `isa` commands, the tests will only be run against the specified
|
||||||
|
//! ISAs. If the file contains no `isa` commands, the tests will be run against all supported ISAs.
|
||||||
|
|
||||||
|
use cretonne::settings::{Flags, Configurable, Error as SetError};
|
||||||
|
use cretonne::isa::TargetIsa;
|
||||||
|
use error::{Result, Location};
|
||||||
|
use testcommand::TestOption;
|
||||||
|
|
||||||
|
/// The ISA specifications in a `.cton` file.
|
||||||
|
pub enum IsaSpec {
|
||||||
|
/// The parsed file does not contain any `isa` commands, but it may contain `set` commands
|
||||||
|
/// which are reflected in the finished `Flags` object.
|
||||||
|
None(Flags),
|
||||||
|
|
||||||
|
/// The parsed file does contains `isa` commands.
|
||||||
|
/// Each `isa` command is used to configure a `TargetIsa` trait object.
|
||||||
|
Some(Vec<Box<TargetIsa>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse an iterator of command line options and apply them to `config`.
|
||||||
|
pub fn parse_options<'a, I>(iter: I, config: &mut Configurable, loc: &Location) -> Result<()>
|
||||||
|
where I: Iterator<Item = &'a str>
|
||||||
|
{
|
||||||
|
for opt in iter.map(TestOption::new) {
|
||||||
|
match opt {
|
||||||
|
TestOption::Flag(name) => {
|
||||||
|
match config.set_bool(name, true) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(SetError::BadName) => return err!(loc, "unknown flag '{}'", opt),
|
||||||
|
Err(_) => return err!(loc, "not a boolean flag: '{}'", opt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TestOption::Value(name, value) => {
|
||||||
|
match config.set(name, value) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(SetError::BadName) => return err!(loc, "unknown setting '{}'", opt),
|
||||||
|
Err(SetError::BadType) => return err!(loc, "invalid setting type: '{}'", opt),
|
||||||
|
Err(SetError::BadValue) => return err!(loc, "invalid setting value: '{}'", opt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -15,5 +15,6 @@ mod error;
|
|||||||
mod lexer;
|
mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod testcommand;
|
mod testcommand;
|
||||||
|
mod isaspec;
|
||||||
mod testfile;
|
mod testfile;
|
||||||
mod sourcemap;
|
mod sourcemap;
|
||||||
|
|||||||
@@ -15,10 +15,13 @@ use cretonne::ir::immediates::{Imm64, Ieee32, Ieee64};
|
|||||||
use cretonne::ir::entities::{AnyEntity, NO_EBB, NO_INST, NO_VALUE};
|
use cretonne::ir::entities::{AnyEntity, NO_EBB, NO_INST, NO_VALUE};
|
||||||
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs, JumpData,
|
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs, JumpData,
|
||||||
BranchData, ReturnData};
|
BranchData, ReturnData};
|
||||||
|
use cretonne::isa;
|
||||||
|
use cretonne::settings;
|
||||||
use testfile::{TestFile, Details, Comment};
|
use testfile::{TestFile, Details, Comment};
|
||||||
use error::{Location, Error, Result};
|
use error::{Location, Error, Result};
|
||||||
use lexer::{self, Lexer, Token};
|
use lexer::{self, Lexer, Token};
|
||||||
use testcommand::TestCommand;
|
use testcommand::TestCommand;
|
||||||
|
use isaspec;
|
||||||
use sourcemap::{SourceMap, MutableSourceMap};
|
use sourcemap::{SourceMap, MutableSourceMap};
|
||||||
|
|
||||||
/// Parse the entire `text` into a list of functions.
|
/// Parse the entire `text` into a list of functions.
|
||||||
@@ -35,6 +38,7 @@ pub fn parse_test<'a>(text: &'a str) -> Result<TestFile<'a>> {
|
|||||||
let mut parser = Parser::new(text);
|
let mut parser = Parser::new(text);
|
||||||
Ok(TestFile {
|
Ok(TestFile {
|
||||||
commands: parser.parse_test_commands(),
|
commands: parser.parse_test_commands(),
|
||||||
|
isa_spec: try!(parser.parse_isa_specs()),
|
||||||
functions: try!(parser.parse_function_list()),
|
functions: try!(parser.parse_function_list()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -397,6 +401,63 @@ impl<'a> Parser<'a> {
|
|||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a list of ISA specs.
|
||||||
|
///
|
||||||
|
/// Accept a mix of `isa` and `set` command lines. The `set` commands are cumulative.
|
||||||
|
///
|
||||||
|
pub fn parse_isa_specs(&mut self) -> Result<isaspec::IsaSpec> {
|
||||||
|
// Was there any `isa` commands?
|
||||||
|
let mut seen_isa = false;
|
||||||
|
// Location of last `set` command since the last `isa`.
|
||||||
|
let mut last_set_loc = None;
|
||||||
|
|
||||||
|
let mut isas = Vec::new();
|
||||||
|
let mut flag_builder = settings::builder();
|
||||||
|
|
||||||
|
while let Some(Token::Identifier(command)) = self.token() {
|
||||||
|
match command {
|
||||||
|
"set" => {
|
||||||
|
last_set_loc = Some(self.loc);
|
||||||
|
try!(isaspec::parse_options(self.consume_line().trim().split_whitespace(),
|
||||||
|
&mut flag_builder,
|
||||||
|
&self.loc));
|
||||||
|
}
|
||||||
|
"isa" => {
|
||||||
|
last_set_loc = None;
|
||||||
|
seen_isa = true;
|
||||||
|
let loc = self.loc;
|
||||||
|
// Grab the whole line so the lexer won't go looking for tokens on the
|
||||||
|
// following lines.
|
||||||
|
let mut words = self.consume_line().trim().split_whitespace();
|
||||||
|
// Look for `isa foo`.
|
||||||
|
let isa_name = match words.next() {
|
||||||
|
None => return err!(loc, "expected ISA name"),
|
||||||
|
Some(w) => w,
|
||||||
|
};
|
||||||
|
let mut isa_builder = match isa::lookup(isa_name) {
|
||||||
|
None => return err!(loc, "unknown ISA '{}'", isa_name),
|
||||||
|
Some(b) => b,
|
||||||
|
};
|
||||||
|
// Apply the ISA-specific settings to `isa_builder`.
|
||||||
|
try!(isaspec::parse_options(words, &mut isa_builder, &self.loc));
|
||||||
|
|
||||||
|
// Construct a trait object with the aggregrate settings.
|
||||||
|
isas.push(isa_builder.finish(settings::Flags::new(&flag_builder)));
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !seen_isa {
|
||||||
|
// No `isa` commands, but we allow for `set` commands.
|
||||||
|
Ok(isaspec::IsaSpec::None(settings::Flags::new(&flag_builder)))
|
||||||
|
} else if let Some(loc) = last_set_loc {
|
||||||
|
err!(loc,
|
||||||
|
"dangling 'set' command after ISA specification has no effect.")
|
||||||
|
} else {
|
||||||
|
Ok(isaspec::IsaSpec::Some(isas))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
@@ -1115,6 +1176,7 @@ mod tests {
|
|||||||
use cretonne::ir::types::{self, ArgumentType, ArgumentExtension};
|
use cretonne::ir::types::{self, ArgumentType, ArgumentExtension};
|
||||||
use cretonne::ir::entities::AnyEntity;
|
use cretonne::ir::entities::AnyEntity;
|
||||||
use testfile::{Details, Comment};
|
use testfile::{Details, Comment};
|
||||||
|
use isaspec::IsaSpec;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1246,12 +1308,41 @@ mod tests {
|
|||||||
let tf = parse_test("; before
|
let tf = parse_test("; before
|
||||||
test cfg option=5
|
test cfg option=5
|
||||||
test verify
|
test verify
|
||||||
|
set enable_float=false
|
||||||
function comment() {}")
|
function comment() {}")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(tf.commands.len(), 2);
|
assert_eq!(tf.commands.len(), 2);
|
||||||
assert_eq!(tf.commands[0].command, "cfg");
|
assert_eq!(tf.commands[0].command, "cfg");
|
||||||
assert_eq!(tf.commands[1].command, "verify");
|
assert_eq!(tf.commands[1].command, "verify");
|
||||||
|
match tf.isa_spec {
|
||||||
|
IsaSpec::None(s) => assert!(!s.enable_float()),
|
||||||
|
_ => panic!("unexpected ISAs"),
|
||||||
|
}
|
||||||
assert_eq!(tf.functions.len(), 1);
|
assert_eq!(tf.functions.len(), 1);
|
||||||
assert_eq!(tf.functions[0].0.name.to_string(), "comment");
|
assert_eq!(tf.functions[0].0.name.to_string(), "comment");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn isa_spec() {
|
||||||
|
assert!(parse_test("isa
|
||||||
|
function foo() {}")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
assert!(parse_test("isa riscv
|
||||||
|
set enable_float=false
|
||||||
|
function foo() {}")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
match parse_test("set enable_float=false
|
||||||
|
isa riscv
|
||||||
|
function foo() {}")
|
||||||
|
.unwrap()
|
||||||
|
.isa_spec {
|
||||||
|
IsaSpec::None(_) => panic!("Expected some ISA"),
|
||||||
|
IsaSpec::Some(v) => {
|
||||||
|
assert_eq!(v.len(), 1);
|
||||||
|
assert_eq!(v[0].name(), "riscv");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,19 @@
|
|||||||
use cretonne::ir::Function;
|
use cretonne::ir::Function;
|
||||||
use cretonne::ir::entities::AnyEntity;
|
use cretonne::ir::entities::AnyEntity;
|
||||||
use testcommand::TestCommand;
|
use testcommand::TestCommand;
|
||||||
|
use isaspec::IsaSpec;
|
||||||
use sourcemap::SourceMap;
|
use sourcemap::SourceMap;
|
||||||
use error::Location;
|
use error::Location;
|
||||||
|
|
||||||
/// A parsed test case.
|
/// A parsed test case.
|
||||||
///
|
///
|
||||||
/// This is the result of parsing a `.cton` file which contains a number of test commands followed
|
/// This is the result of parsing a `.cton` file which contains a number of test commands and ISA
|
||||||
/// by the functions that should be tested.
|
/// specs followed by the functions that should be tested.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TestFile<'a> {
|
pub struct TestFile<'a> {
|
||||||
|
/// `test foo ...` lines.
|
||||||
pub commands: Vec<TestCommand<'a>>,
|
pub commands: Vec<TestCommand<'a>>,
|
||||||
|
/// `isa bar ...` lines.
|
||||||
|
pub isa_spec: IsaSpec,
|
||||||
pub functions: Vec<(Function, Details<'a>)>,
|
pub functions: Vec<(Function, Details<'a>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user