diff --git a/cranelift/src/libcretonne/isa/mod.rs b/cranelift/src/libcretonne/isa/mod.rs index fbde0208e6..67d5a91c29 100644 --- a/cranelift/src/libcretonne/isa/mod.rs +++ b/cranelift/src/libcretonne/isa/mod.rs @@ -91,6 +91,9 @@ pub trait TargetIsa { /// Get the name of this ISA. fn name(&self) -> &'static str; + /// Get the ISA-independent flags that were used to make this trait object. + fn flags(&self) -> &settings::Flags; + /// 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/cranelift/src/libcretonne/isa/riscv/mod.rs b/cranelift/src/libcretonne/isa/riscv/mod.rs index 62bca868a6..2ff352918d 100644 --- a/cranelift/src/libcretonne/isa/riscv/mod.rs +++ b/cranelift/src/libcretonne/isa/riscv/mod.rs @@ -43,6 +43,10 @@ impl TargetIsa for Isa { "riscv" } + fn flags(&self) -> &shared_settings::Flags { + &self.shared_flags + } + fn encode(&self, _: &DataFlowGraph, inst: &InstructionData) -> Option { lookup_enclist(inst.first_type(), inst.opcode(), diff --git a/cranelift/src/libreader/lib.rs b/cranelift/src/libreader/lib.rs index da3cb35685..ef81e7f210 100644 --- a/cranelift/src/libreader/lib.rs +++ b/cranelift/src/libreader/lib.rs @@ -9,6 +9,7 @@ pub use error::{Location, Result, Error}; pub use parser::{parse_functions, parse_test}; pub use testcommand::{TestCommand, TestOption}; pub use testfile::{TestFile, Details}; +pub use isaspec::IsaSpec; pub use sourcemap::SourceMap; mod error; diff --git a/cranelift/src/tools/filetest/runone.rs b/cranelift/src/tools/filetest/runone.rs index e0d1e9d17b..d0199c45cf 100644 --- a/cranelift/src/tools/filetest/runone.rs +++ b/cranelift/src/tools/filetest/runone.rs @@ -1,11 +1,14 @@ //! Run the tests in a single test file. -use std::borrow::{Borrow, Cow}; +use std::borrow::Cow; use std::path::Path; use std::time; use cretonne::ir::Function; +use cretonne::isa::TargetIsa; +use cretonne::settings::Flags; use cretonne::verify_function; use cton_reader::parse_test; +use cton_reader::IsaSpec; use utils::read_to_string; use filetest::{TestResult, new_subtest}; use filetest::subtest::{SubTest, Context, Result}; @@ -20,16 +23,27 @@ pub fn run(path: &Path) -> TestResult { if testfile.functions.is_empty() { return Err("no functions found".to_string()); } + // Parse the test commands. let mut tests = try!(testfile.commands.iter().map(new_subtest).collect::>>()); + // Flags to use for those tests that don't need an ISA. + // This is the cumulative effect of all the `set` commands in the file. + let flags = match testfile.isa_spec { + IsaSpec::None(ref f) => f, + IsaSpec::Some(ref v) => v.last().expect("Empty ISA list").flags(), + }; + // Sort the tests so the mutators are at the end, and those that don't need the verifier are at // the front. tests.sort_by_key(|st| (st.is_mutating(), st.needs_verifier())); + // Expand the tests into (test, flags, isa) tuples. + let mut tuples = try!(test_tuples(&tests, &testfile.isa_spec, flags)); + // Isolate the last test in the hope that this is the only mutating test. // If so, we can completely avoid cloning functions. - let last_test = match tests.pop() { + let last_tuple = match tuples.pop() { None => return Err("no test commands found".to_string()), Some(t) => t, }; @@ -38,14 +52,16 @@ pub fn run(path: &Path) -> TestResult { let mut context = Context { details: details, verified: false, + flags: flags, + isa: None, }; - for test in &tests { - try!(run_one_test(test.borrow(), Cow::Borrowed(&func), &mut context)); + for tuple in &tuples { + try!(run_one_test(*tuple, Cow::Borrowed(&func), &mut context)); } // Run the last test with an owned function which means it won't need to clone it before // mutating. - try!(run_one_test(last_test.borrow(), Cow::Owned(func), &mut context)); + try!(run_one_test(last_tuple, Cow::Owned(func), &mut context)); } @@ -53,9 +69,42 @@ pub fn run(path: &Path) -> TestResult { Ok(started.elapsed()) } -fn run_one_test(test: &SubTest, func: Cow, context: &mut Context) -> Result<()> { +// Given a slice of tests, generate a vector of (test, flags, isa) tuples. +fn test_tuples<'a>(tests: &'a [Box], + isa_spec: &'a IsaSpec, + no_isa_flags: &'a Flags) + -> Result)>> { + let mut out = Vec::new(); + for test in tests { + if test.needs_isa() { + match *isa_spec { + IsaSpec::None(_) => { + // TODO: Generate a list of default ISAs. + return Err(format!("test {} requires an ISA", test.name())); + } + IsaSpec::Some(ref isas) => { + for isa in isas { + out.push((&**test, isa.flags(), Some(&**isa))); + } + } + } + } else { + out.push((&**test, no_isa_flags, None)); + } + } + Ok(out) +} + +fn run_one_test<'a>(tuple: (&'a SubTest, &'a Flags, Option<&'a TargetIsa>), + func: Cow, + context: &mut Context<'a>) + -> Result<()> { + let (test, flags, isa) = tuple; let name = format!("{}({})", test.name(), func.name); + context.flags = flags; + context.isa = isa; + // Should we run the verifier before this test? if !context.verified && test.needs_verifier() { try!(verify_function(&func).map_err(|e| e.to_string())); diff --git a/cranelift/src/tools/filetest/subtest.rs b/cranelift/src/tools/filetest/subtest.rs index b958193cd8..fee9bbef85 100644 --- a/cranelift/src/tools/filetest/subtest.rs +++ b/cranelift/src/tools/filetest/subtest.rs @@ -3,6 +3,8 @@ use std::result; use std::borrow::Cow; use cretonne::ir::Function; +use cretonne::isa::TargetIsa; +use cretonne::settings::Flags; use cton_reader::Details; use filecheck::{self, CheckerBuilder, Checker, Value as FCValue}; @@ -15,6 +17,13 @@ pub struct Context<'a> { /// Was the function verified before running this test? pub verified: bool, + + /// ISA-independent flags for this test. + pub flags: &'a Flags, + + /// Target ISA to test against. Only present for sub-tests whose `needs_isa` method returned + /// true. + pub isa: Option<&'a TargetIsa>, } /// Common interface for implementations of test commands. @@ -36,6 +45,11 @@ pub trait SubTest { false } + /// Does this test need a `TargetIsa` trait object? + fn needs_isa(&self) -> bool { + false + } + /// Run this test on `func`. fn run(&self, func: Cow, context: &Context) -> Result<()>; }