Dumped code from the wasm2cretonne repo.

Integrated wasm test suite translation as cretonne test

Fixes #146.
Fixes #143.
This commit is contained in:
Denis Merigoux
2017-08-10 16:05:04 -07:00
committed by Jakob Stoklund Olesen
parent e8276ed965
commit ee9989c4b9
20 changed files with 2804 additions and 5 deletions

View File

@@ -15,11 +15,15 @@ path = "src/cton-util.rs"
[dependencies]
cretonne = { path = "lib/cretonne" }
cretonne-reader = { path = "lib/reader" }
cretonne-frontend = { path ="lib/frontend" }
cretonne-frontend = { path = "lib/frontend" }
cretonne-wasm = { path = "lib/wasm" }
filecheck = { path = "lib/filecheck" }
wasmparser = "0.6.1"
docopt = "0.8.0"
serde = "1.0.8"
serde_derive = "1.0.8"
num_cpus = "1.5.1"
tempdir="0.3.5"
term = "0.4.6"
[workspace]

View File

@@ -1,12 +1,16 @@
#[macro_use(dbg)]
extern crate cretonne;
extern crate cton_reader;
extern crate cretonne_wasm;
extern crate wasmparser;
extern crate docopt;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate filecheck;
extern crate num_cpus;
extern crate tempdir;
extern crate term;
use cretonne::VERSION;
use docopt::Docopt;
@@ -18,6 +22,7 @@ mod filetest;
mod cat;
mod print_cfg;
mod rsfilecheck;
mod wasm;
const USAGE: &str = "
Cretonne code generator utility
@@ -27,12 +32,15 @@ Usage:
cton-util cat <file>...
cton-util filecheck [-v] <file>
cton-util print-cfg <file>...
cton-util wasm [-cvo] <file>...
cton-util --help | --version
Options:
-v, --verbose be more verbose
-h, --help print this help message
--version print the Cretonne version
-v, --verbose be more verbose
-c, --check checks the correctness of Cretonne IL translated from WebAssembly
-o, --optimize runs otpimization passes on translated WebAssembly functions
-h, --help print this help message
--version print the Cretonne version
";
@@ -42,7 +50,10 @@ struct Args {
cmd_cat: bool,
cmd_filecheck: bool,
cmd_print_cfg: bool,
cmd_wasm: bool,
arg_file: Vec<String>,
flag_check: bool,
flag_optimize: bool,
flag_verbose: bool,
}
@@ -69,6 +80,11 @@ fn cton_util() -> CommandResult {
rsfilecheck::run(args.arg_file, args.flag_verbose)
} else if args.cmd_print_cfg {
print_cfg::run(args.arg_file)
} else if args.cmd_wasm {
wasm::run(args.arg_file,
args.flag_verbose,
args.flag_optimize,
args.flag_check)
} else {
// Debugging / shouldn't happen with proper command line handling above.
Err(format!("Unhandled args: {:?}", args))

227
cranelift/src/wasm.rs Normal file
View File

@@ -0,0 +1,227 @@
//! CLI tool to use the functions provided by crates [wasm2cretonne](../wasm2cretonne/index.html)
//! and [wasmstandalone](../wasmstandalone/index.html).
//!
//! Reads Wasm binary files (one Wasm module per file), translates the functions' code to Cretonne
//! IL. Can also executes the `start` function of the module by laying out the memories, globals
//! and tables, then emitting the translated code with hardcoded addresses to memory.
use cretonne_wasm::{translate_module, FunctionTranslation, DummyRuntime, WasmRuntime};
use std::path::PathBuf;
use cretonne::loop_analysis::LoopAnalysis;
use cretonne::flowgraph::ControlFlowGraph;
use cretonne::dominator_tree::DominatorTree;
use cretonne::Context;
use cretonne::result::CtonError;
use cretonne::ir;
use cretonne::ir::entities::AnyEntity;
use cretonne::isa::TargetIsa;
use cretonne::verifier;
use std::fs::File;
use std::error::Error;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tempdir::TempDir;
use term;
macro_rules! vprintln {
($x: expr, $($tts:tt)*) => {
if $x {
println!($($tts)*);
}
}
}
macro_rules! vprint {
($x: expr, $($tts:tt)*) => {
if $x {
print!($($tts)*);
}
}
}
fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
let mut buf: Vec<u8> = Vec::new();
let file = File::open(path)?;
let mut buf_reader = BufReader::new(file);
buf_reader.read_to_end(&mut buf)?;
Ok(buf)
}
pub fn run(files: Vec<String>,
flag_verbose: bool,
flag_optimize: bool,
flag_check: bool)
-> Result<(), String> {
for filename in files.iter() {
let path = Path::new(&filename);
let name = String::from(path.as_os_str().to_string_lossy());
match handle_module(flag_verbose,
flag_optimize,
flag_check,
path.to_path_buf(),
name) {
Ok(()) => {}
Err(message) => return Err(message),
}
}
Ok(())
}
fn handle_module(flag_verbose: bool,
flag_optimize: bool,
flag_check: bool,
path: PathBuf,
name: String)
-> Result<(), String> {
let mut terminal = term::stdout().unwrap();
terminal.fg(term::color::YELLOW).unwrap();
vprint!(flag_verbose, "Handling: ");
terminal.reset().unwrap();
vprintln!(flag_verbose, "\"{}\"", name);
terminal.fg(term::color::MAGENTA).unwrap();
vprint!(flag_verbose, "Translating...");
terminal.reset().unwrap();
let data = match path.extension() {
None => {
return Err(String::from("the file extension is not wasm or wast"));
}
Some(ext) => {
match ext.to_str() {
Some("wasm") => {
match read_wasm_file(path.clone()) {
Ok(data) => data,
Err(err) => {
return Err(String::from(err.description()));
}
}
}
Some("wast") => {
let tmp_dir = TempDir::new("wasm2cretonne").unwrap();
let file_path = tmp_dir.path().join("module.wasm");
File::create(file_path.clone()).unwrap();
Command::new("wast2wasm")
.arg(path.clone())
.arg("-o")
.arg(file_path.to_str().unwrap())
.output()
.or_else(|e| if let io::ErrorKind::NotFound = e.kind() {
return Err(String::from("wast2wasm not found"));
} else {
return Err(String::from(e.description()));
})
.unwrap();
match read_wasm_file(file_path) {
Ok(data) => data,
Err(err) => {
return Err(String::from(err.description()));
}
}
}
None | Some(&_) => {
return Err(String::from("the file extension is not wasm or wast"));
}
}
}
};
let mut dummy_runtime = DummyRuntime::new();
let translation = {
let mut runtime: &mut WasmRuntime = &mut dummy_runtime;
match translate_module(&data, runtime) {
Ok(x) => x,
Err(string) => {
return Err(string);
}
}
};
terminal.fg(term::color::GREEN).unwrap();
vprintln!(flag_verbose, " ok");
terminal.reset().unwrap();
if flag_check {
terminal.fg(term::color::MAGENTA).unwrap();
vprint!(flag_verbose, "Checking... ");
terminal.reset().unwrap();
for func in translation.functions.iter() {
let il = match func {
&FunctionTranslation::Import() => continue,
&FunctionTranslation::Code { ref il, .. } => il.clone(),
};
match verifier::verify_function(&il, None) {
Ok(()) => (),
Err(err) => return Err(pretty_verifier_error(&il, None, err)),
}
}
terminal.fg(term::color::GREEN).unwrap();
vprintln!(flag_verbose, " ok");
terminal.reset().unwrap();
}
if flag_optimize {
terminal.fg(term::color::MAGENTA).unwrap();
vprint!(flag_verbose, "Optimizing... ");
terminal.reset().unwrap();
for func in translation.functions.iter() {
let mut il = match func {
&FunctionTranslation::Import() => continue,
&FunctionTranslation::Code { ref il, .. } => il.clone(),
};
let mut loop_analysis = LoopAnalysis::new();
let mut cfg = ControlFlowGraph::new();
cfg.compute(&il);
let mut domtree = DominatorTree::new();
domtree.compute(&mut il, &cfg);
loop_analysis.compute(&mut il, &mut cfg, &mut domtree);
let mut context = Context::new();
context.func = il;
context.cfg = cfg;
context.domtree = domtree;
context.loop_analysis = loop_analysis;
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, None) {
Ok(()) => (),
Err(err) => {
return Err(pretty_verifier_error(&context.func, None, err));
}
};
match context.licm() {
Ok(())=> (),
Err(error) => {
match error {
CtonError::Verifier(err) => {
return Err(pretty_verifier_error(&context.func, None, err));
}
CtonError::ImplLimitExceeded |
CtonError::CodeTooLarge => return Err(String::from(error.description())),
}
}
};
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, None) {
Ok(()) => (),
Err(err) => return Err(pretty_verifier_error(&context.func, None, err)),
}
}
terminal.fg(term::color::GREEN).unwrap();
vprintln!(flag_verbose, " ok");
terminal.reset().unwrap();
}
Ok(())
}
/// Pretty-print a verifier error.
pub fn pretty_verifier_error(func: &ir::Function,
isa: Option<&TargetIsa>,
err: verifier::Error)
-> String {
let msg = err.to_string();
let str1 = match err.location {
AnyEntity::Inst(inst) => {
format!("{}\n{}: {}\n\n",
msg,
inst,
func.dfg.display_inst(inst, isa))
}
_ => String::from(format!("{}\n", msg)),
};
format!("{}{}", str1, func.display(isa))
}

