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:
Jakob Stoklund Olesen
2016-09-20 13:38:36 -07:00
parent 83adf341ec
commit 36b143df99
6 changed files with 154 additions and 3 deletions

View File

@@ -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.

View File

@@ -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
View 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(())
}

View File

@@ -15,5 +15,6 @@ mod error;
mod lexer;
mod parser;
mod testcommand;
mod isaspec;
mod testfile;
mod sourcemap;

View File

@@ -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");
}
}
}
}

View File

@@ -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>)>,
}