From 36b143df995bc5ef27720b0ef70a053f5b623ddb Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 20 Sep 2016 13:38:36 -0700 Subject: [PATCH] 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. --- src/libcretonne/isa/mod.rs | 3 ++ src/libcretonne/isa/riscv/mod.rs | 4 ++ src/libreader/isaspec.rs | 49 +++++++++++++++++ src/libreader/lib.rs | 1 + src/libreader/parser.rs | 91 ++++++++++++++++++++++++++++++++ src/libreader/testfile.rs | 9 ++-- 6 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 src/libreader/isaspec.rs diff --git a/src/libcretonne/isa/mod.rs b/src/libcretonne/isa/mod.rs index a43b1a5cb4..fbde0208e6 100644 --- a/src/libcretonne/isa/mod.rs +++ b/src/libcretonne/isa/mod.rs @@ -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. diff --git a/src/libcretonne/isa/riscv/mod.rs b/src/libcretonne/isa/riscv/mod.rs index ea49358aa6..62bca868a6 100644 --- a/src/libcretonne/isa/riscv/mod.rs +++ b/src/libcretonne/isa/riscv/mod.rs @@ -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 { lookup_enclist(inst.first_type(), inst.opcode(), diff --git a/src/libreader/isaspec.rs b/src/libreader/isaspec.rs new file mode 100644 index 0000000000..706c081e3a --- /dev/null +++ b/src/libreader/isaspec.rs @@ -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>), +} + +/// 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 +{ + 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(()) +} diff --git a/src/libreader/lib.rs b/src/libreader/lib.rs index f0f04fa381..da3cb35685 100644 --- a/src/libreader/lib.rs +++ b/src/libreader/lib.rs @@ -15,5 +15,6 @@ mod error; mod lexer; mod parser; mod testcommand; +mod isaspec; mod testfile; mod sourcemap; diff --git a/src/libreader/parser.rs b/src/libreader/parser.rs index 6f65cbafa3..05fa9b831b 100644 --- a/src/libreader/parser.rs +++ b/src/libreader/parser.rs @@ -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> { 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 { + // 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"); + } + } + } } diff --git a/src/libreader/testfile.rs b/src/libreader/testfile.rs index d63b1f9f9d..30ed59074b 100644 --- a/src/libreader/testfile.rs +++ b/src/libreader/testfile.rs @@ -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>, + /// `isa bar ...` lines. + pub isa_spec: IsaSpec, pub functions: Vec<(Function, Details<'a>)>, }