clif-util: Use anyhow::Error for errors instead of String
Also does the same for `cranelift-filetests`.
This commit is contained in:
@@ -25,6 +25,7 @@ memmap = "0.7.0"
|
||||
num_cpus = "1.8.0"
|
||||
target-lexicon = "0.10"
|
||||
thiserror = "1.0.15"
|
||||
anyhow = "1.0.32"
|
||||
|
||||
[features]
|
||||
enable-peepmatic = []
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
//! concurrently.
|
||||
|
||||
use crate::runone;
|
||||
use crate::TestResult;
|
||||
use cranelift_codegen::dbg::LOG_FILENAME_PREFIX;
|
||||
use cranelift_codegen::timing;
|
||||
use file_per_thread_logger;
|
||||
@@ -22,8 +21,14 @@ struct Request(usize, PathBuf);
|
||||
|
||||
/// Reply from worker thread,
|
||||
pub enum Reply {
|
||||
Starting { jobid: usize, thread_num: usize },
|
||||
Done { jobid: usize, result: TestResult },
|
||||
Starting {
|
||||
jobid: usize,
|
||||
thread_num: usize,
|
||||
},
|
||||
Done {
|
||||
jobid: usize,
|
||||
result: anyhow::Result<Duration>,
|
||||
},
|
||||
Tick,
|
||||
}
|
||||
|
||||
@@ -147,11 +152,11 @@ fn worker_thread(
|
||||
// The test panicked, leaving us a `Box<Any>`.
|
||||
// Panics are usually strings.
|
||||
if let Some(msg) = e.downcast_ref::<String>() {
|
||||
Err(format!("panicked in worker #{}: {}", thread_num, msg))
|
||||
anyhow::bail!("panicked in worker #{}: {}", thread_num, msg)
|
||||
} else if let Some(msg) = e.downcast_ref::<&'static str>() {
|
||||
Err(format!("panicked in worker #{}: {}", thread_num, msg))
|
||||
anyhow::bail!("panicked in worker #{}: {}", thread_num, msg)
|
||||
} else {
|
||||
Err(format!("panicked in worker #{}", thread_num))
|
||||
anyhow::bail!("panicked in worker #{}", thread_num)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -60,9 +60,6 @@ mod test_stack_maps;
|
||||
mod test_unwind;
|
||||
mod test_verifier;
|
||||
|
||||
/// The result of running the test in a file.
|
||||
type TestResult = Result<time::Duration, String>;
|
||||
|
||||
/// Main entry point for `clif-util test`.
|
||||
///
|
||||
/// Take a list of filenames which can be either `.clif` files or directories.
|
||||
@@ -72,7 +69,7 @@ type TestResult = Result<time::Duration, String>;
|
||||
/// Directories are scanned recursively for test cases ending in `.clif`. These test cases are
|
||||
/// executed on background threads.
|
||||
///
|
||||
pub fn run(verbose: bool, report_times: bool, files: &[String]) -> TestResult {
|
||||
pub fn run(verbose: bool, report_times: bool, files: &[String]) -> anyhow::Result<time::Duration> {
|
||||
let mut runner = TestRunner::new(verbose, report_times);
|
||||
|
||||
for path in files.iter().map(Path::new) {
|
||||
@@ -98,7 +95,7 @@ pub fn run_passes(
|
||||
passes: &[String],
|
||||
target: &str,
|
||||
file: &str,
|
||||
) -> TestResult {
|
||||
) -> anyhow::Result<time::Duration> {
|
||||
let mut runner = TestRunner::new(verbose, /* report_times */ false);
|
||||
|
||||
let path = Path::new(file);
|
||||
@@ -119,7 +116,7 @@ pub fn run_passes(
|
||||
///
|
||||
/// This function knows how to create all of the possible `test <foo>` commands that can appear in
|
||||
/// a `.clif` test file.
|
||||
fn new_subtest(parsed: &TestCommand) -> subtest::SubtestResult<Box<dyn subtest::SubTest>> {
|
||||
fn new_subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn subtest::SubTest>> {
|
||||
match parsed.command {
|
||||
"binemit" => test_binemit::subtest(parsed),
|
||||
"cat" => test_cat::subtest(parsed),
|
||||
@@ -143,6 +140,15 @@ fn new_subtest(parsed: &TestCommand) -> subtest::SubtestResult<Box<dyn subtest::
|
||||
"stack_maps" => test_stack_maps::subtest(parsed),
|
||||
"unwind" => test_unwind::subtest(parsed),
|
||||
"verifier" => test_verifier::subtest(parsed),
|
||||
_ => Err(format!("unknown test command '{}'", parsed.command)),
|
||||
_ => anyhow::bail!("unknown test command '{}'", parsed.command),
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_anyhow_error(
|
||||
func: &cranelift_codegen::ir::Function,
|
||||
isa: Option<&dyn cranelift_codegen::isa::TargetIsa>,
|
||||
err: cranelift_codegen::CodegenError,
|
||||
) -> anyhow::Error {
|
||||
let s = cranelift_codegen::print_errors::pretty_error(func, isa, err);
|
||||
anyhow::anyhow!("{}", s)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
use crate::concurrent::{ConcurrentRunner, Reply};
|
||||
use crate::runone;
|
||||
use crate::TestResult;
|
||||
use cranelift_codegen::timing;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
@@ -24,12 +23,12 @@ struct QueueEntry {
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
New,
|
||||
Queued,
|
||||
Running,
|
||||
Done(TestResult),
|
||||
Done(anyhow::Result<time::Duration>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
@@ -205,7 +204,7 @@ impl TestRunner {
|
||||
/// Schedule any new jobs to run.
|
||||
fn schedule_jobs(&mut self) {
|
||||
for jobid in self.new_tests..self.tests.len() {
|
||||
assert_eq!(self.tests[jobid].state, State::New);
|
||||
assert!(matches!(self.tests[jobid].state, State::New));
|
||||
if let Some(ref mut conc) = self.threads {
|
||||
// Queue test for concurrent execution.
|
||||
self.tests[jobid].state = State::Queued;
|
||||
@@ -228,7 +227,7 @@ impl TestRunner {
|
||||
/// Schedule any new job to run for the pass command.
|
||||
fn schedule_pass_job(&mut self, passes: &[String], target: &str) {
|
||||
self.tests[0].state = State::Running;
|
||||
let result: Result<time::Duration, String>;
|
||||
let result: anyhow::Result<time::Duration>;
|
||||
|
||||
let specified_target = match target {
|
||||
"" => None,
|
||||
@@ -240,8 +239,8 @@ impl TestRunner {
|
||||
}
|
||||
|
||||
/// Report the end of a job.
|
||||
fn finish_job(&mut self, jobid: usize, result: TestResult) {
|
||||
assert_eq!(self.tests[jobid].state, State::Running);
|
||||
fn finish_job(&mut self, jobid: usize, result: anyhow::Result<time::Duration>) {
|
||||
assert!(matches!(self.tests[jobid].state, State::Running));
|
||||
if result.is_err() {
|
||||
self.errors += 1;
|
||||
}
|
||||
@@ -257,7 +256,7 @@ impl TestRunner {
|
||||
fn handle_reply(&mut self, reply: Reply) {
|
||||
match reply {
|
||||
Reply::Starting { jobid, .. } => {
|
||||
assert_eq!(self.tests[jobid].state, State::Queued);
|
||||
assert!(matches!(self.tests[jobid].state, State::Queued));
|
||||
self.tests[jobid].state = State::Running;
|
||||
}
|
||||
Reply::Done { jobid, result } => {
|
||||
@@ -274,7 +273,7 @@ impl TestRunner {
|
||||
self.tests.len()
|
||||
);
|
||||
for jobid in self.reported_tests..self.tests.len() {
|
||||
if self.tests[jobid].state == State::Running {
|
||||
if let State::Running = self.tests[jobid].state {
|
||||
println!("slow: {}", self.tests[jobid]);
|
||||
}
|
||||
}
|
||||
@@ -356,7 +355,7 @@ impl TestRunner {
|
||||
}
|
||||
|
||||
/// Scan pushed directories for tests and run them.
|
||||
pub fn run(&mut self) -> TestResult {
|
||||
pub fn run(&mut self) -> anyhow::Result<time::Duration> {
|
||||
let started = time::Instant::now();
|
||||
self.scan_dirs(IsPass::NotPass);
|
||||
self.schedule_jobs();
|
||||
@@ -366,13 +365,17 @@ impl TestRunner {
|
||||
println!("{} tests", self.tests.len());
|
||||
match self.errors {
|
||||
0 => Ok(started.elapsed()),
|
||||
1 => Err("1 failure".to_string()),
|
||||
n => Err(format!("{} failures", n)),
|
||||
1 => anyhow::bail!("1 failure"),
|
||||
n => anyhow::bail!("{} failures", n),
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan pushed directories for tests and run specified passes from commandline on them.
|
||||
pub fn run_passes(&mut self, passes: &[String], target: &str) -> TestResult {
|
||||
pub fn run_passes(
|
||||
&mut self,
|
||||
passes: &[String],
|
||||
target: &str,
|
||||
) -> anyhow::Result<time::Duration> {
|
||||
let started = time::Instant::now();
|
||||
self.scan_dirs(IsPass::Pass);
|
||||
self.schedule_pass_job(passes, target);
|
||||
@@ -381,8 +384,8 @@ impl TestRunner {
|
||||
println!("{} tests", self.tests.len());
|
||||
match self.errors {
|
||||
0 => Ok(started.elapsed()),
|
||||
1 => Err("1 failure".to_string()),
|
||||
n => Err(format!("{} failures", n)),
|
||||
1 => anyhow::bail!("1 failure"),
|
||||
n => anyhow::bail!("{} failures", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! Run the tests in a single test file.
|
||||
|
||||
use crate::subtest::{Context, SubTest, SubtestResult};
|
||||
use crate::{new_subtest, TestResult};
|
||||
use crate::new_subtest;
|
||||
use crate::subtest::{Context, SubTest};
|
||||
use anyhow::Context as _;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::print_errors::pretty_verifier_error;
|
||||
@@ -18,11 +19,16 @@ use std::time;
|
||||
/// Load `path` and run the test in it.
|
||||
///
|
||||
/// If running this test causes a panic, it will propagate as normal.
|
||||
pub fn run(path: &Path, passes: Option<&[String]>, target: Option<&str>) -> TestResult {
|
||||
pub fn run(
|
||||
path: &Path,
|
||||
passes: Option<&[String]>,
|
||||
target: Option<&str>,
|
||||
) -> anyhow::Result<time::Duration> {
|
||||
let _tt = timing::process_file();
|
||||
info!("---\nFile: {}", path.to_string_lossy());
|
||||
let started = time::Instant::now();
|
||||
let buffer = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
||||
let buffer =
|
||||
fs::read_to_string(path).with_context(|| format!("failed to read {}", path.display()))?;
|
||||
let options = ParseOptions {
|
||||
target,
|
||||
passes,
|
||||
@@ -39,12 +45,14 @@ pub fn run(path: &Path, passes: Option<&[String]>, target: Option<&str>) -> Test
|
||||
);
|
||||
return Ok(started.elapsed());
|
||||
}
|
||||
return Err(e.to_string());
|
||||
return Err(e)
|
||||
.context(format!("failed to parse {}", path.display()))
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
if testfile.functions.is_empty() {
|
||||
return Err("no functions found".to_string());
|
||||
anyhow::bail!("no functions found");
|
||||
}
|
||||
|
||||
// Parse the test commands.
|
||||
@@ -52,7 +60,7 @@ pub fn run(path: &Path, passes: Option<&[String]>, target: Option<&str>) -> Test
|
||||
.commands
|
||||
.iter()
|
||||
.map(new_subtest)
|
||||
.collect::<SubtestResult<Vec<_>>>()?;
|
||||
.collect::<anyhow::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.
|
||||
@@ -71,7 +79,7 @@ pub fn run(path: &Path, passes: Option<&[String]>, target: Option<&str>) -> Test
|
||||
// Isolate the last test in the hope that this is the only mutating test.
|
||||
// If so, we can completely avoid cloning functions.
|
||||
let last_tuple = match tuples.pop() {
|
||||
None => return Err("no test commands found".to_string()),
|
||||
None => anyhow::bail!("no test commands found"),
|
||||
Some(t) => t,
|
||||
};
|
||||
|
||||
@@ -102,14 +110,14 @@ fn test_tuples<'a>(
|
||||
tests: &'a [Box<dyn SubTest>],
|
||||
isa_spec: &'a IsaSpec,
|
||||
no_isa_flags: &'a Flags,
|
||||
) -> SubtestResult<Vec<(&'a dyn SubTest, &'a Flags, Option<&'a dyn TargetIsa>)>> {
|
||||
) -> anyhow::Result<Vec<(&'a dyn SubTest, &'a Flags, Option<&'a dyn 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()));
|
||||
anyhow::bail!("test {} requires an ISA", test.name());
|
||||
}
|
||||
IsaSpec::Some(ref isas) => {
|
||||
for isa in isas {
|
||||
@@ -131,7 +139,7 @@ fn run_one_test<'a>(
|
||||
tuple: (&'a dyn SubTest, &'a Flags, Option<&'a dyn TargetIsa>),
|
||||
func: Cow<Function>,
|
||||
context: &mut Context<'a>,
|
||||
) -> SubtestResult<()> {
|
||||
) -> anyhow::Result<()> {
|
||||
let (test, flags, isa) = tuple;
|
||||
let name = format!("{}({})", test.name(), func.name);
|
||||
info!("Test: {} {}", name, isa.map_or("-", TargetIsa::name));
|
||||
@@ -141,11 +149,12 @@ fn run_one_test<'a>(
|
||||
|
||||
// Should we run the verifier before this test?
|
||||
if !context.verified && test.needs_verifier() {
|
||||
verify_function(&func, context.flags_or_isa())
|
||||
.map_err(|errors| pretty_verifier_error(&func, isa, None, errors))?;
|
||||
verify_function(&func, context.flags_or_isa()).map_err(|errors| {
|
||||
anyhow::anyhow!("{}", pretty_verifier_error(&func, isa, None, errors))
|
||||
})?;
|
||||
context.verified = true;
|
||||
}
|
||||
|
||||
test.run(func, context)
|
||||
.map_err(|e| format!("{}:\n{}", name, e))
|
||||
test.run(func, context).context(test.name())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! `SubTest` trait.
|
||||
|
||||
use anyhow::Context as _;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::settings::{Flags, FlagsOrIsa};
|
||||
@@ -7,8 +8,6 @@ use cranelift_reader::{Comment, Details};
|
||||
use filecheck::{Checker, CheckerBuilder, NO_VARIABLES};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub type SubtestResult<T> = Result<T, String>;
|
||||
|
||||
/// Context for running a test on a single function.
|
||||
pub struct Context<'a> {
|
||||
/// Comments from the preamble f the test file. These apply to all functions.
|
||||
@@ -66,39 +65,39 @@ pub trait SubTest {
|
||||
}
|
||||
|
||||
/// Run this test on `func`.
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()>;
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
/// Run filecheck on `text`, using directives extracted from `context`.
|
||||
pub fn run_filecheck(text: &str, context: &Context) -> SubtestResult<()> {
|
||||
pub fn run_filecheck(text: &str, context: &Context) -> anyhow::Result<()> {
|
||||
let checker = build_filechecker(context)?;
|
||||
if checker
|
||||
.check(text, NO_VARIABLES)
|
||||
.map_err(|e| format!("filecheck: {}", e))?
|
||||
.context("filecheck failed")?
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
// Filecheck mismatch. Emit an explanation as output.
|
||||
let (_, explain) = checker
|
||||
.explain(text, NO_VARIABLES)
|
||||
.map_err(|e| format!("explain: {}", e))?;
|
||||
Err(format!("filecheck failed:\n{}{}", checker, explain))
|
||||
.context("filecheck explain failed")?;
|
||||
anyhow::bail!("filecheck failed:\n{}{}", checker, explain);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a filechecker using the directives in the file preamble and the function's comments.
|
||||
pub fn build_filechecker(context: &Context) -> SubtestResult<Checker> {
|
||||
pub fn build_filechecker(context: &Context) -> anyhow::Result<Checker> {
|
||||
let mut builder = CheckerBuilder::new();
|
||||
// Preamble comments apply to all functions.
|
||||
for comment in context.preamble_comments {
|
||||
builder
|
||||
.directive(comment.text)
|
||||
.map_err(|e| format!("filecheck: {}", e))?;
|
||||
.context("filecheck directive failed")?;
|
||||
}
|
||||
for comment in &context.details.comments {
|
||||
builder
|
||||
.directive(comment.text)
|
||||
.map_err(|e| format!("filecheck: {}", e))?;
|
||||
.context("filecheck directive failed")?;
|
||||
}
|
||||
Ok(builder.finish())
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! functions and compares the results to the expected output.
|
||||
|
||||
use crate::match_directive::match_directive;
|
||||
use crate::subtest::{Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{Context, SubTest};
|
||||
use cranelift_codegen::binemit::{self, CodeInfo, CodeSink, RegDiversions};
|
||||
use cranelift_codegen::dbg::DisplayList;
|
||||
use cranelift_codegen::dominator_tree::DominatorTree;
|
||||
@@ -12,7 +12,6 @@ use cranelift_codegen::flowgraph::ControlFlowGraph;
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_codegen::ir::entities::AnyEntity;
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::settings::OptLevel;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
@@ -21,10 +20,10 @@ use std::fmt::Write;
|
||||
|
||||
struct TestBinEmit;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "binemit");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
anyhow::bail!("No options allowed on {}", parsed)
|
||||
} else {
|
||||
Ok(Box::new(TestBinEmit))
|
||||
}
|
||||
@@ -130,7 +129,7 @@ impl SubTest for TestBinEmit {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("binemit needs an ISA");
|
||||
let encinfo = isa.encoding_info();
|
||||
// TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
|
||||
@@ -187,7 +186,7 @@ impl SubTest for TestBinEmit {
|
||||
let mut domtree = DominatorTree::with_function(&func, &cfg);
|
||||
let CodeInfo { total_size, .. } =
|
||||
binemit::relax_branches(&mut func, &mut cfg, &mut domtree, isa)
|
||||
.map_err(|e| pretty_error(&func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&func, context.isa, e))?;
|
||||
|
||||
// Collect all of the 'bin:' directives on instructions.
|
||||
let mut bins = HashMap::new();
|
||||
@@ -196,25 +195,26 @@ impl SubTest for TestBinEmit {
|
||||
match comment.entity {
|
||||
AnyEntity::Inst(inst) => {
|
||||
if let Some(prev) = bins.insert(inst, want) {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"multiple 'bin:' directives on {}: '{}' and '{}'",
|
||||
func.dfg.display_inst(inst, isa),
|
||||
prev,
|
||||
want
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"'bin:' directive on non-inst {}: {}",
|
||||
comment.entity, comment.text
|
||||
));
|
||||
comment.entity,
|
||||
comment.text
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if bins.is_empty() {
|
||||
return Err("No 'bin:' directives found".to_string());
|
||||
anyhow::bail!("No 'bin:' directives found");
|
||||
}
|
||||
|
||||
// Now emit all instructions.
|
||||
@@ -265,26 +265,26 @@ impl SubTest for TestBinEmit {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if encodings.is_empty() {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"No encodings found for: {}",
|
||||
func.dfg.display_inst(inst, isa)
|
||||
));
|
||||
);
|
||||
}
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"No matching encodings for {} in {}",
|
||||
func.dfg.display_inst(inst, isa),
|
||||
DisplayList(&encodings),
|
||||
));
|
||||
);
|
||||
}
|
||||
let have = sink.text.trim();
|
||||
if have != want {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"Bad machine code for {}: {}\nWant: {}\nGot: {}",
|
||||
inst,
|
||||
func.dfg.display_inst(inst, isa),
|
||||
want,
|
||||
have
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,10 +312,7 @@ impl SubTest for TestBinEmit {
|
||||
sink.end_codegen();
|
||||
|
||||
if sink.offset != total_size {
|
||||
return Err(format!(
|
||||
"Expected code size {}, got {}",
|
||||
total_size, sink.offset
|
||||
));
|
||||
anyhow::bail!("Expected code size {}, got {}", total_size, sink.offset);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -328,7 +325,7 @@ fn validate_location_annotations(
|
||||
inst: ir::Inst,
|
||||
isa: &dyn isa::TargetIsa,
|
||||
validate_inputs: bool,
|
||||
) -> SubtestResult<()> {
|
||||
) -> anyhow::Result<()> {
|
||||
let values = if validate_inputs {
|
||||
func.dfg.inst_args(inst)
|
||||
} else {
|
||||
@@ -336,12 +333,11 @@ fn validate_location_annotations(
|
||||
};
|
||||
|
||||
if let Some(&v) = values.iter().find(|&&v| !func.locations[v].is_assigned()) {
|
||||
Err(format!(
|
||||
anyhow::bail!(
|
||||
"Need register/stack slot annotation for {} in {}",
|
||||
v,
|
||||
func.dfg.display_inst(inst, isa)
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The `cat` subtest.
|
||||
|
||||
use crate::subtest::{self, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{self, Context, SubTest};
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
@@ -13,13 +13,12 @@ use std::borrow::Cow;
|
||||
/// The result is verified by filecheck.
|
||||
struct TestCat;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "cat");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestCat))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestCat))
|
||||
}
|
||||
|
||||
impl SubTest for TestCat {
|
||||
@@ -31,7 +30,7 @@ impl SubTest for TestCat {
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
subtest::run_filecheck(&func.display(context.isa).to_string(), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,25 +2,23 @@
|
||||
//!
|
||||
//! The `compile` test command runs each function through the full code generator pipeline
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::binemit::{self, CodeInfo};
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use log::info;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestCompile;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "compile");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestCompile))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestCompile))
|
||||
}
|
||||
|
||||
impl SubTest for TestCompile {
|
||||
@@ -36,7 +34,7 @@ impl SubTest for TestCompile {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("compile needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
@@ -47,7 +45,7 @@ impl SubTest for TestCompile {
|
||||
|
||||
let CodeInfo { total_size, .. } = comp_ctx
|
||||
.compile(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
|
||||
info!(
|
||||
"Generated {} bytes of code:\n{}",
|
||||
@@ -66,10 +64,7 @@ impl SubTest for TestCompile {
|
||||
);
|
||||
|
||||
if sink.offset != total_size {
|
||||
return Err(format!(
|
||||
"Expected code size {}, got {}",
|
||||
total_size, sink.offset
|
||||
));
|
||||
anyhow::bail!("Expected code size {}, got {}", total_size, sink.offset);
|
||||
}
|
||||
|
||||
// Run final code through filecheck.
|
||||
|
||||
@@ -5,22 +5,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestDCE;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "dce");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestDCE))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestDCE))
|
||||
}
|
||||
|
||||
impl SubTest for TestDCE {
|
||||
@@ -32,14 +30,14 @@ impl SubTest for TestDCE {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
comp_ctx.flowgraph();
|
||||
comp_ctx.compute_loop_analysis();
|
||||
comp_ctx
|
||||
.dce(context.flags_or_isa())
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
|
||||
let text = comp_ctx.func.display(context.isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//!
|
||||
|
||||
use crate::match_directive::match_directive;
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen::dominator_tree::{DominatorTree, DominatorTreePreorder};
|
||||
use cranelift_codegen::flowgraph::ControlFlowGraph;
|
||||
use cranelift_codegen::ir::entities::AnyEntity;
|
||||
@@ -25,13 +25,12 @@ use std::fmt::{self, Write};
|
||||
|
||||
struct TestDomtree;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "domtree");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestDomtree))
|
||||
anyhow::bail!("No options allowed on {}", parsed)
|
||||
}
|
||||
Ok(Box::new(TestDomtree))
|
||||
}
|
||||
|
||||
impl SubTest for TestDomtree {
|
||||
@@ -40,7 +39,7 @@ impl SubTest for TestDomtree {
|
||||
}
|
||||
|
||||
// Extract our own dominator tree from
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let func = func.borrow();
|
||||
let cfg = ControlFlowGraph::with_function(func);
|
||||
let domtree = DominatorTree::with_function(func, &cfg);
|
||||
@@ -52,38 +51,42 @@ impl SubTest for TestDomtree {
|
||||
let inst = match comment.entity {
|
||||
AnyEntity::Inst(inst) => inst,
|
||||
_ => {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"annotation on non-inst {}: {}",
|
||||
comment.entity, comment.text
|
||||
));
|
||||
comment.entity,
|
||||
comment.text
|
||||
);
|
||||
}
|
||||
};
|
||||
for src_block in tail.split_whitespace() {
|
||||
let block = match context.details.map.lookup_str(src_block) {
|
||||
Some(AnyEntity::Block(block)) => block,
|
||||
_ => return Err(format!("expected defined block, got {}", src_block)),
|
||||
_ => anyhow::bail!("expected defined block, got {}", src_block),
|
||||
};
|
||||
|
||||
// Annotations say that `inst` is the idom of `block`.
|
||||
if expected.insert(block, inst).is_some() {
|
||||
return Err(format!("multiple dominators for {}", src_block));
|
||||
anyhow::bail!("multiple dominators for {}", src_block);
|
||||
}
|
||||
|
||||
// Compare to computed domtree.
|
||||
match domtree.idom(block) {
|
||||
Some(got_inst) if got_inst != inst => {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"mismatching idoms for {}:\n\
|
||||
want: {}, got: {}",
|
||||
src_block, inst, got_inst
|
||||
));
|
||||
src_block,
|
||||
inst,
|
||||
got_inst
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"mismatching idoms for {}:\n\
|
||||
want: {}, got: unreachable",
|
||||
src_block, inst
|
||||
));
|
||||
src_block,
|
||||
inst
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -100,11 +103,12 @@ impl SubTest for TestDomtree {
|
||||
.filter(|block| !expected.contains_key(block))
|
||||
{
|
||||
if let Some(got_inst) = domtree.idom(block) {
|
||||
return Err(format!(
|
||||
anyhow::bail!(
|
||||
"mismatching idoms for renumbered {}:\n\
|
||||
want: unrechable, got: {}",
|
||||
block, got_inst
|
||||
));
|
||||
block,
|
||||
got_inst
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! The `interpret` test command interprets each function on the host machine
|
||||
//! using [RunCommand](cranelift_reader::RunCommand)s.
|
||||
|
||||
use crate::subtest::{Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{Context, SubTest};
|
||||
use cranelift_codegen::{self, ir};
|
||||
use cranelift_interpreter::environment::Environment;
|
||||
use cranelift_interpreter::interpreter::{ControlFlow, Interpreter};
|
||||
@@ -13,13 +13,12 @@ use std::borrow::Cow;
|
||||
|
||||
struct TestInterpret;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "interpret");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestInterpret))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestInterpret))
|
||||
}
|
||||
|
||||
impl SubTest for TestInterpret {
|
||||
@@ -35,26 +34,28 @@ impl SubTest for TestInterpret {
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> anyhow::Result<()> {
|
||||
for comment in context.details.comments.iter() {
|
||||
if let Some(command) =
|
||||
parse_run_command(comment.text, &func.signature).map_err(|e| e.to_string())?
|
||||
{
|
||||
if let Some(command) = parse_run_command(comment.text, &func.signature)? {
|
||||
trace!("Parsed run command: {}", command);
|
||||
|
||||
let mut env = Environment::default();
|
||||
env.add(func.name.to_string(), func.clone().into_owned());
|
||||
let interpreter = Interpreter::new(env);
|
||||
|
||||
command.run(|func_name, args| {
|
||||
// Because we have stored function names with a leading %, we need to re-add it.
|
||||
let func_name = &format!("%{}", func_name);
|
||||
match interpreter.call_by_name(func_name, args) {
|
||||
Ok(ControlFlow::Return(results)) => Ok(results),
|
||||
Ok(_) => panic!("Unexpected returned control flow--this is likely a bug."),
|
||||
Err(t) => Err(t.to_string()),
|
||||
}
|
||||
})?;
|
||||
command
|
||||
.run(|func_name, args| {
|
||||
// Because we have stored function names with a leading %, we need to re-add it.
|
||||
let func_name = &format!("%{}", func_name);
|
||||
match interpreter.call_by_name(func_name, args) {
|
||||
Ok(ControlFlow::Return(results)) => Ok(results),
|
||||
Ok(_) => {
|
||||
panic!("Unexpected returned control flow--this is likely a bug.")
|
||||
}
|
||||
Err(t) => Err(format!("unexpected trap: {:?}", t)),
|
||||
}
|
||||
})
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -3,22 +3,20 @@
|
||||
//! The `test legalizer` test command runs each function through `legalize_function()` and sends
|
||||
//! the result to filecheck.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestLegalizer;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "legalizer");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestLegalizer))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestLegalizer))
|
||||
}
|
||||
|
||||
impl SubTest for TestLegalizer {
|
||||
@@ -34,14 +32,14 @@ impl SubTest for TestLegalizer {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
let isa = context.isa.expect("legalizer needs an ISA");
|
||||
|
||||
comp_ctx.compute_cfg();
|
||||
comp_ctx
|
||||
.legalize(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
|
||||
let text = comp_ctx.func.display(Some(isa)).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -5,22 +5,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestLICM;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "licm");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestLICM))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestLICM))
|
||||
}
|
||||
|
||||
impl SubTest for TestLICM {
|
||||
@@ -32,7 +30,7 @@ impl SubTest for TestLICM {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("LICM needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
@@ -40,7 +38,7 @@ impl SubTest for TestLICM {
|
||||
comp_ctx.compute_loop_analysis();
|
||||
comp_ctx
|
||||
.licm(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
|
||||
let text = comp_ctx.func.display(context.isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
//! Test command for `peepmatic`-generated peephole optimizers.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestPreopt;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "peepmatic");
|
||||
if parsed.options.is_empty() {
|
||||
Ok(Box::new(TestPreopt))
|
||||
} else {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,14 +30,14 @@ impl SubTest for TestPreopt {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
let isa = context.isa.expect("preopt needs an ISA");
|
||||
|
||||
comp_ctx.compute_cfg();
|
||||
comp_ctx
|
||||
.preopt(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
let text = &comp_ctx.func.display(isa).to_string();
|
||||
log::debug!("After peepmatic-based simple_preopt:\n{}", text);
|
||||
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestPostopt;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "postopt");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestPostopt))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestPostopt))
|
||||
}
|
||||
|
||||
impl SubTest for TestPostopt {
|
||||
@@ -29,14 +27,14 @@ impl SubTest for TestPostopt {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
let isa = context.isa.expect("postopt needs an ISA");
|
||||
|
||||
comp_ctx.flowgraph();
|
||||
comp_ctx
|
||||
.postopt(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
|
||||
let text = comp_ctx.func.display(isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -5,23 +5,21 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_preopt::optimize;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestPreopt;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "preopt");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestPreopt))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestPreopt))
|
||||
}
|
||||
|
||||
impl SubTest for TestPreopt {
|
||||
@@ -37,12 +35,12 @@ impl SubTest for TestPreopt {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("compile needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
optimize(&mut comp_ctx, isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
|
||||
let text = comp_ctx.func.display(context.isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::subtest::{self, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{self, Context, SubTest};
|
||||
use cranelift_codegen::cfg_printer::CFGPrinter;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_reader::TestCommand;
|
||||
@@ -13,13 +13,12 @@ use cranelift_reader::TestCommand;
|
||||
/// Object implementing the `test print-cfg` sub-test.
|
||||
struct TestPrintCfg;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "print-cfg");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestPrintCfg))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestPrintCfg))
|
||||
}
|
||||
|
||||
impl SubTest for TestPrintCfg {
|
||||
@@ -31,7 +30,7 @@ impl SubTest for TestPrintCfg {
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
subtest::run_filecheck(&CFGPrinter::new(&func).to_string(), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestRegalloc;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "regalloc");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestRegalloc))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestRegalloc))
|
||||
}
|
||||
|
||||
impl SubTest for TestRegalloc {
|
||||
@@ -36,7 +34,7 @@ impl SubTest for TestRegalloc {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("register allocator needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
@@ -44,11 +42,11 @@ impl SubTest for TestRegalloc {
|
||||
// TODO: Should we have an option to skip legalization?
|
||||
comp_ctx
|
||||
.legalize(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
comp_ctx.compute_domtree();
|
||||
comp_ctx
|
||||
.regalloc(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
|
||||
let text = comp_ctx.func.display(Some(isa)).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -2,26 +2,24 @@
|
||||
//!
|
||||
//! The `rodata` test command runs each function through the full code generator pipeline
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::binemit::{self, CodeInfo};
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_codegen::ir::{Function, Value};
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use log::info;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestRodata;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "rodata");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestRodata))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestRodata))
|
||||
}
|
||||
|
||||
impl SubTest for TestRodata {
|
||||
@@ -37,13 +35,13 @@ impl SubTest for TestRodata {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("rodata needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
let CodeInfo { total_size, .. } = comp_ctx
|
||||
.compile(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
|
||||
info!(
|
||||
"Generated {} bytes of code:\n{}",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! The `run` test command compiles each function on the host machine and executes it
|
||||
|
||||
use crate::function_runner::SingleFunctionCompiler;
|
||||
use crate::subtest::{Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{Context, SubTest};
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_reader::parse_run_command;
|
||||
use cranelift_reader::TestCommand;
|
||||
@@ -13,13 +13,12 @@ use target_lexicon::Architecture;
|
||||
|
||||
struct TestRun;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "run");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestRun))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestRun))
|
||||
}
|
||||
|
||||
impl SubTest for TestRun {
|
||||
@@ -35,7 +34,7 @@ impl SubTest for TestRun {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> anyhow::Result<()> {
|
||||
// If this test requests to run on a completely different
|
||||
// architecture than the host platform then we skip it entirely,
|
||||
// since we won't be able to natively execute machine code.
|
||||
@@ -50,9 +49,7 @@ impl SubTest for TestRun {
|
||||
|
||||
let mut compiler = SingleFunctionCompiler::with_host_isa(context.flags.clone());
|
||||
for comment in context.details.comments.iter() {
|
||||
if let Some(command) =
|
||||
parse_run_command(comment.text, &func.signature).map_err(|e| e.to_string())?
|
||||
{
|
||||
if let Some(command) = parse_run_command(comment.text, &func.signature)? {
|
||||
trace!("Parsed run command: {}", command);
|
||||
|
||||
// Note that here we're also explicitly ignoring `context.isa`,
|
||||
@@ -60,10 +57,10 @@ impl SubTest for TestRun {
|
||||
// host ISA no matter what here, so the ISA listed in the file
|
||||
// is only used as a filter to not run into situations like
|
||||
// running x86_64 code on aarch64 platforms.
|
||||
let compiled_fn = compiler
|
||||
.compile(func.clone().into_owned())
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
command.run(|_, args| Ok(compiled_fn.call(args)))?;
|
||||
let compiled_fn = compiler.compile(func.clone().into_owned())?;
|
||||
command
|
||||
.run(|_, args| Ok(compiled_fn.call(args)))
|
||||
.map_err(|s| anyhow::anyhow!("{}", s))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestSafepoint;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "safepoint");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestSafepoint))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestSafepoint))
|
||||
}
|
||||
|
||||
impl SubTest for TestSafepoint {
|
||||
@@ -20,18 +18,18 @@ impl SubTest for TestSafepoint {
|
||||
"safepoint"
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
let isa = context.isa.expect("register allocator needs an ISA");
|
||||
comp_ctx.compute_cfg();
|
||||
comp_ctx
|
||||
.legalize(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
comp_ctx.compute_domtree();
|
||||
comp_ctx
|
||||
.regalloc(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
|
||||
let text = comp_ctx.func.display(context.isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -5,22 +5,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestShrink;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "shrink");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestShrink))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestShrink))
|
||||
}
|
||||
|
||||
impl SubTest for TestShrink {
|
||||
@@ -32,13 +30,13 @@ impl SubTest for TestShrink {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("shrink needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
comp_ctx
|
||||
.shrink_instructions(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
|
||||
let text = comp_ctx.func.display(isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -5,22 +5,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestSimpleGVN;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "simple-gvn");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestSimpleGVN))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestSimpleGVN))
|
||||
}
|
||||
|
||||
impl SubTest for TestSimpleGVN {
|
||||
@@ -32,13 +30,13 @@ impl SubTest for TestSimpleGVN {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
comp_ctx.flowgraph();
|
||||
comp_ctx
|
||||
.simple_gvn(context.flags_or_isa())
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
|
||||
let text = comp_ctx.func.display(context.isa).to_string();
|
||||
run_filecheck(&text, context)
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
//!
|
||||
//! The resulting function is sent to `filecheck`.
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen;
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
|
||||
struct TestSimplePreopt;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "simple_preopt");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestSimplePreopt))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestSimplePreopt))
|
||||
}
|
||||
|
||||
impl SubTest for TestSimplePreopt {
|
||||
@@ -29,14 +27,14 @@ impl SubTest for TestSimplePreopt {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
let isa = context.isa.expect("preopt needs an ISA");
|
||||
|
||||
comp_ctx.compute_cfg();
|
||||
comp_ctx
|
||||
.preopt(isa)
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, Into::into(e)))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
let text = &comp_ctx.func.display(isa).to_string();
|
||||
log::debug!("After simple_preopt:\n{}", text);
|
||||
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen::binemit::{self, Addend, CodeOffset, CodeSink, Reloc, StackMap};
|
||||
use cranelift_codegen::ir::*;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_reader::TestCommand;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
|
||||
struct TestStackMaps;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "stack_maps");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestStackMaps))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestStackMaps))
|
||||
}
|
||||
|
||||
impl SubTest for TestStackMaps {
|
||||
@@ -23,12 +21,12 @@ impl SubTest for TestStackMaps {
|
||||
"stack_maps"
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
comp_ctx
|
||||
.compile(context.isa.expect("`test stack_maps` requires an isa"))
|
||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
|
||||
|
||||
let mut sink = TestStackMapsSink::default();
|
||||
binemit::emit_function(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! The `unwind` test command runs each function through the full code generator pipeline.
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||
|
||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||
use cranelift_codegen::{self, ir, isa::unwind::UnwindInfo};
|
||||
use cranelift_reader::TestCommand;
|
||||
use gimli::{
|
||||
@@ -14,13 +14,12 @@ use std::borrow::Cow;
|
||||
|
||||
struct TestUnwind;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "unwind");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestUnwind))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestUnwind))
|
||||
}
|
||||
|
||||
impl SubTest for TestUnwind {
|
||||
@@ -36,7 +35,7 @@ impl SubTest for TestUnwind {
|
||||
true
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<ir::Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let isa = context.isa.expect("unwind needs an ISA");
|
||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//! containing the substring "jump to non-existent block".
|
||||
|
||||
use crate::match_directive::match_directive;
|
||||
use crate::subtest::{Context, SubTest, SubtestResult};
|
||||
use crate::subtest::{Context, SubTest};
|
||||
use cranelift_codegen::ir::Function;
|
||||
use cranelift_codegen::verify_function;
|
||||
use cranelift_reader::TestCommand;
|
||||
@@ -19,13 +19,12 @@ use std::fmt::Write;
|
||||
|
||||
struct TestVerifier;
|
||||
|
||||
pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
|
||||
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
|
||||
assert_eq!(parsed.command, "verifier");
|
||||
if !parsed.options.is_empty() {
|
||||
Err(format!("No options allowed on {}", parsed))
|
||||
} else {
|
||||
Ok(Box::new(TestVerifier))
|
||||
anyhow::bail!("No options allowed on {}", parsed);
|
||||
}
|
||||
Ok(Box::new(TestVerifier))
|
||||
}
|
||||
|
||||
impl SubTest for TestVerifier {
|
||||
@@ -38,7 +37,7 @@ impl SubTest for TestVerifier {
|
||||
false
|
||||
}
|
||||
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
|
||||
let func = func.borrow();
|
||||
|
||||
// Scan source annotations for "error:" directives.
|
||||
@@ -52,10 +51,10 @@ impl SubTest for TestVerifier {
|
||||
|
||||
match verify_function(func, context.flags_or_isa()) {
|
||||
Ok(()) if expected.is_empty() => Ok(()),
|
||||
Ok(()) => Err(format!("passed, but expected errors: {:?}", expected)),
|
||||
Ok(()) => anyhow::bail!("passed, but expected errors: {:?}", expected),
|
||||
|
||||
Err(ref errors) if expected.is_empty() => {
|
||||
Err(format!("expected no error, but got:\n{}", errors))
|
||||
anyhow::bail!("expected no error, but got:\n{}", errors);
|
||||
}
|
||||
|
||||
Err(errors) => {
|
||||
@@ -86,7 +85,7 @@ impl SubTest for TestVerifier {
|
||||
if msg.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(msg)
|
||||
anyhow::bail!("{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user