View File

@@ -40,7 +40,8 @@ if [ -n "$needcheck" ]; then
touch $tsfile || echo no target directory
fi
PKGS="cretonne cretonne-reader cretonne-tools cretonne-frontend filecheck"
PKGS="cretonne cretonne-reader cretonne-tools cretonne-frontend cretonne-wasm \
filecheck "
cd "$topdir"
for PKG in $PKGS
do

View File

@@ -0,0 +1,13 @@
(module
(memory 1)
(func $main (local i32)
(set_local 0 (i32.sub (i32.const 4) (i32.const 4)))
(if
(get_local 0)
(then unreachable)
(else (drop (i32.mul (i32.const 6) (get_local 0))))
)
)
(start $main)
(data (i32.const 0) "abcdefgh")
)

View File

@@ -0,0 +1,10 @@
(module
(func $main (local i32)
(set_local 0 (i32.const 0))
(drop (call $inc))
)
(func $inc (result i32)
(i32.const 1)
)
(start $main)
)

View File

@@ -0,0 +1,22 @@
(module
(memory 1)
(func $main (local i32 i32 i32 i32)
(set_local 0 (i32.const 0))
(set_local 1 (i32.const 1))
(set_local 2 (i32.const 1))
(set_local 3 (i32.const 0))
(block
(loop
(br_if 1 (i32.gt_s (get_local 0) (i32.const 5)))
(set_local 3 (get_local 2))
(set_local 2 (i32.add (get_local 2) (get_local 1)))
(set_local 1 (get_local 3))
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
(br 0)
)
)
(i32.store (i32.const 0) (get_local 2))
)
(start $main)
(data (i32.const 0) "0000")
)

View File

@@ -0,0 +1,8 @@
(module
(global $x (mut i32) (i32.const 4))
(memory 1)
(func $main (local i32)
(i32.store (i32.const 0) (get_global $x))
)
(start $main)
)

View File

@@ -0,0 +1,11 @@
(module
(memory 1)
(func $main (local i32)
(i32.store (i32.const 0) (i32.const 0x0))
(if (i32.load (i32.const 0))
(then (i32.store (i32.const 0) (i32.const 0xa)))
(else (i32.store (i32.const 0) (i32.const 0xb))))
)
(start $main)
(data (i32.const 0) "0000")
)