This commit moves the cranelift tests and tools from the `wabt` crate on crates.io (which compiles the wabt C++ codebase) to the `wat` crate on crates.io which is a Rust parser for the `*.wat` format. This was motivated by me noticing that release builds on Windows are ~5 minutes longer than Linux builds, and local timing graphs showed that `wabt-sys` was by far the longest build step in the build process. This commit changes the `clif-util` binary where the `--enable-simd` flag is no longer respected with the text format as input, since the `wat` crate has no feature gating. This was already sort of not respected, though, since `--enable-simd` wasn't consulted for binary inputs which `clif-util` supports as well. If this isn't ok though then it should be ok to close this PR!
336 lines
10 KiB
Rust
Executable File
336 lines
10 KiB
Rust
Executable File
#![deny(trivial_numeric_casts)]
|
|
#![warn(unused_import_braces, unstable_features, unused_extern_crates)]
|
|
#![cfg_attr(
|
|
feature = "cargo-clippy",
|
|
warn(
|
|
clippy::float_arithmetic,
|
|
clippy::mut_mut,
|
|
clippy::nonminimal_bool,
|
|
clippy::option_map_unwrap_or,
|
|
clippy::option_map_unwrap_or_else,
|
|
clippy::unicode_not_nfc,
|
|
clippy::use_self
|
|
)
|
|
)]
|
|
|
|
use clap::{App, Arg, SubCommand};
|
|
use cranelift_codegen::dbg::LOG_FILENAME_PREFIX;
|
|
use cranelift_codegen::VERSION;
|
|
use std::io::{self, Write};
|
|
use std::option::Option;
|
|
use std::process;
|
|
|
|
mod bugpoint;
|
|
mod cat;
|
|
mod compile;
|
|
mod disasm;
|
|
mod print_cfg;
|
|
mod run;
|
|
mod utils;
|
|
|
|
#[cfg(feature = "wasm")]
|
|
mod wasm;
|
|
|
|
/// A command either succeeds or fails with an error message.
|
|
pub type CommandResult = Result<(), String>;
|
|
|
|
fn add_input_file_arg<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("file")
|
|
.default_value("-")
|
|
.multiple(true)
|
|
.value_name("file")
|
|
.help("Specify file(s) to be used for test. Defaults to reading from stdin.")
|
|
}
|
|
|
|
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. Use '-' for stdin.")
|
|
}
|
|
|
|
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> {
|
|
Arg::with_name("verbose").short("v").help("Be more verbose")
|
|
}
|
|
|
|
fn add_time_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("time-passes")
|
|
.short("T")
|
|
.help("Print pass timing report for test")
|
|
}
|
|
|
|
fn add_size_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("print-size")
|
|
.short("X")
|
|
.help("Print bytecode size")
|
|
}
|
|
|
|
fn add_disasm_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("disasm")
|
|
.long("disasm")
|
|
.short("D")
|
|
.help("Print machine code disassembly")
|
|
}
|
|
|
|
fn add_set_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("set")
|
|
.long("set")
|
|
.takes_value(true)
|
|
.multiple(true)
|
|
.help("Configure Cranelift settings")
|
|
}
|
|
|
|
fn add_target_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("target")
|
|
.takes_value(true)
|
|
.long("target")
|
|
.help("Specify the Cranelift target")
|
|
}
|
|
|
|
fn add_print_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("print")
|
|
.short("p")
|
|
.help("Print the resulting Cranelift IR")
|
|
}
|
|
|
|
fn add_debug_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("debug")
|
|
.short("d")
|
|
.help("Enable debug output on stderr/stdout")
|
|
}
|
|
|
|
fn add_just_decode_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("just-decode")
|
|
.short("t")
|
|
.help("Just decode into Cranelift IR")
|
|
}
|
|
|
|
fn add_check_translation_flag<'a>() -> clap::Arg<'a, 'a> {
|
|
Arg::with_name("check-translation")
|
|
.short("c")
|
|
.help("Just checks the correctness of Cranelift IR translated from WebAssembly")
|
|
}
|
|
|
|
/// Returns a vector of clap value options and changes these options into a vector of strings
|
|
fn get_vec(argument_vec: Option<clap::Values>) -> Vec<String> {
|
|
let mut ret_vec: Vec<String> = Vec::new();
|
|
if let Some(clap_vec) = argument_vec {
|
|
for val in clap_vec {
|
|
ret_vec.push(val.to_string());
|
|
}
|
|
}
|
|
|
|
ret_vec
|
|
}
|
|
|
|
fn add_wasm_or_compile<'a>(cmd: &str) -> clap::App<'a, 'a> {
|
|
let about_str = match cmd {
|
|
"wasm" => "Compiles Wasm binary/text into Cranelift IR and then into target language",
|
|
"compile" => "Compiles Cranelift IR into target language",
|
|
_ => panic!("Invalid command"),
|
|
};
|
|
|
|
SubCommand::with_name(cmd)
|
|
.about(about_str)
|
|
.arg(add_verbose_flag())
|
|
.arg(add_print_flag())
|
|
.arg(add_time_flag())
|
|
.arg(add_size_flag())
|
|
.arg(add_disasm_flag())
|
|
.arg(add_set_flag())
|
|
.arg(add_target_flag())
|
|
.arg(add_input_file_arg())
|
|
.arg(add_debug_flag())
|
|
.arg(add_just_decode_flag())
|
|
.arg(add_check_translation_flag())
|
|
}
|
|
|
|
fn handle_debug_flag(debug: bool) {
|
|
if debug {
|
|
pretty_env_logger::init();
|
|
} else {
|
|
file_per_thread_logger::initialize(LOG_FILENAME_PREFIX);
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let app_cmds = App::new("Cranelift code generator utility")
|
|
.version(VERSION)
|
|
.subcommand(
|
|
SubCommand::with_name("test")
|
|
.about("Run Cranelift tests")
|
|
.arg(add_verbose_flag())
|
|
.arg(add_time_flag())
|
|
.arg(add_input_file_arg())
|
|
.arg(add_debug_flag()),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("run")
|
|
.about("Execute CLIF code and verify with test expressions")
|
|
.arg(add_verbose_flag())
|
|
.arg(add_input_file_arg())
|
|
.arg(add_debug_flag()),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("cat")
|
|
.about("Outputs .clif file")
|
|
.arg(add_input_file_arg())
|
|
.arg(add_debug_flag()),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("print-cfg")
|
|
.about("Prints out cfg in dot format")
|
|
.arg(add_input_file_arg())
|
|
.arg(add_debug_flag()),
|
|
)
|
|
.subcommand(add_wasm_or_compile("compile"))
|
|
.subcommand(
|
|
add_wasm_or_compile("wasm").arg(
|
|
Arg::with_name("value-ranges")
|
|
.long("value-ranges")
|
|
.help("Display values ranges and their locations"),
|
|
),
|
|
)
|
|
.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()),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("bugpoint")
|
|
.about("Reduce size of clif file causing panic during compilation.")
|
|
.arg(add_single_input_file_arg())
|
|
.arg(add_set_flag())
|
|
.arg(add_target_flag())
|
|
.arg(add_verbose_flag()),
|
|
);
|
|
|
|
let res_util = match app_cmds.get_matches().subcommand() {
|
|
("cat", Some(rest_cmd)) => {
|
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
|
cat::run(&get_vec(rest_cmd.values_of("file")))
|
|
}
|
|
("test", Some(rest_cmd)) => {
|
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
|
cranelift_filetests::run(
|
|
rest_cmd.is_present("verbose"),
|
|
rest_cmd.is_present("time-passes"),
|
|
&get_vec(rest_cmd.values_of("file")),
|
|
)
|
|
.map(|_time| ())
|
|
}
|
|
("run", Some(rest_cmd)) => {
|
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
|
run::run(
|
|
get_vec(rest_cmd.values_of("file")),
|
|
rest_cmd.is_present("verbose"),
|
|
)
|
|
.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"),
|
|
rest_cmd.is_present("time-passes"),
|
|
&get_vec(rest_cmd.values_of("pass")),
|
|
target_val,
|
|
rest_cmd.value_of("single-file").unwrap(),
|
|
)
|
|
.map(|_time| ())
|
|
}
|
|
("print-cfg", Some(rest_cmd)) => {
|
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
|
print_cfg::run(&get_vec(rest_cmd.values_of("file")))
|
|
}
|
|
("compile", 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;
|
|
}
|
|
|
|
compile::run(
|
|
get_vec(rest_cmd.values_of("file")),
|
|
rest_cmd.is_present("print"),
|
|
rest_cmd.is_present("disasm"),
|
|
rest_cmd.is_present("time-passes"),
|
|
&get_vec(rest_cmd.values_of("set")),
|
|
target_val,
|
|
)
|
|
}
|
|
("wasm", Some(rest_cmd)) => {
|
|
handle_debug_flag(rest_cmd.is_present("debug"));
|
|
|
|
#[cfg(feature = "wasm")]
|
|
let result = {
|
|
let mut target_val: &str = "";
|
|
if let Some(clap_target) = rest_cmd.value_of("target") {
|
|
target_val = clap_target;
|
|
}
|
|
|
|
wasm::run(
|
|
get_vec(rest_cmd.values_of("file")),
|
|
rest_cmd.is_present("verbose"),
|
|
rest_cmd.is_present("just-decode"),
|
|
rest_cmd.is_present("check-translation"),
|
|
rest_cmd.is_present("print"),
|
|
rest_cmd.is_present("disasm"),
|
|
&get_vec(rest_cmd.values_of("set")),
|
|
target_val,
|
|
rest_cmd.is_present("print-size"),
|
|
rest_cmd.is_present("time-passes"),
|
|
rest_cmd.is_present("value-ranges"),
|
|
)
|
|
};
|
|
|
|
#[cfg(not(feature = "wasm"))]
|
|
let result = Err("Error: clif-util was compiled without wasm support.".to_owned());
|
|
|
|
result
|
|
}
|
|
("bugpoint", Some(rest_cmd)) => {
|
|
let mut target_val: &str = "";
|
|
if let Some(clap_target) = rest_cmd.value_of("target") {
|
|
target_val = clap_target;
|
|
}
|
|
|
|
bugpoint::run(
|
|
rest_cmd.value_of("single-file").unwrap(),
|
|
&get_vec(rest_cmd.values_of("set")),
|
|
target_val,
|
|
rest_cmd.is_present("verbose"),
|
|
)
|
|
}
|
|
_ => Err("Invalid subcommand.".to_owned()),
|
|
};
|
|
|
|
if let Err(mut msg) = res_util {
|
|
if !msg.ends_with('\n') {
|
|
msg.push('\n');
|
|
}
|
|
io::stdout().flush().expect("flushing stdout");
|
|
io::stderr().write_all(msg.as_bytes()).unwrap();
|
|
process::exit(1);
|
|
}
|
|
}
|