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 {
|
||||
/// Get the name of this ISA.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Encode an instruction after determining it is legal.
|
||||
///
|
||||
/// 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 {
|
||||
fn name(&self) -> &'static str {
|
||||
"riscv"
|
||||
}
|
||||
|
||||
fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Option<Encoding> {
|
||||
lookup_enclist(inst.first_type(),
|
||||
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 parser;
|
||||
mod testcommand;
|
||||
mod isaspec;
|
||||
mod testfile;
|
||||
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::instructions::{InstructionFormat, InstructionData, VariableArgs, JumpData,
|
||||
BranchData, ReturnData};
|
||||
use cretonne::isa;
|
||||
use cretonne::settings;
|
||||
use testfile::{TestFile, Details, Comment};
|
||||
use error::{Location, Error, Result};
|
||||
use lexer::{self, Lexer, Token};
|
||||
use testcommand::TestCommand;
|
||||
use isaspec;
|
||||
use sourcemap::{SourceMap, MutableSourceMap};
|
||||
|
||||
/// 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);
|
||||
Ok(TestFile {
|
||||
commands: parser.parse_test_commands(),
|
||||
isa_spec: try!(parser.parse_isa_specs()),
|
||||
functions: try!(parser.parse_function_list()),
|
||||
})
|
||||
}
|
||||
@@ -397,6 +401,63 @@ impl<'a> Parser<'a> {
|
||||
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.
|
||||
///
|
||||
/// 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::entities::AnyEntity;
|
||||
use testfile::{Details, Comment};
|
||||
use isaspec::IsaSpec;
|
||||
use error::Error;
|
||||
|
||||
#[test]
|
||||
@@ -1246,12 +1308,41 @@ mod tests {
|
||||
let tf = parse_test("; before
|
||||
test cfg option=5
|
||||
test verify
|
||||
set enable_float=false
|
||||
function comment() {}")
|
||||
.unwrap();
|
||||
assert_eq!(tf.commands.len(), 2);
|
||||
assert_eq!(tf.commands[0].command, "cfg");
|
||||
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[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::entities::AnyEntity;
|
||||
use testcommand::TestCommand;
|
||||
use isaspec::IsaSpec;
|
||||
use sourcemap::SourceMap;
|
||||
use error::Location;
|
||||
|
||||
/// A parsed test case.
|
||||
///
|
||||
/// This is the result of parsing a `.cton` file which contains a number of test commands followed
|
||||
/// by the functions that should be tested.
|
||||
#[derive(Debug)]
|
||||
/// This is the result of parsing a `.cton` file which contains a number of test commands and ISA
|
||||
/// specs followed by the functions that should be tested.
|
||||
pub struct TestFile<'a> {
|
||||
/// `test foo ...` lines.
|
||||
pub commands: Vec<TestCommand<'a>>,
|
||||
/// `isa bar ...` lines.
|
||||
pub isa_spec: IsaSpec,
|
||||
pub functions: Vec<(Function, Details<'a>)>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user