Pass flags and target ISAs to filetests.
Add a `needs_isa()` method to the SubTest trait, and pass a TargetIsa trait object to those sub-tests that request it. When multiple sub-tests and ISAs are specified, test the cross product. If a sub-test requires an ISA, but none are specified, fail the test. In the future, it may be a good idea to generate a default set of ISAs and test against those.
This commit is contained in:
@@ -91,6 +91,9 @@ pub trait TargetIsa {
|
|||||||
/// Get the name of this ISA.
|
/// Get the name of this ISA.
|
||||||
fn name(&self) -> &'static str;
|
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.
|
/// 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.
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ impl TargetIsa for Isa {
|
|||||||
"riscv"
|
"riscv"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn flags(&self) -> &shared_settings::Flags {
|
||||||
|
&self.shared_flags
|
||||||
|
}
|
||||||
|
|
||||||
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(),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub use error::{Location, Result, Error};
|
|||||||
pub use parser::{parse_functions, parse_test};
|
pub use parser::{parse_functions, parse_test};
|
||||||
pub use testcommand::{TestCommand, TestOption};
|
pub use testcommand::{TestCommand, TestOption};
|
||||||
pub use testfile::{TestFile, Details};
|
pub use testfile::{TestFile, Details};
|
||||||
|
pub use isaspec::IsaSpec;
|
||||||
pub use sourcemap::SourceMap;
|
pub use sourcemap::SourceMap;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
//! Run the tests in a single test file.
|
//! Run the tests in a single test file.
|
||||||
|
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::Cow;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time;
|
use std::time;
|
||||||
use cretonne::ir::Function;
|
use cretonne::ir::Function;
|
||||||
|
use cretonne::isa::TargetIsa;
|
||||||
|
use cretonne::settings::Flags;
|
||||||
use cretonne::verify_function;
|
use cretonne::verify_function;
|
||||||
use cton_reader::parse_test;
|
use cton_reader::parse_test;
|
||||||
|
use cton_reader::IsaSpec;
|
||||||
use utils::read_to_string;
|
use utils::read_to_string;
|
||||||
use filetest::{TestResult, new_subtest};
|
use filetest::{TestResult, new_subtest};
|
||||||
use filetest::subtest::{SubTest, Context, Result};
|
use filetest::subtest::{SubTest, Context, Result};
|
||||||
@@ -20,16 +23,27 @@ pub fn run(path: &Path) -> TestResult {
|
|||||||
if testfile.functions.is_empty() {
|
if testfile.functions.is_empty() {
|
||||||
return Err("no functions found".to_string());
|
return Err("no functions found".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the test commands.
|
// Parse the test commands.
|
||||||
let mut tests = try!(testfile.commands.iter().map(new_subtest).collect::<Result<Vec<_>>>());
|
let mut tests = try!(testfile.commands.iter().map(new_subtest).collect::<Result<Vec<_>>>());
|
||||||
|
|
||||||
|
// 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
|
// Sort the tests so the mutators are at the end, and those that don't need the verifier are at
|
||||||
// the front.
|
// the front.
|
||||||
tests.sort_by_key(|st| (st.is_mutating(), st.needs_verifier()));
|
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.
|
// Isolate the last test in the hope that this is the only mutating test.
|
||||||
// If so, we can completely avoid cloning functions.
|
// 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()),
|
None => return Err("no test commands found".to_string()),
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
};
|
};
|
||||||
@@ -38,14 +52,16 @@ pub fn run(path: &Path) -> TestResult {
|
|||||||
let mut context = Context {
|
let mut context = Context {
|
||||||
details: details,
|
details: details,
|
||||||
verified: false,
|
verified: false,
|
||||||
|
flags: flags,
|
||||||
|
isa: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for test in &tests {
|
for tuple in &tuples {
|
||||||
try!(run_one_test(test.borrow(), Cow::Borrowed(&func), &mut context));
|
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
|
// Run the last test with an owned function which means it won't need to clone it before
|
||||||
// mutating.
|
// 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())
|
Ok(started.elapsed())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_one_test(test: &SubTest, func: Cow<Function>, context: &mut Context) -> Result<()> {
|
// Given a slice of tests, generate a vector of (test, flags, isa) tuples.
|
||||||
|
fn test_tuples<'a>(tests: &'a [Box<SubTest>],
|
||||||
|
isa_spec: &'a IsaSpec,
|
||||||
|
no_isa_flags: &'a Flags)
|
||||||
|
-> Result<Vec<(&'a SubTest, &'a Flags, Option<&'a TargetIsa>)>> {
|
||||||
|
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<Function>,
|
||||||
|
context: &mut Context<'a>)
|
||||||
|
-> Result<()> {
|
||||||
|
let (test, flags, isa) = tuple;
|
||||||
let name = format!("{}({})", test.name(), func.name);
|
let name = format!("{}({})", test.name(), func.name);
|
||||||
|
|
||||||
|
context.flags = flags;
|
||||||
|
context.isa = isa;
|
||||||
|
|
||||||
// Should we run the verifier before this test?
|
// Should we run the verifier before this test?
|
||||||
if !context.verified && test.needs_verifier() {
|
if !context.verified && test.needs_verifier() {
|
||||||
try!(verify_function(&func).map_err(|e| e.to_string()));
|
try!(verify_function(&func).map_err(|e| e.to_string()));
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
use std::result;
|
use std::result;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use cretonne::ir::Function;
|
use cretonne::ir::Function;
|
||||||
|
use cretonne::isa::TargetIsa;
|
||||||
|
use cretonne::settings::Flags;
|
||||||
use cton_reader::Details;
|
use cton_reader::Details;
|
||||||
use filecheck::{self, CheckerBuilder, Checker, Value as FCValue};
|
use filecheck::{self, CheckerBuilder, Checker, Value as FCValue};
|
||||||
|
|
||||||
@@ -15,6 +17,13 @@ pub struct Context<'a> {
|
|||||||
|
|
||||||
/// Was the function verified before running this test?
|
/// Was the function verified before running this test?
|
||||||
pub verified: bool,
|
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.
|
/// Common interface for implementations of test commands.
|
||||||
@@ -36,6 +45,11 @@ pub trait SubTest {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does this test need a `TargetIsa` trait object?
|
||||||
|
fn needs_isa(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Run this test on `func`.
|
/// Run this test on `func`.
|
||||||
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()>;
|
fn run(&self, func: Cow<Function>, context: &Context) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user