281 lines
8.7 KiB
Rust
281 lines
8.7 KiB
Rust
//! CLI tool to use the functions provided by the [cranelift-wasm](../cranelift_wasm/index.html)
|
|
//! crate.
|
|
//!
|
|
//! Reads Wasm binary/text files, translates the functions' code to Cranelift IR.
|
|
#![cfg_attr(
|
|
feature = "cargo-clippy",
|
|
allow(clippy::too_many_arguments, clippy::cognitive_complexity)
|
|
)]
|
|
|
|
use crate::disasm::{print_all, PrintRelocs, PrintStackmaps, PrintTraps};
|
|
use crate::utils::{parse_sets_and_triple, read_to_end};
|
|
use cranelift_codegen::ir::DisplayFunctionAnnotations;
|
|
use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error};
|
|
use cranelift_codegen::settings::FlagsOrIsa;
|
|
use cranelift_codegen::timing;
|
|
use cranelift_codegen::Context;
|
|
use cranelift_entity::EntityRef;
|
|
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use term;
|
|
use wabt::{wat2wasm_with_features, Features};
|
|
|
|
macro_rules! vprintln {
|
|
($x: expr, $($tts:tt)*) => {
|
|
if $x {
|
|
println!($($tts)*);
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! vprint {
|
|
($x: expr, $($tts:tt)*) => {
|
|
if $x {
|
|
print!($($tts)*);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn run(
|
|
files: Vec<String>,
|
|
flag_verbose: bool,
|
|
flag_just_decode: bool,
|
|
flag_check_translation: bool,
|
|
flag_print: bool,
|
|
flag_print_disasm: bool,
|
|
flag_set: &[String],
|
|
flag_triple: &str,
|
|
flag_print_size: bool,
|
|
flag_report_times: bool,
|
|
flag_calc_value_ranges: bool,
|
|
flag_enable_simd: bool,
|
|
flag_enable_multi_value: bool,
|
|
) -> Result<(), String> {
|
|
let parsed = parse_sets_and_triple(flag_set, flag_triple)?;
|
|
|
|
for filename in files {
|
|
let path = Path::new(&filename);
|
|
let name = String::from(path.as_os_str().to_string_lossy());
|
|
handle_module(
|
|
flag_verbose,
|
|
flag_just_decode,
|
|
flag_check_translation,
|
|
flag_print,
|
|
flag_print_size,
|
|
flag_print_disasm,
|
|
flag_report_times,
|
|
flag_calc_value_ranges,
|
|
flag_enable_simd,
|
|
flag_enable_multi_value,
|
|
&path.to_path_buf(),
|
|
&name,
|
|
parsed.as_fisa(),
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_module(
|
|
flag_verbose: bool,
|
|
flag_just_decode: bool,
|
|
flag_check_translation: bool,
|
|
flag_print: bool,
|
|
flag_print_size: bool,
|
|
flag_print_disasm: bool,
|
|
flag_report_times: bool,
|
|
flag_calc_value_ranges: bool,
|
|
flag_enable_simd: bool,
|
|
flag_enable_multi_value: bool,
|
|
path: &PathBuf,
|
|
name: &str,
|
|
fisa: FlagsOrIsa,
|
|
) -> Result<(), String> {
|
|
let mut terminal = term::stdout().unwrap();
|
|
let _ = terminal.fg(term::color::YELLOW);
|
|
vprint!(flag_verbose, "Handling: ");
|
|
let _ = terminal.reset();
|
|
vprintln!(flag_verbose, "\"{}\"", name);
|
|
let _ = terminal.fg(term::color::MAGENTA);
|
|
vprint!(flag_verbose, "Translating... ");
|
|
let _ = terminal.reset();
|
|
|
|
let mut module_binary = read_to_end(path.clone()).map_err(|err| err.to_string())?;
|
|
|
|
if !module_binary.starts_with(&[b'\0', b'a', b's', b'm']) {
|
|
let mut features = Features::new();
|
|
if flag_enable_simd {
|
|
features.enable_simd();
|
|
}
|
|
if flag_enable_multi_value {
|
|
features.enable_multi_value();
|
|
}
|
|
|
|
module_binary = match wat2wasm_with_features(&module_binary, features) {
|
|
Ok(data) => data,
|
|
Err(e) => return Err(e.to_string()),
|
|
};
|
|
}
|
|
|
|
let isa = match fisa.isa {
|
|
Some(isa) => isa,
|
|
None => {
|
|
return Err(String::from(
|
|
"Error: the wasm command requires an explicit isa.",
|
|
));
|
|
}
|
|
};
|
|
|
|
let debug_info = flag_calc_value_ranges;
|
|
let mut dummy_environ =
|
|
DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns, debug_info);
|
|
translate_module(&module_binary, &mut dummy_environ).map_err(|e| e.to_string())?;
|
|
|
|
let _ = terminal.fg(term::color::GREEN);
|
|
vprintln!(flag_verbose, "ok");
|
|
let _ = terminal.reset();
|
|
|
|
if flag_just_decode {
|
|
if !flag_print {
|
|
return Ok(());
|
|
}
|
|
|
|
let num_func_imports = dummy_environ.get_num_func_imports();
|
|
for (def_index, func) in dummy_environ.info.function_bodies.iter() {
|
|
let func_index = num_func_imports + def_index.index();
|
|
let mut context = Context::new();
|
|
context.func = func.clone();
|
|
if let Some(start_func) = dummy_environ.info.start_func {
|
|
if func_index == start_func.index() {
|
|
println!("; Selected as wasm start function");
|
|
}
|
|
}
|
|
vprintln!(flag_verbose, "");
|
|
for export_name in
|
|
&dummy_environ.info.functions[FuncIndex::new(func_index)].export_names
|
|
{
|
|
println!("; Exported as \"{}\"", export_name);
|
|
}
|
|
println!("{}", context.func.display(None));
|
|
vprintln!(flag_verbose, "");
|
|
}
|
|
let _ = terminal.reset();
|
|
return Ok(());
|
|
}
|
|
|
|
let _ = terminal.fg(term::color::MAGENTA);
|
|
if flag_check_translation {
|
|
vprint!(flag_verbose, "Checking... ");
|
|
} else {
|
|
vprint!(flag_verbose, "Compiling... ");
|
|
}
|
|
let _ = terminal.reset();
|
|
|
|
if flag_print_size {
|
|
vprintln!(flag_verbose, "");
|
|
}
|
|
|
|
let num_func_imports = dummy_environ.get_num_func_imports();
|
|
let mut total_module_code_size = 0;
|
|
let mut context = Context::new();
|
|
for (def_index, func) in dummy_environ.info.function_bodies.iter() {
|
|
context.func = func.clone();
|
|
|
|
let mut saved_sizes = None;
|
|
let func_index = num_func_imports + def_index.index();
|
|
let mut mem = vec![];
|
|
let mut relocs = PrintRelocs::new(flag_print);
|
|
let mut traps = PrintTraps::new(flag_print);
|
|
let mut stackmaps = PrintStackmaps::new(flag_print);
|
|
if flag_check_translation {
|
|
if let Err(errors) = context.verify(fisa) {
|
|
return Err(pretty_verifier_error(&context.func, fisa.isa, None, errors));
|
|
}
|
|
} else {
|
|
let code_info = context
|
|
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stackmaps)
|
|
.map_err(|err| pretty_error(&context.func, fisa.isa, err))?;
|
|
|
|
if flag_print_size {
|
|
println!(
|
|
"Function #{} code size: {} bytes",
|
|
func_index, code_info.total_size,
|
|
);
|
|
total_module_code_size += code_info.total_size;
|
|
println!(
|
|
"Function #{} bytecode size: {} bytes",
|
|
func_index,
|
|
dummy_environ.func_bytecode_sizes[def_index.index()]
|
|
);
|
|
}
|
|
|
|
if flag_print_disasm {
|
|
saved_sizes = Some((
|
|
code_info.code_size,
|
|
code_info.jumptables_size + code_info.rodata_size,
|
|
));
|
|
}
|
|
}
|
|
|
|
if flag_print {
|
|
vprintln!(flag_verbose, "");
|
|
if let Some(start_func) = dummy_environ.info.start_func {
|
|
if func_index == start_func.index() {
|
|
println!("; Selected as wasm start function");
|
|
}
|
|
}
|
|
for export_name in
|
|
&dummy_environ.info.functions[FuncIndex::new(func_index)].export_names
|
|
{
|
|
println!("; Exported as \"{}\"", export_name);
|
|
}
|
|
let value_ranges = if flag_calc_value_ranges {
|
|
Some(
|
|
context
|
|
.build_value_labels_ranges(isa)
|
|
.expect("value location ranges"),
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
println!(
|
|
"{}",
|
|
context.func.display_with(DisplayFunctionAnnotations {
|
|
isa: fisa.isa,
|
|
value_ranges: value_ranges.as_ref(),
|
|
})
|
|
);
|
|
vprintln!(flag_verbose, "");
|
|
}
|
|
|
|
if let Some((code_size, rodata_size)) = saved_sizes {
|
|
print_all(
|
|
isa,
|
|
&mem,
|
|
code_size,
|
|
rodata_size,
|
|
&relocs,
|
|
&traps,
|
|
&stackmaps,
|
|
)?;
|
|
}
|
|
|
|
context.clear();
|
|
}
|
|
|
|
if !flag_check_translation && flag_print_size {
|
|
println!("Total module code size: {} bytes", total_module_code_size);
|
|
let total_bytecode_size: usize = dummy_environ.func_bytecode_sizes.iter().sum();
|
|
println!("Total module bytecode size: {} bytes", total_bytecode_size);
|
|
}
|
|
|
|
if flag_report_times {
|
|
println!("{}", timing::take_current());
|
|
}
|
|
|
|
let _ = terminal.fg(term::color::GREEN);
|
|
vprintln!(flag_verbose, "ok");
|
|
let _ = terminal.reset();
|
|
Ok(())
|
|
}
|