Adds pass command to clif-util. (#487)
* Adds pass command to clif-util.
This commit is contained in:
committed by
Dan Gohman
parent
17bb62c16c
commit
59b83912ba
@@ -1,5 +1,5 @@
|
|||||||
#![deny(trivial_numeric_casts, unused_extern_crates, unstable_features)]
|
#![deny(trivial_numeric_casts)]
|
||||||
#![warn(unused_import_braces)]
|
#![warn(unused_import_braces, unstable_features, unused_extern_crates)]
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
feature = "cargo-clippy",
|
feature = "cargo-clippy",
|
||||||
warn(
|
warn(
|
||||||
@@ -15,13 +15,13 @@ extern crate cfg_if;
|
|||||||
extern crate capstone;
|
extern crate capstone;
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate cranelift_codegen;
|
extern crate cranelift_codegen;
|
||||||
|
extern crate cranelift_entity;
|
||||||
extern crate cranelift_filetests;
|
extern crate cranelift_filetests;
|
||||||
extern crate cranelift_reader;
|
extern crate cranelift_reader;
|
||||||
extern crate pretty_env_logger;
|
extern crate pretty_env_logger;
|
||||||
|
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(feature = "wasm")] {
|
if #[cfg(feature = "wasm")] {
|
||||||
extern crate cranelift_entity;
|
|
||||||
extern crate cranelift_wasm;
|
extern crate cranelift_wasm;
|
||||||
extern crate term;
|
extern crate term;
|
||||||
extern crate wabt;
|
extern crate wabt;
|
||||||
@@ -53,6 +53,21 @@ fn add_input_file_arg<'a>() -> clap::Arg<'a, 'a> {
|
|||||||
.help("Specify file(s) to be used for test")
|
.help("Specify file(s) to be used for test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_single_input_file_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
|
Arg::with_name("single-file")
|
||||||
|
.required(true)
|
||||||
|
.value_name("single-file")
|
||||||
|
.help("Specify a file to be used")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_pass_arg<'a>() -> clap::Arg<'a, 'a> {
|
||||||
|
Arg::with_name("pass")
|
||||||
|
.required(true)
|
||||||
|
.multiple(true)
|
||||||
|
.value_name("pass")
|
||||||
|
.help("Specify pass(s) to be run on test file")
|
||||||
|
}
|
||||||
|
|
||||||
fn add_verbose_flag<'a>() -> clap::Arg<'a, 'a> {
|
fn add_verbose_flag<'a>() -> clap::Arg<'a, 'a> {
|
||||||
Arg::with_name("verbose").short("v").help("Be more verbose")
|
Arg::with_name("verbose").short("v").help("Be more verbose")
|
||||||
}
|
}
|
||||||
@@ -162,7 +177,16 @@ fn main() {
|
|||||||
"Just checks the correctness of Cranelift IR translated from WebAssembly",
|
"Just checks the correctness of Cranelift IR translated from WebAssembly",
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.subcommand(add_wasm_or_compile("wasm"));
|
.subcommand(add_wasm_or_compile("wasm"))
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("pass")
|
||||||
|
.about("Run specified pass(s) on an input file.")
|
||||||
|
.arg(add_single_input_file_arg())
|
||||||
|
.arg(add_target_flag())
|
||||||
|
.arg(add_pass_arg())
|
||||||
|
.arg(add_debug_flag())
|
||||||
|
.arg(add_time_flag()),
|
||||||
|
);
|
||||||
|
|
||||||
let res_util = match app_cmds.get_matches().subcommand() {
|
let res_util = match app_cmds.get_matches().subcommand() {
|
||||||
("cat", Some(rest_cmd)) => {
|
("cat", Some(rest_cmd)) => {
|
||||||
@@ -172,10 +196,26 @@ fn main() {
|
|||||||
("test", Some(rest_cmd)) => {
|
("test", Some(rest_cmd)) => {
|
||||||
handle_debug_flag(rest_cmd.is_present("debug"));
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
||||||
cranelift_filetests::run(
|
cranelift_filetests::run(
|
||||||
rest_cmd.is_present("time-passes"),
|
rest_cmd.is_present("verbose"),
|
||||||
&get_vec(rest_cmd.values_of("file")),
|
&get_vec(rest_cmd.values_of("file")),
|
||||||
).map(|_time| ())
|
).map(|_time| ())
|
||||||
}
|
}
|
||||||
|
("pass", Some(rest_cmd)) => {
|
||||||
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
||||||
|
|
||||||
|
let mut target_val: &str = "";
|
||||||
|
if let Some(clap_target) = rest_cmd.value_of("target") {
|
||||||
|
target_val = clap_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can be unwrapped because 'single-file' is required
|
||||||
|
cranelift_filetests::run_passes(
|
||||||
|
rest_cmd.is_present("verbose"),
|
||||||
|
&get_vec(rest_cmd.values_of("pass")),
|
||||||
|
target_val,
|
||||||
|
&rest_cmd.value_of("single-file").unwrap().to_string(),
|
||||||
|
).map(|_time| ())
|
||||||
|
}
|
||||||
("print-cfg", Some(rest_cmd)) => {
|
("print-cfg", Some(rest_cmd)) => {
|
||||||
handle_debug_flag(rest_cmd.is_present("debug"));
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
||||||
print_cfg::run(&get_vec(rest_cmd.values_of("file")))
|
print_cfg::run(&get_vec(rest_cmd.values_of("file")))
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ fn handle_module(
|
|||||||
fisa: FlagsOrIsa,
|
fisa: FlagsOrIsa,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let buffer = read_to_string(&path).map_err(|e| format!("{}: {}", name, e))?;
|
let buffer = read_to_string(&path).map_err(|e| format!("{}: {}", name, e))?;
|
||||||
let test_file = parse_test(&buffer).map_err(|e| format!("{}: {}", name, e))?;
|
let test_file = parse_test(&buffer, None, None).map_err(|e| format!("{}: {}", name, e))?;
|
||||||
|
|
||||||
// If we have an isa from the command-line, use that. Otherwise if the
|
// If we have an isa from the command-line, use that. Otherwise if the
|
||||||
// file contains a unique isa, use that.
|
// file contains a unique isa, use that.
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ fn worker_thread(
|
|||||||
// The receiver should always be present for this as long as we have jobs.
|
// The receiver should always be present for this as long as we have jobs.
|
||||||
replies.send(Reply::Starting { jobid, thread_num }).unwrap();
|
replies.send(Reply::Starting { jobid, thread_num }).unwrap();
|
||||||
|
|
||||||
let result = catch_unwind(|| runone::run(path.as_path())).unwrap_or_else(|e| {
|
let result = catch_unwind(|| runone::run(path.as_path(), None, None))
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
// The test panicked, leaving us a `Box<Any>`.
|
// The test panicked, leaving us a `Box<Any>`.
|
||||||
// Panics are usually strings.
|
// Panics are usually strings.
|
||||||
if let Some(msg) = e.downcast_ref::<String>() {
|
if let Some(msg) = e.downcast_ref::<String>() {
|
||||||
|
|||||||
@@ -79,6 +79,24 @@ pub fn run(verbose: bool, files: &[String]) -> TestResult {
|
|||||||
runner.run()
|
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, passes: &[String], target: &str, file: &String) -> TestResult {
|
||||||
|
let mut runner = TestRunner::new(verbose);
|
||||||
|
|
||||||
|
let path = Path::new(file);
|
||||||
|
if path.is_file() {
|
||||||
|
runner.push_test(path);
|
||||||
|
} else {
|
||||||
|
runner.push_dir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
runner.run_passes(passes, target)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new subcommand trait object to match `parsed.command`.
|
/// 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
|
/// This function knows how to create all of the possible `test <foo>` commands that can appear in
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ enum State {
|
|||||||
Done(TestResult),
|
Done(TestResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub enum IsPass {
|
||||||
|
Pass,
|
||||||
|
NotPass,
|
||||||
|
}
|
||||||
|
|
||||||
impl QueueEntry {
|
impl QueueEntry {
|
||||||
pub fn path(&self) -> &Path {
|
pub fn path(&self) -> &Path {
|
||||||
self.path.as_path()
|
self.path.as_path()
|
||||||
@@ -118,7 +124,7 @@ impl TestRunner {
|
|||||||
|
|
||||||
/// Scan any directories pushed so far.
|
/// Scan any directories pushed so far.
|
||||||
/// Push any potential test cases found.
|
/// Push any potential test cases found.
|
||||||
pub fn scan_dirs(&mut self) {
|
pub fn scan_dirs(&mut self, pass_status: IsPass) {
|
||||||
// This recursive search tries to minimize statting in a directory hierarchy containing
|
// This recursive search tries to minimize statting in a directory hierarchy containing
|
||||||
// mostly test cases.
|
// mostly test cases.
|
||||||
//
|
//
|
||||||
@@ -164,10 +170,14 @@ impl TestRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if pass_status == IsPass::Pass {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
// Get the new jobs running before moving on to the next directory.
|
// Get the new jobs running before moving on to the next directory.
|
||||||
self.schedule_jobs();
|
self.schedule_jobs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Report an error related to a path.
|
/// Report an error related to a path.
|
||||||
fn path_error<E: Error>(&mut self, path: &PathBuf, err: &E) {
|
fn path_error<E: Error>(&mut self, path: &PathBuf, err: &E) {
|
||||||
@@ -203,7 +213,7 @@ impl TestRunner {
|
|||||||
} else {
|
} else {
|
||||||
// Run test synchronously.
|
// Run test synchronously.
|
||||||
self.tests[jobid].state = State::Running;
|
self.tests[jobid].state = State::Running;
|
||||||
let result = runone::run(self.tests[jobid].path());
|
let result = runone::run(self.tests[jobid].path(), None, None);
|
||||||
self.finish_job(jobid, result);
|
self.finish_job(jobid, result);
|
||||||
}
|
}
|
||||||
self.new_tests = jobid + 1;
|
self.new_tests = jobid + 1;
|
||||||
@@ -215,6 +225,20 @@ 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 specified_target = match target {
|
||||||
|
"" => None,
|
||||||
|
targ => Some(targ),
|
||||||
|
};
|
||||||
|
|
||||||
|
result = runone::run(self.tests[0].path(), Some(passes), specified_target);
|
||||||
|
self.finish_job(0, result);
|
||||||
|
}
|
||||||
|
|
||||||
/// Report the end of a job.
|
/// Report the end of a job.
|
||||||
fn finish_job(&mut self, jobid: usize, result: TestResult) {
|
fn finish_job(&mut self, jobid: usize, result: TestResult) {
|
||||||
assert_eq!(self.tests[jobid].state, State::Running);
|
assert_eq!(self.tests[jobid].state, State::Running);
|
||||||
@@ -331,10 +355,26 @@ impl TestRunner {
|
|||||||
/// Scan pushed directories for tests and run them.
|
/// Scan pushed directories for tests and run them.
|
||||||
pub fn run(&mut self) -> TestResult {
|
pub fn run(&mut self) -> TestResult {
|
||||||
let started = time::Instant::now();
|
let started = time::Instant::now();
|
||||||
self.scan_dirs();
|
self.scan_dirs(IsPass::NotPass);
|
||||||
self.schedule_jobs();
|
self.schedule_jobs();
|
||||||
self.drain_threads();
|
|
||||||
self.report_slow_tests();
|
self.report_slow_tests();
|
||||||
|
self.drain_threads();
|
||||||
|
|
||||||
|
println!("{} tests", self.tests.len());
|
||||||
|
match self.errors {
|
||||||
|
0 => Ok(started.elapsed()),
|
||||||
|
1 => Err("1 failure".to_string()),
|
||||||
|
n => Err(format!("{} 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 {
|
||||||
|
let started = time::Instant::now();
|
||||||
|
self.scan_dirs(IsPass::Pass);
|
||||||
|
self.schedule_pass_job(passes, target);
|
||||||
|
self.report_slow_tests();
|
||||||
|
|
||||||
println!("{} tests", self.tests.len());
|
println!("{} tests", self.tests.len());
|
||||||
match self.errors {
|
match self.errors {
|
||||||
0 => Ok(started.elapsed()),
|
0 => Ok(started.elapsed()),
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
|
|||||||
/// Load `path` and run the test in it.
|
/// Load `path` and run the test in it.
|
||||||
///
|
///
|
||||||
/// If running this test causes a panic, it will propagate as normal.
|
/// If running this test causes a panic, it will propagate as normal.
|
||||||
pub fn run(path: &Path) -> TestResult {
|
pub fn run(path: &Path, passes: Option<&[String]>, target: Option<&str>) -> TestResult {
|
||||||
let _tt = timing::process_file();
|
let _tt = timing::process_file();
|
||||||
info!("---\nFile: {}", path.to_string_lossy());
|
info!("---\nFile: {}", path.to_string_lossy());
|
||||||
let started = time::Instant::now();
|
let started = time::Instant::now();
|
||||||
let buffer = read_to_string(path).map_err(|e| e.to_string())?;
|
let buffer = read_to_string(path).map_err(|e| e.to_string())?;
|
||||||
let testfile = parse_test(&buffer).map_err(|e| e.to_string())?;
|
let testfile = parse_test(&buffer, passes, target).map_err(|e| e.to_string())?;
|
||||||
if testfile.functions.is_empty() {
|
if testfile.functions.is_empty() {
|
||||||
return Err("no functions found".to_string());
|
return Err("no functions found".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,20 +32,39 @@ use testfile::{Comment, Details, TestFile};
|
|||||||
/// Any test commands or target declarations are ignored.
|
/// Any test commands or target declarations are ignored.
|
||||||
pub fn parse_functions(text: &str) -> ParseResult<Vec<Function>> {
|
pub fn parse_functions(text: &str) -> ParseResult<Vec<Function>> {
|
||||||
let _tt = timing::parse_text();
|
let _tt = timing::parse_text();
|
||||||
parse_test(text).map(|file| file.functions.into_iter().map(|(func, _)| func).collect())
|
parse_test(text, None, None)
|
||||||
|
.map(|file| file.functions.into_iter().map(|(func, _)| func).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the entire `text` as a test case file.
|
/// Parse the entire `text` as a test case file.
|
||||||
///
|
///
|
||||||
/// The returned `TestFile` contains direct references to substrings of `text`.
|
/// The returned `TestFile` contains direct references to substrings of `text`.
|
||||||
pub fn parse_test(text: &str) -> ParseResult<TestFile> {
|
pub fn parse_test<'a>(
|
||||||
|
text: &'a str,
|
||||||
|
passes: Option<&'a [String]>,
|
||||||
|
target: Option<&str>,
|
||||||
|
) -> ParseResult<TestFile<'a>> {
|
||||||
let _tt = timing::parse_text();
|
let _tt = timing::parse_text();
|
||||||
let mut parser = Parser::new(text);
|
let mut parser = Parser::new(text);
|
||||||
// Gather the preamble comments.
|
// Gather the preamble comments.
|
||||||
parser.start_gathering_comments();
|
parser.start_gathering_comments();
|
||||||
|
|
||||||
let commands = parser.parse_test_commands();
|
let isa_spec: isaspec::IsaSpec;
|
||||||
let isa_spec = parser.parse_target_specs()?;
|
let commands: Vec<TestCommand<'a>>;
|
||||||
|
|
||||||
|
// Check for specified passes and target, if present throw out test commands/targets specified in file.
|
||||||
|
match passes {
|
||||||
|
Some(pass_vec) => {
|
||||||
|
parser.parse_test_commands();
|
||||||
|
commands = parser.parse_cmdline_passes(pass_vec);
|
||||||
|
parser.parse_target_specs()?;
|
||||||
|
isa_spec = parser.parse_cmdline_target(target)?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
commands = parser.parse_test_commands();
|
||||||
|
isa_spec = parser.parse_target_specs()?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
parser.token();
|
parser.token();
|
||||||
parser.claim_gathered_comments(AnyEntity::Function);
|
parser.claim_gathered_comments(AnyEntity::Function);
|
||||||
@@ -705,6 +724,15 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a list of test command passes specified in command line.
|
||||||
|
pub fn parse_cmdline_passes(&mut self, passes: &'a [String]) -> Vec<TestCommand<'a>> {
|
||||||
|
let mut list = Vec::new();
|
||||||
|
for pass in passes {
|
||||||
|
list.push(TestCommand::new(pass));
|
||||||
|
}
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a list of test commands.
|
/// Parse a list of test commands.
|
||||||
pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> {
|
pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
@@ -714,12 +742,58 @@ impl<'a> Parser<'a> {
|
|||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a target spec.
|
||||||
|
///
|
||||||
|
/// Accept the target from the command line for pass command.
|
||||||
|
///
|
||||||
|
pub fn parse_cmdline_target(
|
||||||
|
&mut self,
|
||||||
|
target_pass: Option<&str>,
|
||||||
|
) -> ParseResult<isaspec::IsaSpec> {
|
||||||
|
// Were there any `target` commands specified?
|
||||||
|
let mut specified_target = false;
|
||||||
|
|
||||||
|
let mut targets = Vec::new();
|
||||||
|
let flag_builder = settings::builder();
|
||||||
|
|
||||||
|
match target_pass {
|
||||||
|
Some(targ) => {
|
||||||
|
let loc = self.loc;
|
||||||
|
let triple = match Triple::from_str(targ) {
|
||||||
|
Ok(triple) => triple,
|
||||||
|
Err(err) => return err!(loc, err),
|
||||||
|
};
|
||||||
|
let mut isa_builder = match isa::lookup(triple) {
|
||||||
|
Err(isa::LookupError::SupportDisabled) => {
|
||||||
|
return err!(loc, "support disabled target '{}'", targ)
|
||||||
|
}
|
||||||
|
Err(isa::LookupError::Unsupported) => {
|
||||||
|
return err!(loc, "unsupported target '{}'", targ)
|
||||||
|
}
|
||||||
|
Ok(b) => b,
|
||||||
|
};
|
||||||
|
specified_target = true;
|
||||||
|
|
||||||
|
// Construct a trait object with the aggregate settings.
|
||||||
|
targets.push(isa_builder.finish(settings::Flags::new(flag_builder.clone())));
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !specified_target {
|
||||||
|
// No `target` commands.
|
||||||
|
Ok(isaspec::IsaSpec::None(settings::Flags::new(flag_builder)))
|
||||||
|
} else {
|
||||||
|
Ok(isaspec::IsaSpec::Some(targets))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a list of target specs.
|
/// Parse a list of target specs.
|
||||||
///
|
///
|
||||||
/// Accept a mix of `target` and `set` command lines. The `set` commands are cumulative.
|
/// Accept a mix of `target` and `set` command lines. The `set` commands are cumulative.
|
||||||
///
|
///
|
||||||
pub fn parse_target_specs(&mut self) -> ParseResult<isaspec::IsaSpec> {
|
pub fn parse_target_specs(&mut self) -> ParseResult<isaspec::IsaSpec> {
|
||||||
// Was there any `target` commands?
|
// Were there any `target` commands?
|
||||||
let mut seen_target = false;
|
let mut seen_target = false;
|
||||||
// Location of last `set` command since the last `target`.
|
// Location of last `set` command since the last `target`.
|
||||||
let mut last_set_loc = None;
|
let mut last_set_loc = None;
|
||||||
@@ -2703,6 +2777,8 @@ mod tests {
|
|||||||
set enable_float=false
|
set enable_float=false
|
||||||
; still preamble
|
; still preamble
|
||||||
function %comment() system_v {}",
|
function %comment() system_v {}",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
assert_eq!(tf.commands.len(), 2);
|
assert_eq!(tf.commands.len(), 2);
|
||||||
assert_eq!(tf.commands[0].command, "cfg");
|
assert_eq!(tf.commands[0].command, "cfg");
|
||||||
|
|||||||
@@ -222,6 +222,8 @@ mod tests {
|
|||||||
ebb0(v4: i32, v7: i32):
|
ebb0(v4: i32, v7: i32):
|
||||||
v10 = iadd v4, v7
|
v10 = iadd v4, v7
|
||||||
}",
|
}",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
let map = &tf.functions[0].1.map;
|
let map = &tf.functions[0].1.map;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user