* Manually rename BasicBlock to BlockPredecessor BasicBlock is a pair of (Ebb, Inst) that is used to represent the basic block subcomponent of an Ebb that is a predecessor to an Ebb. Eventually we will be able to remove this struct, but for now it makes sense to give it a non-conflicting name so that we can start to transition Ebb to represent a basic block. I have not updated any comments that refer to BasicBlock, as eventually we will remove BlockPredecessor and replace with Block, which is a basic block, so the comments will become correct. * Manually rename SSABuilder block types to avoid conflict SSABuilder has its own Block and BlockData types. These along with associated identifier will cause conflicts in a later commit, so they are renamed to be more verbose here. * Automatically rename 'Ebb' to 'Block' in *.rs * Automatically rename 'EBB' to 'block' in *.rs * Automatically rename 'ebb' to 'block' in *.rs * Automatically rename 'extended basic block' to 'basic block' in *.rs * Automatically rename 'an basic block' to 'a basic block' in *.rs * Manually update comment for `Block` `Block`'s wikipedia article required an update. * Automatically rename 'an `Block`' to 'a `Block`' in *.rs * Automatically rename 'extended_basic_block' to 'basic_block' in *.rs * Automatically rename 'ebb' to 'block' in *.clif * Manually rename clif constant that contains 'ebb' as substring to avoid conflict * Automatically rename filecheck uses of 'EBB' to 'BB' 'regex: EBB' -> 'regex: BB' '$EBB' -> '$BB' * Automatically rename 'EBB' 'Ebb' to 'block' in *.clif * Automatically rename 'an block' to 'a block' in *.clif * Fix broken testcase when function name length increases Test function names are limited to 16 characters. This causes the new longer name to be truncated and fail a filecheck test. An outdated comment was also fixed.
133 lines
4.2 KiB
Rust
133 lines
4.2 KiB
Rust
//! 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::{CallConv, TargetIsa};
|
|
use cranelift_filetests::FunctionRunner;
|
|
use cranelift_native::builder as host_isa_builder;
|
|
use cranelift_reader::{parse_test, Details, IsaSpec, ParseOptions};
|
|
use std::path::PathBuf;
|
|
use target_lexicon::Triple;
|
|
use walkdir::WalkDir;
|
|
|
|
pub fn run(files: Vec<String>, flag_print: bool) -> Result<(), String> {
|
|
let stdin_exist = files.iter().find(|file| *file == "-").is_some();
|
|
let filtered_files = files
|
|
.iter()
|
|
.filter(|file| *file != "-")
|
|
.map(|file| file.to_string())
|
|
.collect::<Vec<String>>();
|
|
let mut total = 0;
|
|
let mut errors = 0;
|
|
let mut special_files: Vec<PathBuf> = vec![];
|
|
if stdin_exist {
|
|
special_files.push("-".into());
|
|
}
|
|
for file in iterate_files(filtered_files).chain(special_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 options = ParseOptions {
|
|
default_calling_convention: CallConv::triple_default(&Triple::host()), // use the host's default calling convention
|
|
..ParseOptions::default()
|
|
};
|
|
let test_file = parse_test(&file_contents, options).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 {
|
|
block0:
|
|
nop
|
|
v1 = bconst.b8 true
|
|
return v1
|
|
}
|
|
; run
|
|
",
|
|
);
|
|
run_file_contents(code).unwrap()
|
|
}
|
|
}
|