Add test run to cranelift-filetests to allow executing CLIF (#890)
* Add ability to run CLIF IR using `clif-util run [-v] {file}` and add `test run` to cranelift-filetests to allow executing CLIF
This re-factors the compile/execute parts to a FunctionRunner that is shared between cranelift-filetests and clif-util. CLIF can be now be run using `clif-util run` as well as during `clif-util test` for files with a `test run` header. As before, only functions suffixed with a `run` comment are executed. The `run: fn(...) == ...` expression syntax is left for a subsequent change.
This commit is contained in:
committed by
Benjamin Bouvier
parent
276bb5e26d
commit
ff3c44385c
118
cranelift/src/run.rs
Normal file
118
cranelift/src/run.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
//! CLI tool to compile Cranelift IR files to native code in memory and execute them.
|
||||
|
||||
use crate::utils::read_to_string;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_filetests::FunctionRunner;
|
||||
use cranelift_native::builder as host_isa_builder;
|
||||
use cranelift_reader::{parse_test, Details, IsaSpec};
|
||||
use std::path::PathBuf;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub fn run(files: Vec<String>, flag_print: bool) -> Result<(), String> {
|
||||
let mut total = 0;
|
||||
let mut errors = 0;
|
||||
for file in iterate_files(files) {
|
||||
total += 1;
|
||||
match run_single_file(&file) {
|
||||
Ok(_) => {
|
||||
if flag_print {
|
||||
println!("{}", file.to_string_lossy());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if flag_print {
|
||||
println!("{}: {}", file.to_string_lossy(), e);
|
||||
}
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flag_print {
|
||||
match total {
|
||||
0 => println!("0 files"),
|
||||
1 => println!("1 file"),
|
||||
n => println!("{} files", n),
|
||||
}
|
||||
}
|
||||
|
||||
match errors {
|
||||
0 => Ok(()),
|
||||
1 => Err(String::from("1 failure")),
|
||||
n => Err(format!("{} failures", n)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over all of the files passed as arguments, recursively iterating through directories
|
||||
fn iterate_files(files: Vec<String>) -> impl Iterator<Item = PathBuf> {
|
||||
files
|
||||
.into_iter()
|
||||
.flat_map(WalkDir::new)
|
||||
.filter(|f| match f {
|
||||
Ok(d) => {
|
||||
// filter out hidden files (starting with .)
|
||||
!d.file_name().to_str().map_or(false, |s| s.starts_with("."))
|
||||
// filter out directories
|
||||
&& !d.file_type().is_dir()
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Unable to read file: {}", e);
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(|f| {
|
||||
f.expect("This should not happen: we have already filtered out the errors")
|
||||
.into_path()
|
||||
})
|
||||
}
|
||||
|
||||
/// Run all functions in a file that are succeeded by "run:" comments
|
||||
fn run_single_file(path: &PathBuf) -> Result<(), String> {
|
||||
let file_contents = read_to_string(&path).map_err(|e| e.to_string())?;
|
||||
run_file_contents(file_contents)
|
||||
}
|
||||
|
||||
/// Main body of `run_single_file` separated for testing
|
||||
fn run_file_contents(file_contents: String) -> Result<(), String> {
|
||||
let test_file = parse_test(&file_contents, None, None).map_err(|e| e.to_string())?;
|
||||
for (func, Details { comments, .. }) in test_file.functions {
|
||||
if comments.iter().any(|c| c.text.contains("run")) {
|
||||
let isa = create_target_isa(&test_file.isa_spec)?;
|
||||
FunctionRunner::new(func, isa).run()?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build an ISA based on the current machine running this code (the host)
|
||||
fn create_target_isa(isa_spec: &IsaSpec) -> Result<Box<dyn TargetIsa>, String> {
|
||||
if let IsaSpec::None(flags) = isa_spec {
|
||||
// build an ISA for the current machine
|
||||
let builder = host_isa_builder()?;
|
||||
Ok(builder.finish(flags.clone()))
|
||||
} else {
|
||||
Err(String::from("A target ISA was specified in the file but should not have been--only the host ISA can be used for running CLIF files"))?
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn nop() {
|
||||
let code = String::from(
|
||||
"
|
||||
function %test() -> b8 system_v {
|
||||
ebb0:
|
||||
nop
|
||||
v1 = bconst.b8 true
|
||||
return v1
|
||||
}
|
||||
|
||||
; run
|
||||
",
|
||||
);
|
||||
run_file_contents(code).unwrap()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user