Peepmatic was an early attempt at a DSL for peephole optimizations, with the idea that maybe sometime in the future we could user it for instruction selection as well. It didn't really pan out, however: * Peepmatic wasn't quite flexible enough, and adding new operators or snippets of code implemented externally in Rust was a bit of a pain. * The performance was never competitive with the hand-written peephole optimizers. It was *very* size efficient, but that came at the cost of run-time efficiency. Everything was table-based and interpreted, rather than generating any Rust code. Ultimately, because of these reasons, we never turned Peepmatic on by default. These days, we just landed the ISLE domain-specific language, and it is better suited than Peepmatic for all the things that Peepmatic was originally designed to do. It is more flexible and easy to integrate with external Rust code. It is has better time efficiency, meeting or even beating hand-written code. I think a small part of the reason why ISLE excels in these things is because its design was informed by Peepmatic's failures. I still plan on continuing Peepmatic's mission to make Cranelift's peephole optimizer passes generated from DSL rewrite rules, but using ISLE instead of Peepmatic. Thank you Peepmatic, rest in peace!
140 lines
3.9 KiB
Rust
140 lines
3.9 KiB
Rust
//! File tests.
|
|
//!
|
|
//! This crate contains the main test driver as well as implementations of the
|
|
//! available filetest commands.
|
|
|
|
#![deny(
|
|
missing_docs,
|
|
trivial_numeric_casts,
|
|
unused_extern_crates,
|
|
unstable_features
|
|
)]
|
|
#![warn(unused_import_braces)]
|
|
#![cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
|
|
#![cfg_attr(
|
|
feature = "cargo-clippy",
|
|
warn(
|
|
clippy::mut_mut,
|
|
clippy::nonminimal_bool,
|
|
clippy::map_unwrap_or,
|
|
clippy::clippy::unicode_not_nfc,
|
|
clippy::use_self
|
|
)
|
|
)]
|
|
|
|
pub use crate::function_runner::SingleFunctionCompiler;
|
|
use crate::runner::TestRunner;
|
|
use cranelift_codegen::timing;
|
|
use cranelift_reader::TestCommand;
|
|
use std::path::Path;
|
|
use std::time;
|
|
|
|
mod concurrent;
|
|
pub mod function_runner;
|
|
mod match_directive;
|
|
mod runner;
|
|
mod runone;
|
|
mod runtest_environment;
|
|
mod subtest;
|
|
|
|
mod test_cat;
|
|
mod test_compile;
|
|
mod test_dce;
|
|
mod test_domtree;
|
|
mod test_interpret;
|
|
mod test_legalizer;
|
|
mod test_licm;
|
|
mod test_preopt;
|
|
mod test_print_cfg;
|
|
mod test_run;
|
|
mod test_safepoint;
|
|
mod test_simple_gvn;
|
|
mod test_simple_preopt;
|
|
mod test_unwind;
|
|
mod test_verifier;
|
|
|
|
/// Main entry point for `clif-util test`.
|
|
///
|
|
/// Take a list of filenames which can be either `.clif` files or directories.
|
|
///
|
|
/// Files are interpreted as test cases and executed immediately.
|
|
///
|
|
/// 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]) -> anyhow::Result<time::Duration> {
|
|
let mut runner = TestRunner::new(verbose, report_times);
|
|
|
|
for path in files.iter().map(Path::new) {
|
|
if path.is_file() {
|
|
runner.push_test(path);
|
|
} else {
|
|
runner.push_dir(path);
|
|
}
|
|
}
|
|
|
|
runner.start_threads();
|
|
runner.run()
|
|
}
|
|
|
|
/// Used for 'pass' subcommand.
|
|
/// Commands are interpreted as test and executed.
|
|
///
|
|
/// Directories are scanned recursively for test cases ending in `.clif`.
|
|
///
|
|
pub fn run_passes(
|
|
verbose: bool,
|
|
report_times: bool,
|
|
passes: &[String],
|
|
target: &str,
|
|
file: &str,
|
|
) -> anyhow::Result<time::Duration> {
|
|
let mut runner = TestRunner::new(verbose, /* report_times */ false);
|
|
|
|
let path = Path::new(file);
|
|
if path == Path::new("-") || path.is_file() {
|
|
runner.push_test(path);
|
|
} else {
|
|
runner.push_dir(path);
|
|
}
|
|
|
|
let result = runner.run_passes(passes, target);
|
|
if report_times {
|
|
println!("{}", timing::take_current());
|
|
}
|
|
result
|
|
}
|
|
|
|
/// Create a new subcommand trait object to match `parsed.command`.
|
|
///
|
|
/// 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) -> anyhow::Result<Box<dyn subtest::SubTest>> {
|
|
match parsed.command {
|
|
"cat" => test_cat::subtest(parsed),
|
|
"compile" => test_compile::subtest(parsed),
|
|
"dce" => test_dce::subtest(parsed),
|
|
"domtree" => test_domtree::subtest(parsed),
|
|
"interpret" => test_interpret::subtest(parsed),
|
|
"legalizer" => test_legalizer::subtest(parsed),
|
|
"licm" => test_licm::subtest(parsed),
|
|
"preopt" => test_preopt::subtest(parsed),
|
|
"print-cfg" => test_print_cfg::subtest(parsed),
|
|
"run" => test_run::subtest(parsed),
|
|
"safepoint" => test_safepoint::subtest(parsed),
|
|
"simple-gvn" => test_simple_gvn::subtest(parsed),
|
|
"simple_preopt" => test_simple_preopt::subtest(parsed),
|
|
"unwind" => test_unwind::subtest(parsed),
|
|
"verifier" => test_verifier::subtest(parsed),
|
|
_ => anyhow::bail!("unknown test command '{}'", parsed.command),
|
|
}
|
|
}
|
|
|
|
fn pretty_anyhow_error(
|
|
func: &cranelift_codegen::ir::Function,
|
|
err: cranelift_codegen::CodegenError,
|
|
) -> anyhow::Error {
|
|
let s = cranelift_codegen::print_errors::pretty_error(func, err);
|
|
anyhow::anyhow!("{}", s)
|
|
}
|