diff --git a/Cargo.toml b/Cargo.toml index 6df15bd86c..7fdbee1e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ 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" } +wasm2cretonne-util = { path = "lib/wasm2cretonne-util" } filecheck = { path = "lib/filecheck" } docopt = "0.8.0" serde = "1.0.8" diff --git a/lib/wasm2cretonne-util/Cargo.toml b/lib/wasm2cretonne-util/Cargo.toml new file mode 100644 index 0000000000..a4f3eb94a6 --- /dev/null +++ b/lib/wasm2cretonne-util/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "wasm2cretonne-util" +version = "0.0.0" +authors = ["The Cretonne Project Developers"] +publish = false + +[[bin]] +name = "wasm2cretonne-util" +path = "src/main.rs" + +[dependencies] +wasm2cretonne = { path = "../wasm2cretonne" } +wasmstandalone = { path = "../wasmstandalone" } +wasmparser = "0.6.1" +cretonne = { path = "../cretonne" } +cretonne-frontend = { path = "../frontend" } +wasmtext = { git = "https://github.com/yurydelendik/wasmtext" } +docopt = "0.8.0" +serde = "1.0.8" +serde_derive = "1.0.8" +term = "*" +tempdir="*" diff --git a/lib/wasm2cretonne-util/README.md b/lib/wasm2cretonne-util/README.md new file mode 100644 index 0000000000..a3f63c05c6 --- /dev/null +++ b/lib/wasm2cretonne-util/README.md @@ -0,0 +1,62 @@ +# wasm2cretonne + +[Cretonne](https://github.com/stoklund/cretonne) frontend for WebAssembly. Reads wasm binary modules and translate the functions it contains into Cretonne IL functions. + +The translation needs some info about the runtime in order to handle the wasm instructions `get_global`, `set_global`, and `call_indirect`. These informations are included in structs implementing the `WasmRuntime` trait like `DummyRuntime` or `StandaloneRuntime`. + + +The `StandaloneRuntime` is a setup for in-memory execution of the module just after translation to Cretonne IL. It allocates memory for the wasm linear memories, the globals and the tables and embeds the addresses of these memories inside the generated Cretonne IL functions. Then it runs Cretonne's compilation, emits the code to memory and executes the `start` function of the module. + +## API + +Use the functions defined in the crates `wasm2cretonne` and `wasmruntime`. + +### Example + +```rust +use wasm2cretonne::translate_module; +use wasmruntime::{StandaloneRuntime, compile_module, execute}; +use std::path::{Path, PathBuf}; + +fn read_wasm_file(path: PathBuf) -> Result, io::Error> { + let mut buf: Vec = Vec::new(); + let file = File::open(path)?; + let mut buf_reader = BufReader::new(file); + buf_reader.read_to_end(&mut buf)?; + Ok(buf) +} + +let path = Path::new("filetests/arith.wasm"); +let data = match read_wasm_file(path.to_path_buf()) { + Ok(data) => data, + Err(err) => { + panic!("Error: {}", err); + } +}; +let mut runtime = StandaloneRuntime::new(); +let translation = match translate_module(&data, &mut runtime) { + Ok(x) => x, + Err(string) => { + panic!(string); + } +}; +let exec = compile_module(&translation, "intel"); +execute(exec); +println!("Memory after execution: {:?}", runtime.inspect_memory(0,0,4)); +``` + +## CLI tool + +The binary created by the root crate of this repo is an utility to parse, translate, compile and execute wasm binaries using Cretonne. Usage: + +``` +wasm2cretonne-util + -v, --verbose displays info on the different steps + -p, --print displays the module and translated functions + -c, --check checks the corectness of the translated functions + -o, --optimize runs optimization passes on the translated functions + -e, --execute enable the standalone runtime and executes the start function of the module + -m, --memory interactive memory inspector after execution +``` + +The tool reads `.wasm` files but also `.wast` as long as the [WebAssembly binary toolkit](https://github.com/WebAssembly/wabt)'s `wast2wasm` executable is accessible in your `PATH`. For now, only the 64 bits Intel architecture is supported for execution. diff --git a/lib/wasm2cretonne-util/filetests/arith.wasm b/lib/wasm2cretonne-util/filetests/arith.wasm new file mode 100644 index 0000000000..a393264a2a Binary files /dev/null and b/lib/wasm2cretonne-util/filetests/arith.wasm differ diff --git a/lib/wasm2cretonne-util/filetests/arith.wast b/lib/wasm2cretonne-util/filetests/arith.wast new file mode 100644 index 0000000000..fa7115696b --- /dev/null +++ b/lib/wasm2cretonne-util/filetests/arith.wast @@ -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") +) diff --git a/lib/wasm2cretonne-util/filetests/call.wasm b/lib/wasm2cretonne-util/filetests/call.wasm new file mode 100644 index 0000000000..c64ad73d1d Binary files /dev/null and b/lib/wasm2cretonne-util/filetests/call.wasm differ diff --git a/lib/wasm2cretonne-util/filetests/call.wast b/lib/wasm2cretonne-util/filetests/call.wast new file mode 100644 index 0000000000..e8640d2342 --- /dev/null +++ b/lib/wasm2cretonne-util/filetests/call.wast @@ -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) +) diff --git a/lib/wasm2cretonne-util/filetests/globals.wasm b/lib/wasm2cretonne-util/filetests/globals.wasm new file mode 100644 index 0000000000..8b319b6bcd Binary files /dev/null and b/lib/wasm2cretonne-util/filetests/globals.wasm differ diff --git a/lib/wasm2cretonne-util/filetests/globals.wast b/lib/wasm2cretonne-util/filetests/globals.wast new file mode 100644 index 0000000000..646e5f0f45 --- /dev/null +++ b/lib/wasm2cretonne-util/filetests/globals.wast @@ -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) +) diff --git a/lib/wasm2cretonne-util/filetests/memory.wasm b/lib/wasm2cretonne-util/filetests/memory.wasm new file mode 100644 index 0000000000..0d3074bb7e Binary files /dev/null and b/lib/wasm2cretonne-util/filetests/memory.wasm differ diff --git a/lib/wasm2cretonne-util/filetests/memory.wast b/lib/wasm2cretonne-util/filetests/memory.wast new file mode 100644 index 0000000000..0c81bad174 --- /dev/null +++ b/lib/wasm2cretonne-util/filetests/memory.wast @@ -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") +) diff --git a/lib/wasm2cretonne-util/filetests/sample.wasm b/lib/wasm2cretonne-util/filetests/sample.wasm new file mode 100644 index 0000000000..0126305329 Binary files /dev/null and b/lib/wasm2cretonne-util/filetests/sample.wasm differ diff --git a/lib/wasm2cretonne-util/src/main.rs b/lib/wasm2cretonne-util/src/main.rs new file mode 100644 index 0000000000..8a1c7c95d3 --- /dev/null +++ b/lib/wasm2cretonne-util/src/main.rs @@ -0,0 +1,440 @@ +//! 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. + +extern crate wasm2cretonne; +extern crate wasmstandalone; +extern crate wasmparser; +extern crate cretonne; +extern crate wasmtext; +extern crate docopt; +extern crate serde; +#[macro_use] +extern crate serde_derive; +extern crate term; +extern crate tempdir; + +use wasm2cretonne::{translate_module, TranslationResult, FunctionTranslation, DummyRuntime, + WasmRuntime}; +use wasmstandalone::{StandaloneRuntime, compile_module, execute}; +use std::path::PathBuf; +use wasmparser::{Parser, ParserState, WasmDecoder, SectionCode}; +use wasmtext::Writer; +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, stdout}; +use std::io::prelude::*; +use docopt::Docopt; +use std::path::Path; +use std::process::Command; +use tempdir::TempDir; + +macro_rules! vprintln { + ($x: expr, $($tts:tt)*) => { + if $x { + println!($($tts)*); + } + } +} + +macro_rules! vprint { + ($x: expr, $($tts:tt)*) => { + if $x { + print!($($tts)*); + } + } +} + +const USAGE: &str = " +Wasm to Cretonne IL translation utility. +Takes a binary WebAssembly module and returns its functions in Cretonne IL format. +The translation is dependent on the runtime chosen. +The default is a dummy runtime that produces placeholder values. + +Usage: + wasm2cretonne-util [-vcop] ... + wasm2cretonne-util -e [-mvcop] ... + wasm2cretonne-util --help | --version + +Options: + -v, --verbose displays info on the different steps + -p, --print displays the module and translated functions + -c, --check checks the corectness of the translated functions + -o, --optimize runs optimization passes on the translated functions + -e, --execute enable the standalone runtime and executes the start function of the module + -m, --memory interactive memory inspector after execution + -h, --help print this help message + --version print the Cretonne version +"; + +#[derive(Deserialize, Debug, Clone)] +struct Args { + arg_file: Vec, + flag_verbose: bool, + flag_execute: bool, + flag_memory: bool, + flag_check: bool, + flag_optimize: bool, + flag_print: bool, +} + +fn read_wasm_file(path: PathBuf) -> Result, io::Error> { + let mut buf: Vec = Vec::new(); + let file = File::open(path)?; + let mut buf_reader = BufReader::new(file); + buf_reader.read_to_end(&mut buf)?; + Ok(buf) +} + + +fn main() { + let args: Args = Docopt::new(USAGE) + .and_then(|d| d.help(true).version(Some(format!("0.0.0"))).deserialize()) + .unwrap_or_else(|e| e.exit()); + let mut terminal = term::stdout().unwrap(); + for filename in args.arg_file.iter() { + let path = Path::new(&filename); + let name = String::from(path.as_os_str().to_string_lossy()); + match handle_module(&args, path.to_path_buf(), name) { + Ok(()) => {} + Err(message) => { + terminal.fg(term::color::RED).unwrap(); + vprintln!(args.flag_verbose, "error"); + terminal.reset().unwrap(); + vprintln!(args.flag_verbose, "{}", message) + } + } + } +} + +fn handle_module(args: &Args, path: PathBuf, name: String) -> Result<(), String> { + let mut terminal = term::stdout().unwrap(); + terminal.fg(term::color::YELLOW).unwrap(); + vprint!(args.flag_verbose, "Handling: "); + terminal.reset().unwrap(); + vprintln!(args.flag_verbose, "\"{}\"", name); + terminal.fg(term::color::MAGENTA).unwrap(); + vprint!(args.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 mut standalone_runtime = StandaloneRuntime::new(); + let translation = { + let mut runtime: &mut WasmRuntime = if args.flag_execute { + &mut standalone_runtime + } else { + &mut dummy_runtime + }; + match translate_module(&data, runtime) { + Ok(x) => x, + Err(string) => { + return Err(string); + } + } + }; + terminal.fg(term::color::GREEN).unwrap(); + vprintln!(args.flag_verbose, " ok"); + terminal.reset().unwrap(); + if args.flag_check { + terminal.fg(term::color::MAGENTA).unwrap(); + vprint!(args.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!(args.flag_verbose, " ok"); + terminal.reset().unwrap(); + } + if args.flag_print { + let mut writer1 = stdout(); + let mut writer2 = stdout(); + match pretty_print_translation(&name, &data, &translation, &mut writer1, &mut writer2) { + Err(error) => return Err(String::from(error.description())), + Ok(()) => (), + } + } + if args.flag_optimize { + terminal.fg(term::color::MAGENTA).unwrap(); + vprint!(args.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!(args.flag_verbose, " ok"); + terminal.reset().unwrap(); + } + if args.flag_execute { + terminal.fg(term::color::MAGENTA).unwrap(); + vprint!(args.flag_verbose, "Compiling... "); + terminal.reset().unwrap(); + match compile_module(&translation) { + Ok(exec) => { + terminal.fg(term::color::GREEN).unwrap(); + vprintln!(args.flag_verbose, "ok"); + terminal.reset().unwrap(); + terminal.fg(term::color::MAGENTA).unwrap(); + vprint!(args.flag_verbose, "Executing... "); + terminal.reset().unwrap(); + match execute(exec) { + Ok(()) => { + terminal.fg(term::color::GREEN).unwrap(); + vprintln!(args.flag_verbose, "ok"); + terminal.reset().unwrap(); + } + Err(s) => { + return Err(s); + } + } + } + Err(s) => { + return Err(s); + } + }; + if args.flag_memory { + let mut input = String::new(); + terminal.fg(term::color::YELLOW).unwrap(); + println!("Inspecting memory"); + terminal.fg(term::color::MAGENTA).unwrap(); + println!("Type 'quit' to exit."); + terminal.reset().unwrap(); + loop { + input.clear(); + terminal.fg(term::color::YELLOW).unwrap(); + print!("Memory index, offset, length (e.g. 0,0,4): "); + terminal.reset().unwrap(); + let _ = stdout().flush(); + match io::stdin().read_line(&mut input) { + Ok(_) => { + input.pop(); + if input == "quit" { + break; + } + let split: Vec<&str> = input.split(",").collect(); + if split.len() != 3 { + break; + } + let memory = standalone_runtime + .inspect_memory(str::parse(split[0]).unwrap(), + str::parse(split[1]).unwrap(), + str::parse(split[2]).unwrap()); + let mut s = memory + .iter() + .fold(String::from("#"), |mut acc, byte| { + acc.push_str(format!("{:02x}_", byte).as_str()); + acc + }); + s.pop(); + println!("{}", s); + } + Err(error) => return Err(String::from(error.description())), + } + } + } + } + Ok(()) +} + +// Prints out a Wasm module, and for each function the corresponding translation in Cretonne IL. +fn pretty_print_translation(filename: &String, + data: &Vec, + translation: &TranslationResult, + writer_wast: &mut Write, + writer_cretonne: &mut Write) + -> Result<(), io::Error> { + let mut terminal = term::stdout().unwrap(); + let mut parser = Parser::new(data.as_slice()); + let mut parser_writer = Writer::new(writer_wast); + let imports_count = translation + .functions + .iter() + .fold(0, |acc, &ref f| match f { + &FunctionTranslation::Import() => acc + 1, + &FunctionTranslation::Code { .. } => acc, + }); + match parser.read() { + s @ &ParserState::BeginWasm { .. } => parser_writer.write(&s)?, + _ => panic!("modules should begin properly"), + } + loop { + match parser.read() { + s @ &ParserState::BeginSection { code: SectionCode::Code, .. } => { + // The code section begins + parser_writer.write(&s)?; + break; + } + &ParserState::EndWasm => return Ok(()), + s @ _ => parser_writer.write(&s)?, + } + } + let mut function_index = 0; + loop { + match parser.read() { + s @ &ParserState::BeginFunctionBody { .. } => { + terminal.fg(term::color::BLUE).unwrap(); + write!(writer_cretonne, + "====== Function No. {} of module \"{}\" ======\n", + function_index, + filename)?; + terminal.fg(term::color::CYAN).unwrap(); + write!(writer_cretonne, "Wast ---------->\n")?; + terminal.reset().unwrap(); + parser_writer.write(&s)?; + } + s @ &ParserState::EndSection => { + parser_writer.write(&s)?; + break; + } + _ => panic!("wrong content in code section"), + } + { + loop { + match parser.read() { + s @ &ParserState::EndFunctionBody => { + parser_writer.write(&s)?; + break; + } + s @ _ => { + parser_writer.write(&s)?; + } + }; + } + } + let mut function_string = + format!(" {}", + match translation.functions[function_index + imports_count] { + FunctionTranslation::Code { ref il, .. } => il, + FunctionTranslation::Import() => panic!("should not happen"), + } + .display(None)); + function_string.pop(); + let function_str = str::replace(function_string.as_str(), "\n", "\n "); + terminal.fg(term::color::CYAN).unwrap(); + write!(writer_cretonne, "Cretonne IL --->\n")?; + terminal.reset().unwrap(); + write!(writer_cretonne, "{}\n", function_str)?; + function_index += 1; + } + loop { + match parser.read() { + &ParserState::EndWasm => return Ok(()), + s @ _ => parser_writer.write(&s)?, + } + } +} + +/// 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)) +} diff --git a/lib/wasm2cretonne-util/testsuite/address.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/address.wast.0.wasm new file mode 100644 index 0000000000..bf81db7cc8 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/address.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/binary.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/binary.wast.0.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/binary.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/binary.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/binary.wast.1.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/binary.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/binary.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/binary.wast.2.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/binary.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/binary.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/binary.wast.3.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/binary.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/block.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/block.wast.0.wasm new file mode 100644 index 0000000000..a5f96555a0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/block.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/br.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/br.wast.0.wasm new file mode 100644 index 0000000000..f4131e83b6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/br.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/br_if.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/br_if.wast.0.wasm new file mode 100644 index 0000000000..3c91d8350b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/br_if.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/br_table.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/br_table.wast.0.wasm new file mode 100644 index 0000000000..09b895d2b0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/br_table.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/break-drop.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/break-drop.wast.0.wasm new file mode 100644 index 0000000000..57a108d478 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/break-drop.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/call.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/call.wast.0.wasm new file mode 100644 index 0000000000..83a7f79217 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/call.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/call_indirect.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/call_indirect.wast.0.wasm new file mode 100644 index 0000000000..974170480d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/call_indirect.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/comments.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/comments.wast.0.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/comments.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/comments.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/comments.wast.1.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/comments.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/comments.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/comments.wast.2.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/comments.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/comments.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/comments.wast.3.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/comments.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/conversions.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/conversions.wast.0.wasm new file mode 100644 index 0000000000..ddebe20d3d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/conversions.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/custom_section.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/custom_section.wast.0.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/custom_section.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/custom_section.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/custom_section.wast.1.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/custom_section.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/custom_section.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/custom_section.wast.2.wasm new file mode 100644 index 0000000000..8b19588df2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/custom_section.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/endianness.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/endianness.wast.0.wasm new file mode 100644 index 0000000000..d26c99d06c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/endianness.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.0.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.1.wasm new file mode 100644 index 0000000000..505f4fe7a4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.10.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.10.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.10.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.11.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.11.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.11.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.18.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.18.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.18.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.19.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.19.wasm new file mode 100644 index 0000000000..f1bae6aa24 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.19.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.2.wasm new file mode 100644 index 0000000000..0ec4916edc Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.20.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.20.wasm new file mode 100644 index 0000000000..3c2410be16 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.20.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.21.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.21.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.21.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.22.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.22.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.22.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.23.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.23.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.23.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.24.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.24.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.24.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.25.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.25.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.25.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.26.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.26.wasm new file mode 100644 index 0000000000..a2f90fe113 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.26.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.27.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.27.wasm new file mode 100644 index 0000000000..a9893a2a7e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.27.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.28.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.28.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.28.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.29.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.29.wasm new file mode 100644 index 0000000000..d8fc92d022 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.29.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.3.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.36.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.36.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.36.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.37.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.37.wasm new file mode 100644 index 0000000000..1f2cfccf74 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.37.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.38.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.38.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.38.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.39.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.39.wasm new file mode 100644 index 0000000000..48fb7293d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.39.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.4.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.40.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.40.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.40.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.41.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.41.wasm new file mode 100644 index 0000000000..48fb7293d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.41.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.42.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.42.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.42.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.43.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.43.wasm new file mode 100644 index 0000000000..48fb7293d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.43.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.44.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.44.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.44.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.45.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.45.wasm new file mode 100644 index 0000000000..48fb7293d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.45.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.46.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.46.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.46.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.47.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.47.wasm new file mode 100644 index 0000000000..48fb7293d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.47.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.48.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.48.wasm new file mode 100644 index 0000000000..539a89bf19 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.48.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.49.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.49.wasm new file mode 100644 index 0000000000..48fb7293d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.49.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.5.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.55.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.55.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.55.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.56.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.56.wasm new file mode 100644 index 0000000000..6696dc1f5f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.56.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.57.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.57.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.57.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.58.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.58.wasm new file mode 100644 index 0000000000..f6bda9d18c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.58.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.59.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.59.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.59.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.6.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.60.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.60.wasm new file mode 100644 index 0000000000..f6bda9d18c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.60.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.61.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.61.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.61.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.62.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.62.wasm new file mode 100644 index 0000000000..f6bda9d18c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.62.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.63.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.63.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.63.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.64.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.64.wasm new file mode 100644 index 0000000000..f6bda9d18c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.64.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.65.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.65.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.65.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.66.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.66.wasm new file mode 100644 index 0000000000..f6bda9d18c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.66.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.67.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.67.wasm new file mode 100644 index 0000000000..2981c2461c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.67.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.68.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.68.wasm new file mode 100644 index 0000000000..f6bda9d18c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.68.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.7.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.7.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.7.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.8.wasm new file mode 100644 index 0000000000..fff82363ca Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/exports.wast.9.wasm b/lib/wasm2cretonne-util/testsuite/exports.wast.9.wasm new file mode 100644 index 0000000000..615bb04c6f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/exports.wast.9.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/f32.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/f32.wast.0.wasm new file mode 100644 index 0000000000..9bde5ff260 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/f32.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/f32_bitwise.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/f32_bitwise.wast.0.wasm new file mode 100644 index 0000000000..76817cfe13 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/f32_bitwise.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/f32_cmp.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/f32_cmp.wast.0.wasm new file mode 100644 index 0000000000..54042ae839 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/f32_cmp.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/f64.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/f64.wast.0.wasm new file mode 100644 index 0000000000..69c3e69ca4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/f64.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/f64_bitwise.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/f64_bitwise.wast.0.wasm new file mode 100644 index 0000000000..8c71ce01a4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/f64_bitwise.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/f64_cmp.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/f64_cmp.wast.0.wasm new file mode 100644 index 0000000000..db5c2f565f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/f64_cmp.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/fac.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/fac.wast.0.wasm new file mode 100644 index 0000000000..9c3af8127a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/fac.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.0.wasm new file mode 100644 index 0000000000..93fc9ddbc2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.1.wasm new file mode 100644 index 0000000000..638a2124f4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.10.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.10.wasm new file mode 100644 index 0000000000..63bf9ab6ea Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.10.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.11.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.11.wasm new file mode 100644 index 0000000000..f459847b96 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.11.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.12.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.12.wasm new file mode 100644 index 0000000000..b3480b8159 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.12.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.13.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.13.wasm new file mode 100644 index 0000000000..109d98ede9 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.13.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.14.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.14.wasm new file mode 100644 index 0000000000..6ec49ca9fa Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.14.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.15.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.15.wasm new file mode 100644 index 0000000000..c01271abfa Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.15.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.16.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.16.wasm new file mode 100644 index 0000000000..ce28899f48 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.16.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.17.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.17.wasm new file mode 100644 index 0000000000..40f19d0710 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.17.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.18.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.18.wasm new file mode 100644 index 0000000000..ed67b7aff3 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.18.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.19.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.19.wasm new file mode 100644 index 0000000000..3cb6e63980 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.19.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.2.wasm new file mode 100644 index 0000000000..a33c750123 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.20.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.20.wasm new file mode 100644 index 0000000000..054222cdcf Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.20.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.21.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.21.wasm new file mode 100644 index 0000000000..b0bf7a0a4d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.21.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.22.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.22.wasm new file mode 100644 index 0000000000..4d5a0ac68e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.22.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.23.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.23.wasm new file mode 100644 index 0000000000..fe96aa795b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.23.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.24.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.24.wasm new file mode 100644 index 0000000000..3781066407 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.24.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.25.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.25.wasm new file mode 100644 index 0000000000..57ab86234e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.25.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.26.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.26.wasm new file mode 100644 index 0000000000..8d41cd60ff Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.26.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.27.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.27.wasm new file mode 100644 index 0000000000..a5b348cb7d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.27.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.28.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.28.wasm new file mode 100644 index 0000000000..72c035d96f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.28.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.29.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.29.wasm new file mode 100644 index 0000000000..9e81c03093 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.29.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.3.wasm new file mode 100644 index 0000000000..58fb098688 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.30.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.30.wasm new file mode 100644 index 0000000000..093e0789a1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.30.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.31.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.31.wasm new file mode 100644 index 0000000000..f03455aa23 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.31.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.32.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.32.wasm new file mode 100644 index 0000000000..710445bc00 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.32.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.33.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.33.wasm new file mode 100644 index 0000000000..54a29b9df6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.33.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.34.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.34.wasm new file mode 100644 index 0000000000..a1a99c443e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.34.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.35.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.35.wasm new file mode 100644 index 0000000000..c3abad956d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.35.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.36.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.36.wasm new file mode 100644 index 0000000000..59ffbb5d87 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.36.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.37.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.37.wasm new file mode 100644 index 0000000000..d1cd0f57ef Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.37.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.38.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.38.wasm new file mode 100644 index 0000000000..ca28d5e4d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.38.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.39.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.39.wasm new file mode 100644 index 0000000000..20d1715bd5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.39.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.4.wasm new file mode 100644 index 0000000000..8c4561e205 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.40.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.40.wasm new file mode 100644 index 0000000000..d363f76fb0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.40.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.41.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.41.wasm new file mode 100644 index 0000000000..c6b9e24fb7 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.41.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.42.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.42.wasm new file mode 100644 index 0000000000..f194fc1498 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.42.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.43.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.43.wasm new file mode 100644 index 0000000000..ce2b73f7ab Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.43.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.44.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.44.wasm new file mode 100644 index 0000000000..9650b94d6e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.44.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.45.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.45.wasm new file mode 100644 index 0000000000..d8d69bbb20 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.45.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.46.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.46.wasm new file mode 100644 index 0000000000..b017655622 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.46.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.47.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.47.wasm new file mode 100644 index 0000000000..33dc257ff2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.47.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.48.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.48.wasm new file mode 100644 index 0000000000..5be5f22c3a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.48.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.49.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.49.wasm new file mode 100644 index 0000000000..b754cc9d95 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.49.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.5.wasm new file mode 100644 index 0000000000..20ec463aa5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.50.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.50.wasm new file mode 100644 index 0000000000..3fd1baca49 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.50.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.51.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.51.wasm new file mode 100644 index 0000000000..f39c98d057 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.51.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.52.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.52.wasm new file mode 100644 index 0000000000..61021fcf69 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.52.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.53.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.53.wasm new file mode 100644 index 0000000000..4e3bde4866 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.53.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.54.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.54.wasm new file mode 100644 index 0000000000..1572cc4298 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.54.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.55.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.55.wasm new file mode 100644 index 0000000000..fa832af0e4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.55.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.56.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.56.wasm new file mode 100644 index 0000000000..ef8bec6e15 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.56.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.57.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.57.wasm new file mode 100644 index 0000000000..15959e6329 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.57.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.58.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.58.wasm new file mode 100644 index 0000000000..b8870a873c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.58.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.59.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.59.wasm new file mode 100644 index 0000000000..1184279628 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.59.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.6.wasm new file mode 100644 index 0000000000..9ba8b8660c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.60.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.60.wasm new file mode 100644 index 0000000000..2387d2d982 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.60.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.61.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.61.wasm new file mode 100644 index 0000000000..c17811c135 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.61.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.62.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.62.wasm new file mode 100644 index 0000000000..3e6561f5f4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.62.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.63.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.63.wasm new file mode 100644 index 0000000000..fba3bcf972 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.63.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.64.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.64.wasm new file mode 100644 index 0000000000..ca400ce4cb Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.64.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.65.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.65.wasm new file mode 100644 index 0000000000..f5ddcf462e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.65.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.66.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.66.wasm new file mode 100644 index 0000000000..13ba4d5a54 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.66.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.67.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.67.wasm new file mode 100644 index 0000000000..5b0cb31b80 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.67.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.68.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.68.wasm new file mode 100644 index 0000000000..39d64d72d6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.68.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.69.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.69.wasm new file mode 100644 index 0000000000..410dfbc269 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.69.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.7.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.7.wasm new file mode 100644 index 0000000000..76b8308189 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.7.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.70.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.70.wasm new file mode 100644 index 0000000000..05c684522c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.70.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.71.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.71.wasm new file mode 100644 index 0000000000..fe18d15da7 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.71.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.72.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.72.wasm new file mode 100644 index 0000000000..eef3768648 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.72.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.73.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.73.wasm new file mode 100644 index 0000000000..abbf750b18 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.73.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.74.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.74.wasm new file mode 100644 index 0000000000..add2c0ab20 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.74.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.75.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.75.wasm new file mode 100644 index 0000000000..315d6494e2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.75.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.76.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.76.wasm new file mode 100644 index 0000000000..10b8195680 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.76.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.77.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.77.wasm new file mode 100644 index 0000000000..18103680c6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.77.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.78.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.78.wasm new file mode 100644 index 0000000000..c25f9c43af Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.78.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.79.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.79.wasm new file mode 100644 index 0000000000..687aad0de1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.79.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.8.wasm new file mode 100644 index 0000000000..bac9a68a77 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.80.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.80.wasm new file mode 100644 index 0000000000..226f08ce9b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.80.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.81.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.81.wasm new file mode 100644 index 0000000000..e6489b12ee Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.81.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.82.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.82.wasm new file mode 100644 index 0000000000..b0ba33f992 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.82.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.83.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.83.wasm new file mode 100644 index 0000000000..b348011399 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.83.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.84.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.84.wasm new file mode 100644 index 0000000000..413a384164 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.84.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.85.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.85.wasm new file mode 100644 index 0000000000..7f542c9297 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.85.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.86.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.86.wasm new file mode 100644 index 0000000000..f8608e0c59 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.86.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.87.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.87.wasm new file mode 100644 index 0000000000..8bc5cd88ff Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.87.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.88.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.88.wasm new file mode 100644 index 0000000000..66f2bfa699 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.88.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.89.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.89.wasm new file mode 100644 index 0000000000..7fefd64cbe Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.89.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.9.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.9.wasm new file mode 100644 index 0000000000..1f070302c4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.9.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.90.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.90.wasm new file mode 100644 index 0000000000..7e53886a4c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.90.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.91.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.91.wasm new file mode 100644 index 0000000000..97da0e6c04 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.91.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.92.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.92.wasm new file mode 100644 index 0000000000..be60329b83 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.92.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.93.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.93.wasm new file mode 100644 index 0000000000..da5cc6ed34 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.93.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.94.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.94.wasm new file mode 100644 index 0000000000..7403a43dc7 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.94.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_exprs.wast.95.wasm b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.95.wasm new file mode 100644 index 0000000000..5933e6544e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_exprs.wast.95.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_literals.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/float_literals.wast.0.wasm new file mode 100644 index 0000000000..7ce5d39c83 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_literals.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_memory.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/float_memory.wast.0.wasm new file mode 100644 index 0000000000..05258ff16b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_memory.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_memory.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/float_memory.wast.1.wasm new file mode 100644 index 0000000000..c46b327da3 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_memory.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_memory.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/float_memory.wast.2.wasm new file mode 100644 index 0000000000..9174f1c7b7 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_memory.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_memory.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/float_memory.wast.3.wasm new file mode 100644 index 0000000000..3e56542a04 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_memory.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_memory.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/float_memory.wast.4.wasm new file mode 100644 index 0000000000..587914ae12 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_memory.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_memory.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/float_memory.wast.5.wasm new file mode 100644 index 0000000000..d384d39e30 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_memory.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/float_misc.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/float_misc.wast.0.wasm new file mode 100644 index 0000000000..351164b0a8 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/float_misc.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/forward.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/forward.wast.0.wasm new file mode 100644 index 0000000000..7ac7c552c4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/forward.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/func.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/func.wast.0.wasm new file mode 100644 index 0000000000..cda0735226 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/func.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.0.wasm new file mode 100644 index 0000000000..8916eb2579 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.8.wasm new file mode 100644 index 0000000000..6826a72c9a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.9.wasm b/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.9.wasm new file mode 100644 index 0000000000..f77e78a8a9 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/func_ptrs.wast.9.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/get_local.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/get_local.wast.0.wasm new file mode 100644 index 0000000000..cb39060eec Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/get_local.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/globals.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/globals.wast.0.wasm new file mode 100644 index 0000000000..653d8dfbd4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/globals.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/globals.wast.16.wasm b/lib/wasm2cretonne-util/testsuite/globals.wast.16.wasm new file mode 100644 index 0000000000..4418d51804 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/globals.wast.16.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/globals.wast.19.wasm b/lib/wasm2cretonne-util/testsuite/globals.wast.19.wasm new file mode 100644 index 0000000000..f634698e68 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/globals.wast.19.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/i32.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/i32.wast.0.wasm new file mode 100644 index 0000000000..b516c4dfd0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/i32.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/i64.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/i64.wast.0.wasm new file mode 100644 index 0000000000..1eeba31df4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/i64.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/if.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/if.wast.0.wasm new file mode 100644 index 0000000000..49e2b1ad8f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/if.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.0.wasm new file mode 100644 index 0000000000..57fd691a02 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.1.wasm new file mode 100644 index 0000000000..44cafbbeda Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.10.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.10.wasm new file mode 100644 index 0000000000..2f5d2a6a74 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.10.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.11.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.11.wasm new file mode 100644 index 0000000000..fd7a0a7965 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.11.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.12.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.12.wasm new file mode 100644 index 0000000000..a67a230aa1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.12.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.13.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.13.wasm new file mode 100644 index 0000000000..fc0333420a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.13.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.14.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.14.wasm new file mode 100644 index 0000000000..1b10aa7138 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.14.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.15.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.15.wasm new file mode 100644 index 0000000000..1d576d9bd1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.15.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.16.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.16.wasm new file mode 100644 index 0000000000..01e697f21c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.16.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.17.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.17.wasm new file mode 100644 index 0000000000..885d5bd9b2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.17.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.18.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.18.wasm new file mode 100644 index 0000000000..d6647f6b58 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.18.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.19.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.19.wasm new file mode 100644 index 0000000000..55f8407e74 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.19.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.2.wasm new file mode 100644 index 0000000000..767dd4cab0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.20.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.20.wasm new file mode 100644 index 0000000000..83d154c9d5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.20.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.21.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.21.wasm new file mode 100644 index 0000000000..6072362cc6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.21.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.22.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.22.wasm new file mode 100644 index 0000000000..dd6531f10b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.22.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.23.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.23.wasm new file mode 100644 index 0000000000..1104039dad Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.23.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.24.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.24.wasm new file mode 100644 index 0000000000..211ade86ec Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.24.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.25.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.25.wasm new file mode 100644 index 0000000000..76bf13a6c0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.25.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.26.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.26.wasm new file mode 100644 index 0000000000..dd4e01d42d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.26.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.27.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.27.wasm new file mode 100644 index 0000000000..23488f40a8 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.27.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.28.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.28.wasm new file mode 100644 index 0000000000..7b046aaa67 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.28.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.29.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.29.wasm new file mode 100644 index 0000000000..552abb20d1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.29.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.3.wasm new file mode 100644 index 0000000000..8d7f4e4af6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.30.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.30.wasm new file mode 100644 index 0000000000..c2c821468f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.30.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.31.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.31.wasm new file mode 100644 index 0000000000..638e168461 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.31.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.32.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.32.wasm new file mode 100644 index 0000000000..89bfb0a9bf Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.32.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.33.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.33.wasm new file mode 100644 index 0000000000..ab0911f6fa Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.33.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.34.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.34.wasm new file mode 100644 index 0000000000..e382fa9b0f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.34.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.35.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.35.wasm new file mode 100644 index 0000000000..bf0e2b1528 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.35.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.36.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.36.wasm new file mode 100644 index 0000000000..0bd2ff71c4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.36.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.37.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.37.wasm new file mode 100644 index 0000000000..82f7f25d0a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.37.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.38.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.38.wasm new file mode 100644 index 0000000000..cd5a9c959f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.38.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.39.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.39.wasm new file mode 100644 index 0000000000..4760358307 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.39.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.4.wasm new file mode 100644 index 0000000000..5c1ae26e52 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.40.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.40.wasm new file mode 100644 index 0000000000..826872e84a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.40.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.41.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.41.wasm new file mode 100644 index 0000000000..ea0cd6bbf2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.41.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.42.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.42.wasm new file mode 100644 index 0000000000..29af0f908e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.42.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.43.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.43.wasm new file mode 100644 index 0000000000..ee499e9268 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.43.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.44.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.44.wasm new file mode 100644 index 0000000000..27aa8136ef Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.44.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.45.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.45.wasm new file mode 100644 index 0000000000..27aa8136ef Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.45.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.49.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.49.wasm new file mode 100644 index 0000000000..3cf0083692 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.49.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.5.wasm new file mode 100644 index 0000000000..07b93fb1a4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.50.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.50.wasm new file mode 100644 index 0000000000..d4cfcd9b50 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.50.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.51.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.51.wasm new file mode 100644 index 0000000000..e0ce1decbd Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.51.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.52.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.52.wasm new file mode 100644 index 0000000000..27d3f4d906 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.52.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.53.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.53.wasm new file mode 100644 index 0000000000..431bc7e67d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.53.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.54.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.54.wasm new file mode 100644 index 0000000000..1e25eb2549 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.54.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.55.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.55.wasm new file mode 100644 index 0000000000..8c61b5bd7d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.55.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.56.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.56.wasm new file mode 100644 index 0000000000..2a6b158a1a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.56.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.57.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.57.wasm new file mode 100644 index 0000000000..a098343f39 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.57.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.58.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.58.wasm new file mode 100644 index 0000000000..bf2cd933c8 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.58.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.59.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.59.wasm new file mode 100644 index 0000000000..60cf1991dd Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.59.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.6.wasm new file mode 100644 index 0000000000..10e75e56a3 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.60.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.60.wasm new file mode 100644 index 0000000000..0d52289f71 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.60.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.61.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.61.wasm new file mode 100644 index 0000000000..c3e0390f53 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.61.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.62.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.62.wasm new file mode 100644 index 0000000000..9f17b38fc3 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.62.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.63.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.63.wasm new file mode 100644 index 0000000000..62d775f65d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.63.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.64.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.64.wasm new file mode 100644 index 0000000000..8130164ea5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.64.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.65.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.65.wasm new file mode 100644 index 0000000000..68ee3ca26f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.65.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.66.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.66.wasm new file mode 100644 index 0000000000..ac4bc9f248 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.66.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.67.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.67.wasm new file mode 100644 index 0000000000..86d66cffd9 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.67.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.68.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.68.wasm new file mode 100644 index 0000000000..5101c52d66 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.68.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.69.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.69.wasm new file mode 100644 index 0000000000..ad4cf5e006 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.69.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.7.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.7.wasm new file mode 100644 index 0000000000..0cca060359 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.7.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.70.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.70.wasm new file mode 100644 index 0000000000..551315ad82 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.70.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.71.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.71.wasm new file mode 100644 index 0000000000..551315ad82 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.71.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.75.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.75.wasm new file mode 100644 index 0000000000..01e7c8deb2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.75.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.76.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.76.wasm new file mode 100644 index 0000000000..a1146c50ab Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.76.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.77.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.77.wasm new file mode 100644 index 0000000000..b966a19092 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.77.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.78.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.78.wasm new file mode 100644 index 0000000000..a7db97093e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.78.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.79.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.79.wasm new file mode 100644 index 0000000000..19592e8b9d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.79.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.8.wasm new file mode 100644 index 0000000000..843504ae3d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.80.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.80.wasm new file mode 100644 index 0000000000..9c81a86595 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.80.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.81.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.81.wasm new file mode 100644 index 0000000000..4d0cb1a9d4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.81.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.82.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.82.wasm new file mode 100644 index 0000000000..0fd00827c5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.82.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.83.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.83.wasm new file mode 100644 index 0000000000..1d55b9bcff Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.83.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.84.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.84.wasm new file mode 100644 index 0000000000..6f794ae335 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.84.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.85.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.85.wasm new file mode 100644 index 0000000000..20ed7a1956 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.85.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.86.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.86.wasm new file mode 100644 index 0000000000..969740f29a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.86.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.87.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.87.wasm new file mode 100644 index 0000000000..bd3ae5ba71 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.87.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.88.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.88.wasm new file mode 100644 index 0000000000..877a2e563d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.88.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.89.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.89.wasm new file mode 100644 index 0000000000..1acf047dd5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.89.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.9.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.9.wasm new file mode 100644 index 0000000000..2a48ca2958 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.9.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.90.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.90.wasm new file mode 100644 index 0000000000..3c7cc625f8 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.90.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.91.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.91.wasm new file mode 100644 index 0000000000..788006a408 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.91.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.92.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.92.wasm new file mode 100644 index 0000000000..de0a76f947 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.92.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.93.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.93.wasm new file mode 100644 index 0000000000..e9bd718944 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.93.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.94.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.94.wasm new file mode 100644 index 0000000000..4dd394f853 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.94.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.95.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.95.wasm new file mode 100644 index 0000000000..7cc4a3cba0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.95.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.96.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.96.wasm new file mode 100644 index 0000000000..877a2e563d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.96.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.97.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.97.wasm new file mode 100644 index 0000000000..1acf047dd5 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.97.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/imports.wast.98.wasm b/lib/wasm2cretonne-util/testsuite/imports.wast.98.wasm new file mode 100644 index 0000000000..2b92957d10 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/imports.wast.98.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.0.wasm new file mode 100644 index 0000000000..363ee730eb Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.1.wasm new file mode 100644 index 0000000000..b764f35181 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.10.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.10.wasm new file mode 100644 index 0000000000..f83f6b8d3d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.10.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.11.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.11.wasm new file mode 100644 index 0000000000..ceaf709859 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.11.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.12.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.12.wasm new file mode 100644 index 0000000000..64199159e1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.12.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.13.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.13.wasm new file mode 100644 index 0000000000..d07ab17528 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.13.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.14.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.14.wasm new file mode 100644 index 0000000000..0df895aa46 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.14.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.15.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.15.wasm new file mode 100644 index 0000000000..ef7d53fd6c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.15.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.16.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.16.wasm new file mode 100644 index 0000000000..498f0b557f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.16.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.17.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.17.wasm new file mode 100644 index 0000000000..b0e10e0028 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.17.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.18.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.18.wasm new file mode 100644 index 0000000000..5efa19fa74 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.18.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.2.wasm new file mode 100644 index 0000000000..4e4b5e4877 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.3.wasm new file mode 100644 index 0000000000..df82315c1f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.4.wasm new file mode 100644 index 0000000000..0ac7512019 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.5.wasm new file mode 100644 index 0000000000..8321359ba1 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.6.wasm new file mode 100644 index 0000000000..d29c13d515 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.7.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.7.wasm new file mode 100644 index 0000000000..c988103c7f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.7.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.8.wasm new file mode 100644 index 0000000000..16204e74da Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_exprs.wast.9.wasm b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.9.wasm new file mode 100644 index 0000000000..4b9734ec29 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_exprs.wast.9.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/int_literals.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/int_literals.wast.0.wasm new file mode 100644 index 0000000000..ff5a26f1ed Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/int_literals.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/labels.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/labels.wast.0.wasm new file mode 100644 index 0000000000..6e893c43b8 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/labels.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/left-to-right.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/left-to-right.wast.0.wasm new file mode 100644 index 0000000000..29a2475c86 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/left-to-right.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.0.wasm new file mode 100644 index 0000000000..f9137a286a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.1.wasm new file mode 100644 index 0000000000..8d2a6f5762 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.15.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.15.wasm new file mode 100644 index 0000000000..4616241323 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.15.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.16.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.16.wasm new file mode 100644 index 0000000000..5d1cf1eb97 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.16.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.17.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.17.wasm new file mode 100644 index 0000000000..f61d36722b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.17.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.2.wasm new file mode 100644 index 0000000000..2aba3e42d7 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.3.wasm new file mode 100644 index 0000000000..d5e336c80d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.4.wasm new file mode 100644 index 0000000000..8a7332cd59 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.5.wasm new file mode 100644 index 0000000000..0b76a6338a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.6.wasm new file mode 100644 index 0000000000..7b2e142a3b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.7.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.7.wasm new file mode 100644 index 0000000000..39ea09809a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.7.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.8.wasm new file mode 100644 index 0000000000..231032f16f Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/linking.wast.9.wasm b/lib/wasm2cretonne-util/testsuite/linking.wast.9.wasm new file mode 100644 index 0000000000..c881d37668 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/linking.wast.9.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/loop.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/loop.wast.0.wasm new file mode 100644 index 0000000000..3e69bf667d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/loop.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.0.wasm new file mode 100644 index 0000000000..93b6da7662 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.1.wasm new file mode 100644 index 0000000000..e47e0cf8d4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.15.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.15.wasm new file mode 100644 index 0000000000..6591570f72 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.15.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.2.wasm new file mode 100644 index 0000000000..64ffe0388b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.3.wasm new file mode 100644 index 0000000000..d3e6416146 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.39.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.39.wasm new file mode 100644 index 0000000000..77ba1f041e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.39.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.40.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.40.wasm new file mode 100644 index 0000000000..64197c7ffb Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.40.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.41.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.41.wasm new file mode 100644 index 0000000000..8b535ce73c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.41.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.49.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.49.wasm new file mode 100644 index 0000000000..60ae51fd8b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.49.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.50.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.50.wasm new file mode 100644 index 0000000000..ebf4341625 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.50.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.51.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.51.wasm new file mode 100644 index 0000000000..2da5f34088 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.51.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.52.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.52.wasm new file mode 100644 index 0000000000..0d6f3fe08a Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.52.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.6.wasm new file mode 100644 index 0000000000..f9528a2f71 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.62.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.62.wasm new file mode 100644 index 0000000000..b59b220575 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.62.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/memory.wast.8.wasm new file mode 100644 index 0000000000..1d5ecb7b10 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory_redundancy.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/memory_redundancy.wast.0.wasm new file mode 100644 index 0000000000..ac41ccc6b0 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory_redundancy.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory_trap.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/memory_trap.wast.0.wasm new file mode 100644 index 0000000000..7171e88566 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory_trap.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/memory_trap.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/memory_trap.wast.1.wasm new file mode 100644 index 0000000000..c4e7acd77e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/memory_trap.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/names.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/names.wast.0.wasm new file mode 100644 index 0000000000..6e34bb08ad Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/names.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/names.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/names.wast.1.wasm new file mode 100644 index 0000000000..c6ac4b4d26 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/names.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/names.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/names.wast.2.wasm new file mode 100644 index 0000000000..cadc3c568d Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/names.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/names.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/names.wast.3.wasm new file mode 100644 index 0000000000..bb83511383 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/names.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/nop.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/nop.wast.0.wasm new file mode 100644 index 0000000000..c59a8cd83c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/nop.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/reloc.wasm b/lib/wasm2cretonne-util/testsuite/reloc.wasm new file mode 100644 index 0000000000..4f18ef6c98 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/reloc.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/resizing.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/resizing.wast.0.wasm new file mode 100644 index 0000000000..44fec0b342 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/resizing.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/resizing.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/resizing.wast.1.wasm new file mode 100644 index 0000000000..45e0f4e5fa Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/resizing.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/resizing.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/resizing.wast.2.wasm new file mode 100644 index 0000000000..01cee6f1ee Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/resizing.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/return.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/return.wast.0.wasm new file mode 100644 index 0000000000..72ef8c5b7c Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/return.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/select.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/select.wast.0.wasm new file mode 100644 index 0000000000..fceb0289bc Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/select.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/set_local.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/set_local.wast.0.wasm new file mode 100644 index 0000000000..6f60f4507b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/set_local.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/simple.wasm b/lib/wasm2cretonne-util/testsuite/simple.wasm new file mode 100644 index 0000000000..4e7c773ae2 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/simple.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/skip-stack-guard-page.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/skip-stack-guard-page.wast.0.wasm new file mode 100644 index 0000000000..79d487b41b Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/skip-stack-guard-page.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/stack.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/stack.wast.0.wasm new file mode 100644 index 0000000000..1b639cfdcf Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/stack.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/start.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/start.wast.3.wasm new file mode 100644 index 0000000000..21b795c5e4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/start.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/start.wast.4.wasm b/lib/wasm2cretonne-util/testsuite/start.wast.4.wasm new file mode 100644 index 0000000000..21b795c5e4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/start.wast.4.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/start.wast.5.wasm b/lib/wasm2cretonne-util/testsuite/start.wast.5.wasm new file mode 100644 index 0000000000..7a4b81947e Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/start.wast.5.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/start.wast.6.wasm b/lib/wasm2cretonne-util/testsuite/start.wast.6.wasm new file mode 100644 index 0000000000..fccdf74aba Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/start.wast.6.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/start.wast.7.wasm b/lib/wasm2cretonne-util/testsuite/start.wast.7.wasm new file mode 100644 index 0000000000..55403c53b4 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/start.wast.7.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/start.wast.8.wasm b/lib/wasm2cretonne-util/testsuite/start.wast.8.wasm new file mode 100644 index 0000000000..c9c64ba2c3 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/start.wast.8.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/switch.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/switch.wast.0.wasm new file mode 100644 index 0000000000..b20ce9f154 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/switch.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/tee_local.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/tee_local.wast.0.wasm new file mode 100644 index 0000000000..c7490e67c6 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/tee_local.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/traps.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/traps.wast.0.wasm new file mode 100644 index 0000000000..6b36d5c043 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/traps.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/traps.wast.1.wasm b/lib/wasm2cretonne-util/testsuite/traps.wast.1.wasm new file mode 100644 index 0000000000..31d416dfdf Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/traps.wast.1.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/traps.wast.2.wasm b/lib/wasm2cretonne-util/testsuite/traps.wast.2.wasm new file mode 100644 index 0000000000..74e1fe5bf3 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/traps.wast.2.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/traps.wast.3.wasm b/lib/wasm2cretonne-util/testsuite/traps.wast.3.wasm new file mode 100644 index 0000000000..6d98e64442 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/traps.wast.3.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/unreachable.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/unreachable.wast.0.wasm new file mode 100644 index 0000000000..1615bb9043 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/unreachable.wast.0.wasm differ diff --git a/lib/wasm2cretonne-util/testsuite/unwind.wast.0.wasm b/lib/wasm2cretonne-util/testsuite/unwind.wast.0.wasm new file mode 100644 index 0000000000..6057472111 Binary files /dev/null and b/lib/wasm2cretonne-util/testsuite/unwind.wast.0.wasm differ diff --git a/lib/wasm2cretonne/.gitignore b/lib/wasm2cretonne/.gitignore new file mode 100644 index 0000000000..4308d82204 --- /dev/null +++ b/lib/wasm2cretonne/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/lib/wasm2cretonne/Cargo.toml b/lib/wasm2cretonne/Cargo.toml new file mode 100644 index 0000000000..ef82b76118 --- /dev/null +++ b/lib/wasm2cretonne/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wasm2cretonne" +version = "0.0.0" +authors = ["The Cretonne Project Developers"] +publish = false + +[dependencies] +wasmparser = "0.6.1" +cretonne = { path = "../cretonne" } +cretonne-frontend = { path = "../frontend" } diff --git a/lib/wasm2cretonne/src/code_translator.rs b/lib/wasm2cretonne/src/code_translator.rs new file mode 100644 index 0000000000..208c531812 --- /dev/null +++ b/lib/wasm2cretonne/src/code_translator.rs @@ -0,0 +1,1375 @@ +//! This module contains the bulk of the interesting code performing the translation between +//! WebAssembly and Cretonne IL. +//! +//! The translation is done in one pass, opcode by opcode. Two main data structures are used during +//! code translations: the value stack and the control stack. The value stack mimics the execution +//! of the WebAssembly stack machine: each instruction result is pushed onto the stack and +//! instruction arguments are popped off the stack. Similarly, when encountering a control flow +//! block, it is pushed onto the control stack and popped off when encountering the corresponding +//! `End`. +//! +//! Another data structure, the translation state, records information concerning unreachable code +//! status and about if inserting a return at the end of the function is necessary. +//! +//! Some of the WebAssembly instructions need information about the runtime to be translated: +//! +//! - the loads and stores need the memory base address; +//! - the `get_global` et `set_global` instructions depends on how the globals are implemented; +//! - `current_memory` and `grow_memory` are runtime functions; +//! - `call_indirect` has to translate the function index into the address of where this +//! is; +//! +//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as +//! argument. +use cretonne::ir::{Function, Signature, Value, Type, InstBuilder, FunctionName, Ebb, FuncRef, + SigRef, ExtFuncData, Inst, MemFlags}; +use cretonne::ir::types::*; +use cretonne::ir::immediates::{Ieee32, Ieee64, Offset32}; +use cretonne::ir::condcodes::{IntCC, FloatCC}; +use cton_frontend::{ILBuilder, FunctionBuilder}; +use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate}; +use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local, + GlobalIndex, FunctionIndex, SignatureIndex}; +use std::collections::HashMap; +use runtime::WasmRuntime; +use std::u32; + + +/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following +/// fields: +/// +/// - `destination`: reference to the `Ebb` that will hold the code after the control block; +/// - `return_values`: types of the values returned by the control block; +/// - `original_stack_size`: size of the value stack at the beginning of the control block. +/// +/// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction +/// separating the `true` and `false` branch. The `loop` frame has a `header` field that references +/// the `Ebb` that contains the beginning of the body of the loop. +#[derive(Debug)] +enum ControlStackFrame { + If { + destination: Ebb, + branch_inst: Inst, + return_values: Vec, + original_stack_size: usize, + reachable: bool, + }, + Block { + destination: Ebb, + return_values: Vec, + original_stack_size: usize, + reachable: bool, + }, + Loop { + destination: Ebb, + header: Ebb, + return_values: Vec, + original_stack_size: usize, + reachable: bool, + }, +} + +/// Helper methods for the control stack objects. +impl ControlStackFrame { + fn return_values(&self) -> &[Type] { + match self { + &ControlStackFrame::If { ref return_values, .. } | + &ControlStackFrame::Block { ref return_values, .. } | + &ControlStackFrame::Loop { ref return_values, .. } => return_values.as_slice(), + } + } + fn following_code(&self) -> Ebb { + match self { + &ControlStackFrame::If { destination, .. } | + &ControlStackFrame::Block { destination, .. } | + &ControlStackFrame::Loop { destination, .. } => destination, + } + } + fn br_destination(&self) -> Ebb { + match self { + &ControlStackFrame::If { destination, .. } | + &ControlStackFrame::Block { destination, .. } => destination, + &ControlStackFrame::Loop { header, .. } => header, + } + } + fn original_stack_size(&self) -> usize { + match self { + &ControlStackFrame::If { original_stack_size, .. } | + &ControlStackFrame::Block { original_stack_size, .. } | + &ControlStackFrame::Loop { original_stack_size, .. } => original_stack_size, + } + } + fn is_loop(&self) -> bool { + match self { + &ControlStackFrame::If { .. } | + &ControlStackFrame::Block { .. } => false, + &ControlStackFrame::Loop { .. } => true, + } + } + + fn is_reachable(&self) -> bool { + match self { + &ControlStackFrame::If { reachable, .. } | + &ControlStackFrame::Block { reachable, .. } | + &ControlStackFrame::Loop { reachable, .. } => reachable, + } + } + + fn set_reachable(&mut self) { + match self { + &mut ControlStackFrame::If { ref mut reachable, .. } | + &mut ControlStackFrame::Block { ref mut reachable, .. } | + &mut ControlStackFrame::Loop { ref mut reachable, .. } => *reachable = true, + } + } +} + +/// Contains information passed along during the translation and that records: +/// +/// - if the last instruction added was a `return`; +/// - the depth of the two unreachable control blocks stacks, that are manipulated when translating +/// unreachable code; +/// - all the `Ebb`s referenced by `br_table` instructions, because those are always reachable even +/// if they are at a point of the code that would have been unreachable otherwise. +struct TranslationState { + last_inst_return: bool, + phantom_unreachable_stack_depth: usize, + real_unreachable_stack_depth: usize, +} + +/// Holds mappings between the function and signatures indexes in the Wasm module and their +/// references as imports of the Cretonne IL function. +#[derive(Clone,Debug)] +pub struct FunctionImports { + /// Mappings index in function index space -> index in function local imports + pub functions: HashMap, + /// Mappings index in signature index space -> index in signature local imports + pub signatures: HashMap, +} + +impl FunctionImports { + fn new() -> FunctionImports { + FunctionImports { + functions: HashMap::new(), + signatures: HashMap::new(), + } + } +} + +/// Returns a well-formed Cretonne IL function from a wasm function body and a signature. +pub fn translate_function_body(parser: &mut Parser, + function_index: FunctionIndex, + sig: Signature, + locals: &Vec<(usize, Type)>, + exports: &Option>, + signatures: &Vec, + functions: &Vec, + il_builder: &mut ILBuilder, + runtime: &mut WasmRuntime) + -> Result<(Function, FunctionImports), String> { + runtime.next_function(); + // First we build the Function object with its name and signature + let mut func = Function::new(); + let args_num: usize = sig.argument_types.len(); + let args_types: Vec = sig.argument_types + .iter() + .map(|arg| arg.value_type) + .collect(); + func.signature = sig.clone(); + match exports { + &None => (), + &Some(ref exports) => { + match exports.get(&function_index) { + None => (), + Some(name) => func.name = FunctionName::new(name.clone()), + } + } + } + let mut func_imports = FunctionImports::new(); + let mut stack: Vec = Vec::new(); + let mut control_stack: Vec = Vec::new(); + // We introduce a arbitrary scope for the FunctionBuilder object + { + let mut builder = FunctionBuilder::new(&mut func, il_builder); + let first_ebb = builder.create_ebb(); + builder.switch_to_block(first_ebb, &[]); + builder.seal_block(first_ebb); + for i in 0..args_num { + // First we declare the function arguments' as non-SSA vars because they will be + // accessed by get_local + let arg_value = builder.arg_value(i as usize); + builder.declare_var(Local(i as u32), args_types[i]); + builder.def_var(Local(i as u32), arg_value); + } + // We also declare and initialize to 0 the local variables + let mut local_index = args_num; + for &(loc_count, ty) in locals { + let val = match ty { + I32 => builder.ins().iconst(ty, 0), + I64 => builder.ins().iconst(ty, 0), + F32 => builder.ins().f32const(Ieee32::with_bits(0)), + F64 => builder.ins().f64const(Ieee64::with_bits(0)), + _ => panic!("should not happen"), + }; + for _ in 0..loc_count { + builder.declare_var(Local(local_index as u32), ty); + builder.def_var(Local(local_index as u32), val); + local_index += 1; + } + } + let mut state = TranslationState { + last_inst_return: false, + phantom_unreachable_stack_depth: 0, + real_unreachable_stack_depth: 0, + }; + // We initialize the control stack with the implicit function block + let end_ebb = builder.create_ebb(); + control_stack.push(ControlStackFrame::Block { + destination: end_ebb, + original_stack_size: 0, + return_values: sig.return_types + .iter() + .map(|argty| argty.value_type) + .collect(), + reachable: false, + }); + // Now the main loop that reads every wasm instruction and translates it + loop { + let parser_state = parser.read(); + match *parser_state { + ParserState::CodeOperator(ref op) => { + if state.phantom_unreachable_stack_depth + + state.real_unreachable_stack_depth > 0 { + translate_unreachable_operator(op, + &mut builder, + &mut stack, + &mut control_stack, + &mut state) + } else { + translate_operator(op, + &mut builder, + runtime, + &mut stack, + &mut control_stack, + &mut state, + &sig, + &functions, + &signatures, + &exports, + &mut func_imports) + } + } + + ParserState::EndFunctionBody => break, + _ => return Err(String::from("wrong content in function body")), + } + } + // In WebAssembly, the final return instruction is implicit so we need to build it + // explicitely in Cretonne IL. + if !state.last_inst_return && !builder.is_filled() && + (!builder.is_unreachable() || !builder.is_pristine()) { + let cut_index = stack.len() - sig.return_types.len(); + let return_vals = stack.split_off(cut_index); + builder.ins().return_(return_vals.as_slice()); + } + // Because the function has an implicit block as body, we need to explicitely close it. + let frame = control_stack.pop().unwrap(); + builder.switch_to_block(frame.following_code(), frame.return_values()); + builder.seal_block(frame.following_code()); + // If the block is reachable we also have to include a return instruction in it. + if !builder.is_unreachable() { + stack.truncate(frame.original_stack_size()); + stack.extend_from_slice(builder.ebb_args(frame.following_code())); + let cut_index = stack.len() - sig.return_types.len(); + let return_vals = stack.split_off(cut_index); + builder.ins().return_(return_vals.as_slice()); + } + } + Ok((func, func_imports)) +} + +/// Translates wasm operators into Cretonne IL instructions. Returns `true` if it inserted +/// a return. +fn translate_operator(op: &Operator, + builder: &mut FunctionBuilder, + runtime: &mut WasmRuntime, + stack: &mut Vec, + control_stack: &mut Vec, + state: &mut TranslationState, + sig: &Signature, + functions: &Vec, + signatures: &Vec, + exports: &Option>, + func_imports: &mut FunctionImports) { + state.last_inst_return = false; + // This big match treats all Wasm code operators. + match *op { + /********************************** Locals **************************************** + * `get_local` and `set_local` are treated as non-SSA variables and will completely + * diseappear in the Cretonne Code + ***********************************************************************************/ + Operator::GetLocal { local_index } => stack.push(builder.use_var(Local(local_index))), + Operator::SetLocal { local_index } => { + let val = stack.pop().unwrap(); + builder.def_var(Local(local_index), val); + } + Operator::TeeLocal { local_index } => { + let val = stack.last().unwrap(); + builder.def_var(Local(local_index), *val); + } + /********************************** Globals **************************************** + * `get_global` and `set_global` are handled by the runtime. + ***********************************************************************************/ + Operator::GetGlobal { global_index } => { + let val = runtime.translate_get_global(builder, global_index as GlobalIndex); + stack.push(val); + } + Operator::SetGlobal { global_index } => { + let val = stack.pop().unwrap(); + runtime.translate_set_global(builder, global_index as GlobalIndex, val); + } + /********************************* Stack misc *************************************** + * `drop`, `nop`, `unreachable` and `select`. + ***********************************************************************************/ + Operator::Drop => { + stack.pop(); + } + Operator::Select => { + let cond = stack.pop().unwrap(); + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().select(cond, arg2, arg1)); + } + Operator::Nop => { + // We do nothing + } + Operator::Unreachable => { + builder.ins().trap(); + state.real_unreachable_stack_depth = 1; + } + /***************************** Control flow blocks ********************************** + * When starting a control flow block, we create a new `Ebb` that will hold the code + * after the block, and we push a frame on the control stack. Depending on the type + * of block, we create a new `Ebb` for the body of the block with an associated + * jump instruction. + * + * The `End` instruction pops the last control frame from the control stack, seals + * the destination block (since `br` instructions targeting it only appear inside the + * block and have already been translated) and modify the value stack to use the + * possible `Ebb`'s arguments values. + ***********************************************************************************/ + Operator::Block { ty } => { + let next = builder.create_ebb(); + match type_to_type(&ty) { + Ok(ty_cre) => { + builder.append_ebb_arg(next, ty_cre); + } + Err(_) => {} + } + control_stack.push(ControlStackFrame::Block { + destination: next, + return_values: translate_type(ty).unwrap(), + original_stack_size: stack.len(), + reachable: false, + }); + } + Operator::Loop { ty } => { + let loop_body = builder.create_ebb(); + let next = builder.create_ebb(); + match type_to_type(&ty) { + Ok(ty_cre) => { + builder.append_ebb_arg(next, ty_cre); + } + Err(_) => {} + } + builder.ins().jump(loop_body, &[]); + control_stack.push(ControlStackFrame::Loop { + destination: next, + header: loop_body, + return_values: translate_type(ty).unwrap(), + original_stack_size: stack.len(), + reachable: false, + }); + builder.switch_to_block(loop_body, &[]); + } + Operator::If { ty } => { + let val = stack.pop().unwrap(); + let if_not = builder.create_ebb(); + let jump_inst = builder.ins().brz(val, if_not, &[]); + // Here we append an argument to an Ebb targeted by an argumentless jump instruction + // But in fact there are two cases: + // - either the If does not have a Else clause, in that case ty = EmptyBlock + // and we add nothing; + // - either the If have an Else clause, in that case the destination of this jump + // instruction will be changed later when we translate the Else operator. + match type_to_type(&ty) { + Ok(ty_cre) => { + builder.append_ebb_arg(if_not, ty_cre); + } + Err(_) => {} + } + control_stack.push(ControlStackFrame::If { + destination: if_not, + branch_inst: jump_inst, + return_values: translate_type(ty).unwrap(), + original_stack_size: stack.len(), + reachable: false, + }); + } + Operator::Else => { + // We take the control frame pushed by the if, use its ebb as the else body + // and push a new control frame with a new ebb for the code after the if/then/else + // At the end of the then clause we jump to the destination + let (destination, return_values, branch_inst) = match &control_stack[control_stack.len() - + 1] { + &ControlStackFrame::If { + destination, + ref return_values, + branch_inst, + .. + } => (destination, return_values, branch_inst), + _ => panic!("should not happen"), + }; + let cut_index = stack.len() - return_values.len(); + let jump_args = stack.split_off(cut_index); + builder.ins().jump(destination, jump_args.as_slice()); + // We change the target of the branch instruction + let else_ebb = builder.create_ebb(); + builder.change_jump_destination(branch_inst, else_ebb); + builder.seal_block(else_ebb); + builder.switch_to_block(else_ebb, &[]); + } + Operator::End => { + let frame = control_stack.pop().unwrap(); + if !builder.is_unreachable() || !builder.is_pristine() { + let cut_index = stack.len() - frame.return_values().len(); + let jump_args = stack.split_off(cut_index); + builder + .ins() + .jump(frame.following_code(), jump_args.as_slice()); + } + builder.switch_to_block(frame.following_code(), frame.return_values()); + builder.seal_block(frame.following_code()); + // If it is a loop we also have to seal the body loop block + match frame { + ControlStackFrame::Loop { header, .. } => builder.seal_block(header), + _ => {} + } + stack.truncate(frame.original_stack_size()); + stack.extend_from_slice(builder.ebb_args(frame.following_code())); + } + /**************************** Branch instructions ********************************* + * The branch instructions all have as arguments a target nesting level, which + * corresponds to how many control stack frames do we have to pop to get the + * destination `Ebb`. + * + * Once the destination `Ebb` is found, we sometimes have to declare a certain depth + * of the stack unreachable, because some branch instructions are terminator. + * + * The `br_table` case is much more complicated because Cretonne's `br_table` instruction + * does not support jump arguments like all the other branch instructions. That is why, in + * the case where we would use jump arguments for every other branch instructions, we + * need to split the critical edges leaving the `br_tables` by creating one `Ebb` per + * table destination; the `br_table` will point to these newly created `Ebbs` and these + * `Ebb`s contain only a jump instruction pointing to the final destination, this time with + * jump arguments. + * + * This system is also implemented in Cretonne's SSA construction algorithm, because + * `use_var` located in a destination `Ebb` of a `br_table` might trigger the addition + * of jump arguments in each predecessor branch instruction, one of which might be a + * `br_table`. + ***********************************************************************************/ + Operator::Br { relative_depth } => { + let i = control_stack.len() - 1 - (relative_depth as usize); + let frame = &mut control_stack[i]; + let jump_args = if frame.is_loop() { + Vec::new() + } else { + let cut_index = stack.len() - frame.return_values().len(); + stack.split_off(cut_index) + }; + builder + .ins() + .jump(frame.br_destination(), jump_args.as_slice()); + // We signal that all the code that follows until the next End is unreachable + frame.set_reachable(); + state.real_unreachable_stack_depth = 1 + relative_depth as usize; + } + Operator::BrIf { relative_depth } => { + let val = stack.pop().unwrap(); + let i = control_stack.len() - 1 - (relative_depth as usize); + let frame = &mut control_stack[i]; + let cut_index = stack.len() - frame.return_values().len(); + let jump_args = stack.split_off(cut_index); + builder + .ins() + .brnz(val, frame.br_destination(), jump_args.as_slice()); + // The values returned by the branch are still available for the reachable + // code that comes after it + frame.set_reachable(); + stack.extend(jump_args); + } + Operator::BrTable { ref table } => { + let (depths, default) = table.read_table(); + let mut min_depth = default; + for depth in depths.iter() { + if *depth < min_depth { + min_depth = *depth; + } + } + let jump_args_count = control_stack[control_stack.len() - 1 - (min_depth as usize)] + .return_values() + .len(); + if jump_args_count == 0 { + // No jump arguments + let val = stack.pop().unwrap(); + if depths.len() > 0 { + let jt = builder.create_jump_table(); + for (index, depth) in depths.iter().enumerate() { + let i = control_stack.len() - 1 - (*depth as usize); + let frame = &mut control_stack[i]; + let ebb = frame.br_destination(); + builder.insert_jump_table_entry(jt, index, ebb); + frame.set_reachable(); + } + builder.ins().br_table(val, jt); + } + let i = control_stack.len() - 1 - (default as usize); + let frame = &mut control_stack[i]; + let ebb = frame.br_destination(); + builder.ins().jump(ebb, &[]); + state.real_unreachable_stack_depth = 1 + min_depth as usize; + frame.set_reachable(); + } else { + // Here we have jump arguments, but Cretonne's br_table doesn't support them + // We then proceed to split the edges going out of the br_table + let val = stack.pop().unwrap(); + let cut_index = stack.len() - jump_args_count; + let jump_args = stack.split_off(cut_index); + if depths.len() > 0 { + let jt = builder.create_jump_table(); + let dest_ebbs: HashMap = depths + .iter() + .enumerate() + .fold(HashMap::new(), |mut acc, (index, &depth)| { + if acc.get(&(depth as usize)).is_none() { + let branch_ebb = builder.create_ebb(); + builder.insert_jump_table_entry(jt, index, branch_ebb); + acc.insert(depth as usize, branch_ebb); + return acc; + }; + let branch_ebb = acc.get(&(depth as usize)).unwrap().clone(); + builder.insert_jump_table_entry(jt, index, branch_ebb); + acc + }); + builder.ins().br_table(val, jt); + let default_ebb = control_stack[control_stack.len() - 1 - (default as usize)] + .br_destination(); + builder.ins().jump(default_ebb, jump_args.as_slice()); + stack.extend(jump_args.clone()); + for (depth, dest_ebb) in dest_ebbs { + builder.switch_to_block(dest_ebb, &[]); + builder.seal_block(dest_ebb); + let i = control_stack.len() - 1 - (depth as usize); + let frame = &mut control_stack[i]; + let real_dest_ebb = frame.br_destination(); + builder.ins().jump(real_dest_ebb, jump_args.as_slice()); + frame.set_reachable(); + } + state.real_unreachable_stack_depth = 1 + min_depth as usize; + } else { + let ebb = control_stack[control_stack.len() - 1 - (default as usize)] + .br_destination(); + builder.ins().jump(ebb, jump_args.as_slice()); + stack.extend(jump_args); + state.real_unreachable_stack_depth = 1 + min_depth as usize; + } + } + } + Operator::Return => { + let return_count = sig.return_types.len(); + let cut_index = stack.len() - return_count; + let return_args = stack.split_off(cut_index); + builder.ins().return_(return_args.as_slice()); + state.last_inst_return = true; + state.real_unreachable_stack_depth = 1; + } + /************************************ Calls **************************************** + * The call instructions pop off their arguments from the stack and append their + * return values to it. `call_indirect` needs runtime support because there is an + * argument referring to an index in the external functions table of the module. + ************************************************************************************/ + Operator::Call { function_index } => { + let args_num = args_count(function_index as usize, functions, signatures); + let cut_index = stack.len() - args_num; + let call_args = stack.split_off(cut_index); + let internal_function_index = find_function_import(function_index as usize, + builder, + func_imports, + functions, + exports, + signatures); + let call_inst = builder + .ins() + .call(internal_function_index, call_args.as_slice()); + let ret_values = builder.inst_results(call_inst); + for val in ret_values { + stack.push(*val); + } + } + Operator::CallIndirect { + index, + table_index: _, + } => { + // index is the index of the function's signature and table_index is the index + // of the table to search the function in + // TODO: have runtime support for tables + let sigref = find_signature_import(index as usize, builder, func_imports, signatures); + let args_num = builder.signature(sigref).unwrap().argument_types.len(); + let index_val = stack.pop().unwrap(); + let cut_index = stack.len() - args_num; + let call_args = stack.split_off(cut_index); + let ret_values = + runtime.translate_call_indirect(builder, sigref, index_val, call_args.as_slice()); + for val in ret_values { + stack.push(*val); + } + } + /******************************* Memory management *********************************** + * Memory management is handled by runtime. It is usually translated into calls to + * special functions. + ************************************************************************************/ + Operator::GrowMemory { reserved: _ } => { + let val = stack.pop().unwrap(); + stack.push(runtime.translate_grow_memory(builder, val)); + } + Operator::CurrentMemory { reserved: _ } => { + stack.push(runtime.translate_current_memory(builder)); + } + /******************************* Load instructions *********************************** + * Wasm specifies an integer alignment flag but we drop it in Cretonne. + * The memory base address is provided by the runtime. + * TODO: differentiate between 32 bit and 64 bit architecture, to put the uextend or not + ************************************************************************************/ + Operator::I32Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().uload8(I32, memflags, addr, memoffset)) + } + Operator::I32Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().uload8(I32, memflags, addr, memoffset)) + } + Operator::I32Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().sload8(I32, memflags, addr, memoffset)) + } + Operator::I32Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().sload8(I32, memflags, addr, memoffset)) + } + Operator::I64Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().uload8(I64, memflags, addr, memoffset)) + } + Operator::I64Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().uload16(I64, memflags, addr, memoffset)) + } + Operator::I64Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().sload8(I64, memflags, addr, memoffset)) + } + Operator::I64Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().sload16(I64, memflags, addr, memoffset)) + } + Operator::I64Load32S { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().sload32(memflags, addr, memoffset)) + } + Operator::I64Load32U { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().uload32(memflags, addr, memoffset)) + } + Operator::I32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().load(I32, memflags, addr, memoffset)) + } + Operator::F32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().load(F32, memflags, addr, memoffset)) + } + Operator::I64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().load(I64, memflags, addr, memoffset)) + } + Operator::F64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + stack.push(builder.ins().load(F64, memflags, addr, memoffset)) + } + /****************************** Store instructions *********************************** + * Wasm specifies an integer alignment flag but we drop it in Cretonne. + * The memory base address is provided by the runtime. + * TODO: differentiate between 32 bit and 64 bit architecture, to put the uextend or not + ************************************************************************************/ + Operator::I32Store { memory_immediate: MemoryImmediate { flags: _, offset } } | + Operator::I64Store { memory_immediate: MemoryImmediate { flags: _, offset } } | + Operator::F32Store { memory_immediate: MemoryImmediate { flags: _, offset } } | + Operator::F64Store { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let val = stack.pop().unwrap(); + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + builder.ins().store(memflags, val, addr, memoffset); + } + Operator::I32Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } | + Operator::I64Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let val = stack.pop().unwrap(); + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + builder.ins().istore8(memflags, val, addr, memoffset); + } + Operator::I32Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } | + Operator::I64Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let val = stack.pop().unwrap(); + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + builder.ins().istore16(memflags, val, addr, memoffset); + } + Operator::I64Store32 { memory_immediate: MemoryImmediate { flags: _, offset } } => { + let val = stack.pop().unwrap(); + let address_i32 = stack.pop().unwrap(); + let base = runtime.translate_memory_base_address(builder, 0); + let address_i64 = builder.ins().uextend(I64, address_i32); + let addr = builder.ins().iadd(base, address_i64); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + builder.ins().istore32(memflags, val, addr, memoffset); + } + /****************************** Nullary Operators ************************************/ + Operator::I32Const { value } => stack.push(builder.ins().iconst(I32, value as i64)), + Operator::I64Const { value } => stack.push(builder.ins().iconst(I64, value)), + Operator::F32Const { value } => { + stack.push(builder.ins().f32const(f32_translation(value))); + } + Operator::F64Const { value } => { + stack.push(builder.ins().f64const(f64_translation(value))); + } + /******************************* Unary Operators *************************************/ + Operator::I32Clz => { + let arg = stack.pop().unwrap(); + let val = builder.ins().clz(arg); + stack.push(builder.ins().sextend(I32, val)); + } + Operator::I64Clz => { + let arg = stack.pop().unwrap(); + let val = builder.ins().clz(arg); + stack.push(builder.ins().sextend(I64, val)); + } + Operator::I32Ctz => { + let val = stack.pop().unwrap(); + let short_res = builder.ins().ctz(val); + stack.push(builder.ins().sextend(I32, short_res)); + } + Operator::I64Ctz => { + let val = stack.pop().unwrap(); + let short_res = builder.ins().ctz(val); + stack.push(builder.ins().sextend(I64, short_res)); + } + Operator::I32Popcnt => { + let arg = stack.pop().unwrap(); + let val = builder.ins().popcnt(arg); + stack.push(builder.ins().sextend(I32, val)); + } + Operator::I64Popcnt => { + let arg = stack.pop().unwrap(); + let val = builder.ins().popcnt(arg); + stack.push(builder.ins().sextend(I64, val)); + } + Operator::I64ExtendSI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().sextend(I64, val)); + } + Operator::I64ExtendUI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().uextend(I64, val)); + } + Operator::I32WrapI64 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().ireduce(I32, val)); + } + Operator::F32Sqrt | + Operator::F64Sqrt => { + let arg = stack.pop().unwrap(); + stack.push(builder.ins().sqrt(arg)); + } + Operator::F32Ceil | + Operator::F64Ceil => { + let arg = stack.pop().unwrap(); + stack.push(builder.ins().ceil(arg)); + } + Operator::F32Floor | + Operator::F64Floor => { + let arg = stack.pop().unwrap(); + stack.push(builder.ins().floor(arg)); + } + Operator::F32Trunc | + Operator::F64Trunc => { + let arg = stack.pop().unwrap(); + stack.push(builder.ins().trunc(arg)); + } + Operator::F32Nearest | + Operator::F64Nearest => { + let arg = stack.pop().unwrap(); + stack.push(builder.ins().nearest(arg)); + } + Operator::F32Abs | Operator::F64Abs => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fabs(val)); + } + Operator::F32Neg | Operator::F64Neg => { + let arg = stack.pop().unwrap(); + stack.push(builder.ins().fneg(arg)); + } + Operator::F64ConvertUI64 | + Operator::F64ConvertUI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_from_uint(F64, val)); + } + Operator::F64ConvertSI64 | + Operator::F64ConvertSI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_from_sint(F64, val)); + } + Operator::F32ConvertSI64 | + Operator::F32ConvertSI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_from_sint(F32, val)); + } + Operator::F32ConvertUI64 | + Operator::F32ConvertUI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_from_uint(F32, val)); + } + Operator::F64PromoteF32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fpromote(F64, val)); + } + Operator::F32DemoteF64 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fdemote(F32, val)); + } + Operator::I64TruncSF64 | + Operator::I64TruncSF32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_to_sint(I64, val)); + } + Operator::I32TruncSF64 | + Operator::I32TruncSF32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_to_sint(I32, val)); + } + Operator::I64TruncUF64 | + Operator::I64TruncUF32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_to_uint(I64, val)); + } + Operator::I32TruncUF64 | + Operator::I32TruncUF32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().fcvt_to_uint(I32, val)); + } + Operator::F32ReinterpretI32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().bitcast(F32, val)); + } + Operator::F64ReinterpretI64 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().bitcast(F64, val)); + } + Operator::I32ReinterpretF32 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().bitcast(I32, val)); + } + Operator::I64ReinterpretF64 => { + let val = stack.pop().unwrap(); + stack.push(builder.ins().bitcast(I64, val)); + } + /****************************** Binary Operators ************************************/ + Operator::I32Add | Operator::I64Add => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().iadd(arg1, arg2)); + } + Operator::I32And | Operator::I64And => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().band(arg1, arg2)); + } + Operator::I32Or | Operator::I64Or => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().bor(arg1, arg2)); + } + Operator::I32Xor | Operator::I64Xor => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().bxor(arg1, arg2)); + } + Operator::I32Shl | Operator::I64Shl => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().ishl(arg1, arg2)); + } + Operator::I32ShrS | + Operator::I64ShrS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().sshr(arg1, arg2)); + } + Operator::I32ShrU | + Operator::I64ShrU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().ushr(arg1, arg2)); + } + Operator::I32Rotl | + Operator::I64Rotl => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().rotl(arg1, arg2)); + } + Operator::I32Rotr | + Operator::I64Rotr => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().rotr(arg1, arg2)); + } + Operator::F32Add | Operator::F64Add => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fadd(arg1, arg2)); + } + Operator::I32Sub | Operator::I64Sub => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().isub(arg1, arg2)); + } + Operator::F32Sub | Operator::F64Sub => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fsub(arg1, arg2)); + } + Operator::I32Mul | Operator::I64Mul => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().imul(arg1, arg2)); + } + Operator::F32Mul | Operator::F64Mul => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fmul(arg1, arg2)); + } + Operator::F32Div | Operator::F64Div => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fdiv(arg1, arg2)); + } + Operator::I32DivS | + Operator::I64DivS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().sdiv(arg1, arg2)); + } + Operator::I32DivU | + Operator::I64DivU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().udiv(arg1, arg2)); + } + Operator::I32RemS | + Operator::I64RemS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().srem(arg1, arg2)); + } + Operator::I32RemU | + Operator::I64RemU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().urem(arg1, arg2)); + } + Operator::F32Min | Operator::F64Min => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fmin(arg1, arg2)); + } + Operator::F32Max | Operator::F64Max => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fmax(arg1, arg2)); + } + Operator::F32Copysign | + Operator::F64Copysign => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + stack.push(builder.ins().fcopysign(arg1, arg2)); + } + /**************************** Comparison Operators **********************************/ + Operator::I32LtS | Operator::I64LtS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::SignedLessThan, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32LtU | Operator::I64LtU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::UnsignedLessThan, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32LeS | Operator::I64LeS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::SignedLessThanOrEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32LeU | Operator::I64LeU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder + .ins() + .icmp(IntCC::UnsignedLessThanOrEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32GtS | Operator::I64GtS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::SignedGreaterThan, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32GtU | Operator::I64GtU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::UnsignedGreaterThan, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32GeS | Operator::I64GeS => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder + .ins() + .icmp(IntCC::SignedGreaterThanOrEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32GeU | Operator::I64GeU => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder + .ins() + .icmp(IntCC::UnsignedGreaterThanOrEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32Eqz | Operator::I64Eqz => { + let arg = stack.pop().unwrap(); + let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32Eq | Operator::I64Eq => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::Equal, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::F32Eq | Operator::F64Eq => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().fcmp(FloatCC::Equal, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::I32Ne | Operator::I64Ne => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().icmp(IntCC::NotEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::F32Ne | Operator::F64Ne => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().fcmp(FloatCC::NotEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::F32Gt | Operator::F64Gt => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().fcmp(FloatCC::GreaterThan, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::F32Ge | Operator::F64Ge => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().fcmp(FloatCC::GreaterThanOrEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::F32Lt | Operator::F64Lt => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().fcmp(FloatCC::LessThan, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + Operator::F32Le | Operator::F64Le => { + let arg2 = stack.pop().unwrap(); + let arg1 = stack.pop().unwrap(); + let val = builder.ins().fcmp(FloatCC::LessThanOrEqual, arg1, arg2); + stack.push(builder.ins().bint(I32, val)); + } + } +} + +/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them +/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable +/// portion so the translation state muts be updated accordingly. +fn translate_unreachable_operator(op: &Operator, + builder: &mut FunctionBuilder, + stack: &mut Vec, + control_stack: &mut Vec, + state: &mut TranslationState) { + // We don't translate because the code is unreachable + // Nevertheless we have to record a phantom stack for this code + // to know when the unreachable code ends + match *op { + Operator::If { ty: _ } | + Operator::Loop { ty: _ } | + Operator::Block { ty: _ } => { + state.phantom_unreachable_stack_depth += 1; + } + Operator::End => { + if state.phantom_unreachable_stack_depth > 0 { + state.phantom_unreachable_stack_depth -= 1; + } else { + // This End corresponds to a real control stack frame + // We switch to the destination block but we don't insert + // a jump instruction since the code is still unreachable + let frame = control_stack.pop().unwrap(); + + builder.switch_to_block(frame.following_code(), &[]); + builder.seal_block(frame.following_code()); + match frame { + // If it is a loop we also have to seal the body loop block + ControlStackFrame::Loop { header, .. } => builder.seal_block(header), + // If it is a if then the code after is reachable again + ControlStackFrame::If { .. } => { + state.real_unreachable_stack_depth = 1; + } + _ => {} + } + if frame.is_reachable() { + state.real_unreachable_stack_depth = 1; + } + // Now we have to split off the stack the values not used + // by unreachable code that hasn't been translated + stack.truncate(frame.original_stack_size()); + // And add the return values of the block but only if the next block is reachble + // (which corresponds to testing if the stack depth is 1) + if state.real_unreachable_stack_depth == 1 { + stack.extend_from_slice(builder.ebb_args(frame.following_code())); + } + state.real_unreachable_stack_depth -= 1; + state.last_inst_return = false; + } + } + Operator::Else => { + if state.phantom_unreachable_stack_depth > 0 { + // This is part of a phantom if-then-else, we do nothing + } else { + // Encountering an real else means that the code in the else + // clause is reachable again + let (branch_inst, original_stack_size) = match &control_stack[control_stack.len() - + 1] { + &ControlStackFrame::If { + branch_inst, + original_stack_size, + .. + } => (branch_inst, original_stack_size), + _ => panic!("should not happen"), + }; + // We change the target of the branch instruction + let else_ebb = builder.create_ebb(); + builder.change_jump_destination(branch_inst, else_ebb); + builder.seal_block(else_ebb); + builder.switch_to_block(else_ebb, &[]); + // Now we have to split off the stack the values not used + // by unreachable code that hasn't been translated + stack.truncate(original_stack_size); + state.real_unreachable_stack_depth = 0; + state.last_inst_return = false; + } + } + _ => { + // We don't translate because this is unreachable code + } + } +} + +fn args_count(index: FunctionIndex, + functions: &Vec, + signatures: &Vec) + -> usize { + signatures[functions[index] as usize].argument_types.len() +} + +// Given a index in the function index space, search for it in the function imports and if it is +// not there add it to the function imports. +fn find_function_import(index: FunctionIndex, + builder: &mut FunctionBuilder, + func_imports: &mut FunctionImports, + functions: &Vec, + exports: &Option>, + signatures: &Vec) + -> FuncRef { + match func_imports.functions.get(&index) { + Some(local_index) => return *local_index, + None => {} + } + // We have to import the function + let sig_index = functions[index]; + match func_imports.signatures.get(&(sig_index as usize)) { + Some(local_sig_index) => { + let local_func_index = + builder.import_function(ExtFuncData { + name: match exports { + &None => FunctionName::new(""), + &Some(ref exports) => { + match exports.get(&index) { + None => FunctionName::new(""), + Some(name) => { + FunctionName::new(name.clone()) + } + } + } + }, + signature: *local_sig_index, + }); + func_imports.functions.insert(index, local_func_index); + return local_func_index; + } + None => {} + }; + // We have to import the signature + let sig_local_index = builder.import_signature(signatures[sig_index as usize].clone()); + func_imports + .signatures + .insert(sig_index as usize, sig_local_index); + let local_func_index = + builder.import_function(ExtFuncData { + name: match exports { + &None => FunctionName::new(""), + &Some(ref exports) => { + match exports.get(&index) { + None => FunctionName::new(""), + Some(name) => FunctionName::new(name.clone()), + } + } + }, + signature: sig_local_index, + }); + func_imports.functions.insert(index, local_func_index); + local_func_index +} + +fn find_signature_import(sig_index: SignatureIndex, + builder: &mut FunctionBuilder, + func_imports: &mut FunctionImports, + signatures: &Vec) + -> SigRef { + match func_imports.signatures.get(&(sig_index as usize)) { + Some(local_sig_index) => return *local_sig_index, + None => {} + } + let sig_local_index = builder.import_signature(signatures[sig_index as usize].clone()); + func_imports + .signatures + .insert(sig_index as usize, sig_local_index); + sig_local_index +} diff --git a/lib/wasm2cretonne/src/lib.rs b/lib/wasm2cretonne/src/lib.rs new file mode 100644 index 0000000000..5006e5c9aa --- /dev/null +++ b/lib/wasm2cretonne/src/lib.rs @@ -0,0 +1,27 @@ +//! Performs the translation from a wasm module in binary format to the in-memory representation +//! of the Cretonne IL. More particularly, it translates the code of all the functions bodies and +//! interacts with a runtime implementing the [`WasmRuntime`](trait.WasmRuntime.html) trait to +//! deal with tables, globals and linear memory. +//! +//! The crate provides a `DummyRuntime` trait that will allow to translate the code of the +//! functions but will fail at execution. You should use +//! [`wasmstandalone::StandaloneRuntime`](../wasmstandalone/struct.StandaloneRuntime.html) to be +//! able to execute the translated code. +//! +//! The main function of this module is [`translate_module`](fn.translate_module.html). + +extern crate wasmparser; +extern crate cton_frontend; +extern crate cretonne; + +mod module_translator; +mod translation_utils; +mod code_translator; +mod runtime; +mod sections_translator; + +pub use module_translator::{translate_module, TranslationResult, FunctionTranslation, + ImportMappings}; +pub use runtime::{WasmRuntime, DummyRuntime}; +pub use translation_utils::{Local, FunctionIndex, GlobalIndex, TableIndex, MemoryIndex, RawByte, + MemoryAddress, SignatureIndex, Global, GlobalInit, Table, Memory}; diff --git a/lib/wasm2cretonne/src/module_translator.rs b/lib/wasm2cretonne/src/module_translator.rs new file mode 100644 index 0000000000..1c22655300 --- /dev/null +++ b/lib/wasm2cretonne/src/module_translator.rs @@ -0,0 +1,288 @@ +//! Translation skeletton that traverses the whole WebAssembly module and call helper functions +//! to deal with each part of it. +use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder}; +use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section, + parse_function_section, parse_export_section, parse_memory_section, + parse_global_section, parse_table_section, parse_elements_section, + parse_data_section}; +use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex, invert_hashmaps}; +use cretonne::ir::{Function, Type, FuncRef, SigRef}; +use code_translator::translate_function_body; +use cton_frontend::ILBuilder; +use std::collections::HashMap; +use runtime::WasmRuntime; + +/// Output of the [`translate_module`](fn.translate_module.html) function. Contains the translated +/// functions and when present the index of the function defined as `start` of the module. +pub struct TranslationResult { + pub functions: Vec, + pub start_index: Option, +} + +/// A function in a WebAssembly module can be either imported, or defined inside it. If it is +/// defined inside it, then the translation in Cretonne IL is available as well as the mappings +/// between Cretonne imports and indexes in the function index space. +#[derive(Clone)] +pub enum FunctionTranslation { + Code { + il: Function, + imports: ImportMappings, + }, + Import(), +} + +#[derive(Clone,Debug)] +/// Mappings describing the relations between imports of the Cretonne IL functions and the +/// functions in the WebAssembly module. +pub struct ImportMappings { + /// Find the index of a function in the WebAssembly module thanks to a `FuncRef`. + pub functions: HashMap, + /// Find the index of a signature in the WebAssembly module thanks to a `SigRef`. + pub signatures: HashMap, +} + +impl ImportMappings { + pub fn new() -> ImportMappings { + ImportMappings { + functions: HashMap::new(), + signatures: HashMap::new(), + } + } +} + +/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL +/// [`Function`](../cretonne/ir/function/struct.Function.html). +/// Returns the functions and also the mappings for imported functions and signature between the +/// indexes in the wasm module and the indexes inside each functions. +pub fn translate_module(data: &Vec, + runtime: &mut WasmRuntime) + -> Result { + let mut parser = Parser::new(data.as_slice()); + match *parser.read() { + ParserState::BeginWasm { .. } => {} + ref s @ _ => panic!("modules should begin properly: {:?}", s), + } + let mut signatures = None; + let mut functions: Option> = None; + let mut globals = Vec::new(); + let mut exports: Option> = None; + let mut next_input = ParserInput::Default; + let mut function_index: FunctionIndex = 0; + let mut function_imports_count = 0; + let mut start_index: Option = None; + loop { + match *parser.read_with_input(next_input) { + ParserState::BeginSection { code: SectionCode::Type, .. } => { + match parse_function_signatures(&mut parser) { + Ok(sigs) => signatures = Some(sigs), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the type section: {}", s)) + } + }; + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Import, .. } => { + match parse_import_section(&mut parser) { + Ok(imps) => { + for import in imps { + match import { + Import::Function { sig_index } => { + functions = match functions { + None => Some(vec![sig_index as SignatureIndex]), + Some(mut funcs) => { + funcs.push(sig_index as SignatureIndex); + Some(funcs) + } + }; + function_index += 1; + } + Import::Memory(mem) => { + runtime.declare_memory(mem); + } + Import::Global(glob) => { + runtime.declare_global(glob.clone()); + globals.push(glob); + } + Import::Table(tab) => { + runtime.declare_table(tab); + } + } + } + } + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the import section: {}", s)) + } + } + function_imports_count = function_index; + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Function, .. } => { + match parse_function_section(&mut parser) { + Ok(funcs) => { + match functions { + None => functions = Some(funcs), + Some(ref mut imps) => imps.extend(funcs), + } + } + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the function section: {}", s)) + } + } + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Table, .. } => { + match parse_table_section(&mut parser, runtime) { + Ok(()) => (), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the table section: {}", s)) + } + } + } + ParserState::BeginSection { code: SectionCode::Memory, .. } => { + match parse_memory_section(&mut parser) { + Ok(mems) => { + for mem in mems { + runtime.declare_memory(mem); + } + } + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the memory section: {}", s)) + } + } + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Global, .. } => { + match parse_global_section(&mut parser, runtime) { + Ok(mut globs) => globals.append(&mut globs), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the global section: {}", s)) + } + } + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Export, .. } => { + match parse_export_section(&mut parser) { + Ok(exps) => exports = Some(exps), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the export section: {}", s)) + } + } + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Start, .. } => { + match *parser.read() { + ParserState::StartSectionEntry(index) => { + start_index = Some(index as FunctionIndex) + } + _ => return Err(String::from("wrong content in the start section")), + } + match *parser.read() { + ParserState::EndSection => {} + _ => return Err(String::from("wrong content in the start section")), + } + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Element, .. } => { + match parse_elements_section(&mut parser, runtime, &globals) { + Ok(()) => (), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the element section: {}", s)) + } + } + next_input = ParserInput::Default; + } + ParserState::BeginSection { code: SectionCode::Code, .. } => { + // The code section begins + break; + } + ParserState::EndSection => { + next_input = ParserInput::Default; + } + ParserState::EndWasm => { + return Ok(TranslationResult { + functions: Vec::new(), + start_index: None, + }) + } + ParserState::BeginSection { code: SectionCode::Data, .. } => { + match parse_data_section(&mut parser, runtime, &globals) { + Ok(()) => (), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the data section: {}", s)) + } + } + } + _ => return Err(String::from("wrong content in the preamble")), + }; + } + // At this point we've entered the code section + // First we check that we have all that is necessary to translate a function. + let signatures = match signatures { + None => Vec::new(), + Some(sigs) => sigs, + }; + let functions = match functions { + None => return Err(String::from("missing a function section")), + Some(functions) => functions, + }; + let mut il_functions: Vec = Vec::new(); + il_functions.resize(function_imports_count, FunctionTranslation::Import()); + let mut il_builder = ILBuilder::new(); + runtime.begin_translation(); + loop { + let locals: Vec<(usize, Type)> = match *parser.read() { + ParserState::BeginFunctionBody { ref locals, .. } => { + locals + .iter() + .map(|&(index, ref ty)| { + (index as usize, + match type_to_type(ty) { + Ok(ty) => ty, + Err(()) => panic!("unsupported type for local variable"), + }) + }) + .collect() + } + ParserState::EndSection => break, + _ => return Err(String::from(format!("wrong content in code section"))), + }; + let signature = signatures[functions[function_index as usize] as usize].clone(); + match translate_function_body(&mut parser, + function_index, + signature, + &locals, + &exports, + &signatures, + &functions, + &mut il_builder, + runtime) { + Ok((il_func, imports)) => { + il_functions.push(FunctionTranslation::Code { + il: il_func, + imports: invert_hashmaps(imports), + }) + } + Err(s) => return Err(s), + } + function_index += 1; + } + loop { + match *parser.read() { + ParserState::BeginSection { code: SectionCode::Data, .. } => { + match parse_data_section(&mut parser, runtime, &globals) { + Ok(()) => (), + Err(SectionParsingError::WrongSectionContent(s)) => { + return Err(format!("wrong content in the data section: {}", s)) + } + } + } + ParserState::EndWasm => { + return Ok(TranslationResult { + functions: il_functions, + start_index, + }) + } + _ => (), + } + } +} diff --git a/lib/wasm2cretonne/src/runtime/dummy.rs b/lib/wasm2cretonne/src/runtime/dummy.rs new file mode 100644 index 0000000000..5969c0ee8f --- /dev/null +++ b/lib/wasm2cretonne/src/runtime/dummy.rs @@ -0,0 +1,93 @@ +use runtime::WasmRuntime; +use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, FunctionIndex, + MemoryIndex}; +use cton_frontend::FunctionBuilder; +use cretonne::ir::{Value, InstBuilder, SigRef}; +use cretonne::ir::immediates::{Ieee32, Ieee64}; +use cretonne::ir::types::*; + +/// This runtime implementation is a "naïve" one, doing essentially nothing and emitting +/// placeholders when forced to. Don't try to execute code translated with this runtime, it is +/// essentially here for translation debug purposes. +pub struct DummyRuntime { + globals: Vec, +} + +impl DummyRuntime { + /// Allocates the runtime data structures. + pub fn new() -> DummyRuntime { + DummyRuntime { globals: Vec::new() } + } +} + +impl WasmRuntime for DummyRuntime { + fn translate_get_global(&self, + builder: &mut FunctionBuilder, + global_index: GlobalIndex) + -> Value { + let ref glob = self.globals.get(global_index as usize).unwrap(); + match glob.ty { + I32 => builder.ins().iconst(glob.ty, -1), + I64 => builder.ins().iconst(glob.ty, -1), + F32 => builder.ins().f32const(Ieee32::with_bits(0xbf800000)), // -1.0 + F64 => { + builder + .ins() + .f64const(Ieee64::with_bits(0xbff0000000000000)) + } // -1.0 + _ => panic!("should not happen"), + } + } + + fn translate_set_global(&self, _: &mut FunctionBuilder, _: GlobalIndex, _: Value) { + // We do nothing + } + fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder, _: Value) -> Value { + builder.ins().iconst(I32, -1) + } + fn translate_current_memory(&mut self, builder: &mut FunctionBuilder) -> Value { + builder.ins().iconst(I32, -1) + } + fn translate_call_indirect<'a>(&self, + builder: &'a mut FunctionBuilder, + sig_ref: SigRef, + index_val: Value, + call_args: &[Value]) + -> &'a [Value] { + let call_inst = builder.ins().call_indirect(sig_ref, index_val, call_args); + builder.inst_results(call_inst) + } + fn translate_memory_base_address(&self, + builder: &mut FunctionBuilder, + _: MemoryIndex) + -> Value { + builder.ins().iconst(I64, 0) + } + fn declare_global(&mut self, global: Global) { + self.globals.push(global); + } + fn declare_table(&mut self, _: Table) { + //We do nothing + } + fn declare_table_elements(&mut self, _: TableIndex, _: usize, _: &[FunctionIndex]) { + //We do nothing + } + fn declare_memory(&mut self, _: Memory) { + //We do nothing + } + fn declare_data_initialization(&mut self, + _: MemoryIndex, + _: usize, + _: &[u8]) + -> Result<(), String> { + // We do nothing + Ok(()) + } + + fn begin_translation(&mut self) { + // We do nothing + } + fn next_function(&mut self) { + // We do nothing + } +} diff --git a/lib/wasm2cretonne/src/runtime/mod.rs b/lib/wasm2cretonne/src/runtime/mod.rs new file mode 100644 index 0000000000..9f2a41d4f9 --- /dev/null +++ b/lib/wasm2cretonne/src/runtime/mod.rs @@ -0,0 +1,5 @@ +mod spec; +mod dummy; + +pub use runtime::spec::WasmRuntime; +pub use runtime::dummy::DummyRuntime; diff --git a/lib/wasm2cretonne/src/runtime/spec.rs b/lib/wasm2cretonne/src/runtime/spec.rs new file mode 100644 index 0000000000..24e98b0d58 --- /dev/null +++ b/lib/wasm2cretonne/src/runtime/spec.rs @@ -0,0 +1,61 @@ +//! All the runtime support necessary for the wasm to cretonne translation is formalized by the +//! trait `WasmRuntime`. +use cton_frontend::FunctionBuilder; +use cretonne::ir::{Value, SigRef}; +use translation_utils::{Local, FunctionIndex, TableIndex, GlobalIndex, MemoryIndex, Global, Table, + Memory}; + +/// An object satisfyng the `WasmRuntime` trait can be passed as argument to the +/// [`translate_module`](fn.translate_module.html) function. These methods should not be called +/// by the user, they are only for the `wasm2cretonne` internal use. +pub trait WasmRuntime { + /// Declares a global to the runtime. + fn declare_global(&mut self, global: Global); + /// Declares a table to the runtime. + fn declare_table(&mut self, table: Table); + /// Fills a declared table with references to functions in the module. + fn declare_table_elements(&mut self, + table_index: TableIndex, + offset: usize, + elements: &[FunctionIndex]); + /// Declares a memory to the runtime + fn declare_memory(&mut self, memory: Memory); + /// Fills a declared memory with bytes at module instantiation. + fn declare_data_initialization(&mut self, + memory_index: MemoryIndex, + offset: usize, + data: &[u8]) + -> Result<(), String>; + /// Call this function after having declared all the runtime elements but prior to the + /// function body translation. + fn begin_translation(&mut self); + /// Call this function between each function body translation. + fn next_function(&mut self); + /// Translates a `get_global` wasm instruction. + fn translate_get_global(&self, + builder: &mut FunctionBuilder, + global_index: GlobalIndex) + -> Value; + /// Translates a `set_global` wasm instruction. + fn translate_set_global(&self, + builder: &mut FunctionBuilder, + global_index: GlobalIndex, + val: Value); + /// Translates a `grow_memory` wasm instruction. Returns the old size (in pages) of the memory. + fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder, val: Value) -> Value; + /// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory. + fn translate_current_memory(&mut self, builder: &mut FunctionBuilder) -> Value; + /// Returns the base address of a wasm memory as a Cretonne `Value`. + fn translate_memory_base_address(&self, + builder: &mut FunctionBuilder, + index: MemoryIndex) + -> Value; + /// Translates a `call_indirect` wasm instruction. It involves looking up the value contained + /// it the table at location `index_val` and calling the corresponding function. + fn translate_call_indirect<'a>(&self, + builder: &'a mut FunctionBuilder, + sig_ref: SigRef, + index_val: Value, + call_args: &[Value]) + -> &'a [Value]; +} diff --git a/lib/wasm2cretonne/src/sections_translator.rs b/lib/wasm2cretonne/src/sections_translator.rs new file mode 100644 index 0000000000..11edd08f30 --- /dev/null +++ b/lib/wasm2cretonne/src/sections_translator.rs @@ -0,0 +1,367 @@ +//! Helper functions to gather information for each of the non-function sections of a +//! WebAssembly module. +//! +//! The code of theses helper function is straightforward since it is only about reading metadata +//! about linear memories, tables, globals, etc. and storing them for later use. +//! +//! The special case of the initialize expressions for table elements offsets or global variables +//! is handled, according to the semantics of WebAssembly, to only specific expressions that are +//! interpreted on the fly. +use translation_utils::{type_to_type, Import, TableIndex, FunctionIndex, GlobalIndex, + SignatureIndex, MemoryIndex, Global, GlobalInit, Table, TableElementType, + Memory}; +use cretonne::ir::{Signature, ArgumentType, CallConv}; +use cretonne; +use wasmparser::{Parser, ParserState, FuncType, ImportSectionEntryType, ExternalKind, WasmDecoder, + MemoryType, Operator}; +use wasmparser; +use std::collections::HashMap; +use std::str::from_utf8; +use runtime::WasmRuntime; + +pub enum SectionParsingError { + WrongSectionContent(String), +} + +/// Reads the Type Section of the wasm module and returns the corresponding function signatures. +pub fn parse_function_signatures(parser: &mut Parser) + -> Result, SectionParsingError> { + let mut signatures: Vec = Vec::new(); + loop { + match *parser.read() { + ParserState::EndSection => break, + ParserState::TypeSectionEntry(FuncType { + form: wasmparser::Type::Func, + ref params, + ref returns, + }) => { + let mut sig = Signature::new(CallConv::Native); + sig.argument_types + .extend(params + .iter() + .map(|ty| { + let cret_arg: cretonne::ir::Type = match type_to_type(ty) { + Ok(ty) => ty, + Err(()) => panic!("only numeric types are supported in\ + function signatures"), + }; + ArgumentType::new(cret_arg) + })); + sig.return_types + .extend(returns + .iter() + .map(|ty| { + let cret_arg: cretonne::ir::Type = match type_to_type(ty) { + Ok(ty) => ty, + Err(()) => panic!("only numeric types are supported in\ + function signatures"), + }; + ArgumentType::new(cret_arg) + })); + signatures.push(sig); + } + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + } + } + Ok(signatures) +} + +/// Retrieves the imports from the imports section of the binary. +pub fn parse_import_section(parser: &mut Parser) -> Result, SectionParsingError> { + let mut imports = Vec::new(); + loop { + match *parser.read() { + ParserState::ImportSectionEntry { + ty: ImportSectionEntryType::Function(sig), .. + } => imports.push(Import::Function { sig_index: sig }), + ParserState::ImportSectionEntry { + ty: ImportSectionEntryType::Memory(MemoryType { limits: ref memlimits }), .. + } => { + imports.push(Import::Memory(Memory { + pages_count: memlimits.initial as usize, + maximum: memlimits.maximum.map(|x| x as usize), + })) + } + ParserState::ImportSectionEntry { + ty: ImportSectionEntryType::Global(ref ty), .. + } => { + imports.push(Import::Global(Global { + ty: type_to_type(&ty.content_type).unwrap(), + mutability: ty.mutability != 0, + initializer: GlobalInit::Import(), + })); + } + ParserState::ImportSectionEntry { + ty: ImportSectionEntryType::Table(ref tab), .. + } => { + imports.push(Import::Table(Table { + ty: match type_to_type(&tab.element_type) { + Ok(t) => TableElementType::Val(t), + Err(()) => TableElementType::Func(), + }, + size: tab.limits.initial as usize, + maximum: tab.limits.maximum.map(|x| x as usize), + })); + } + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(imports) +} + +/// Retrieves the correspondances between functions and signatures from the function section +pub fn parse_function_section(parser: &mut Parser) + -> Result, SectionParsingError> { + let mut funcs = Vec::new(); + loop { + match *parser.read() { + ParserState::FunctionSectionEntry(sigindex) => funcs.push(sigindex as SignatureIndex), + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(funcs) +} + +/// Retrieves the names of the functions from the export section +pub fn parse_export_section(parser: &mut Parser) + -> Result, SectionParsingError> { + let mut exports: HashMap = HashMap::new(); + loop { + match *parser.read() { + ParserState::ExportSectionEntry { + field, + ref kind, + index, + } => { + match kind { + &ExternalKind::Function => { + exports.insert(index as FunctionIndex, + String::from(from_utf8(field).unwrap())); + () + } + _ => (),//TODO: deal with other kind of exports + } + } + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(exports) +} + +/// Retrieves the size and maximum fields of memories from the memory section +pub fn parse_memory_section(parser: &mut Parser) -> Result, SectionParsingError> { + let mut memories: Vec = Vec::new(); + loop { + match *parser.read() { + ParserState::MemorySectionEntry(ref ty) => { + memories.push(Memory { + pages_count: ty.limits.initial as usize, + maximum: ty.limits.maximum.map(|x| x as usize), + }) + } + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(memories) +} + +/// Retrieves the size and maximum fields of memories from the memory section +pub fn parse_global_section(parser: &mut Parser, + runtime: &mut WasmRuntime) + -> Result, SectionParsingError> { + let mut globals = Vec::new(); + loop { + let (content_type, mutability) = match *parser.read() { + ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutability), + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::BeginInitExpressionBody => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + } + let initializer = match *parser.read() { + ParserState::InitExpressionOperator(Operator::I32Const { value }) => { + GlobalInit::I32Const(value) + } + ParserState::InitExpressionOperator(Operator::I64Const { value }) => { + GlobalInit::I64Const(value) + } + ParserState::InitExpressionOperator(Operator::F32Const { value }) => { + GlobalInit::F32Const(value.bits()) + } + ParserState::InitExpressionOperator(Operator::F64Const { value }) => { + GlobalInit::F64Const(value.bits()) + } + ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { + GlobalInit::GlobalRef(global_index as GlobalIndex) + } + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + + }; + match *parser.read() { + ParserState::EndInitExpressionBody => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + } + let global = Global { + ty: type_to_type(&content_type).unwrap(), + mutability: mutability != 0, + initializer: initializer, + }; + runtime.declare_global(global.clone()); + globals.push(global); + match *parser.read() { + ParserState::EndGlobalSectionEntry => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + } + } + Ok(globals) +} + +pub fn parse_data_section(parser: &mut Parser, + runtime: &mut WasmRuntime, + globals: &Vec) + -> Result<(), SectionParsingError> { + loop { + let memory_index = match *parser.read() { + ParserState::BeginDataSectionEntry(memory_index) => memory_index, + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::BeginInitExpressionBody => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + let offset = match *parser.read() { + ParserState::InitExpressionOperator(Operator::I32Const { value }) => { + if value < 0 { + return Err(SectionParsingError::WrongSectionContent(String::from("negative offset value",),),); + } else { + value as usize + } + } + ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { + match globals[global_index as usize].initializer { + GlobalInit::I32Const(value) => { + if value < 0 { + return Err(SectionParsingError::WrongSectionContent(String::from("negative offset value",),),); + } else { + value as usize + } + } + GlobalInit::Import() => { + return Err(SectionParsingError::WrongSectionContent(String::from("imported globals not supported",),),) + } // TODO: add runtime support + _ => panic!("should not happen"), + } + } + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::EndInitExpressionBody => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + { + let data = match *parser.read() { + ParserState::DataSectionEntryBody(data) => data, + ref s @ _ => { + return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))) + } + }; + match runtime.declare_data_initialization(memory_index as MemoryIndex, offset, data) { + Ok(()) => (), + Err(s) => return Err(SectionParsingError::WrongSectionContent(format!("{}", s))), + }; + } + match *parser.read() { + ParserState::EndDataSectionEntry => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(()) +} + +/// Retrieves the tables from the table section +pub fn parse_table_section(parser: &mut Parser, + runtime: &mut WasmRuntime) + -> Result<(), SectionParsingError> { + loop { + match *parser.read() { + ParserState::TableSectionEntry(ref table) => { + runtime.declare_table(Table { + ty: match type_to_type(&table.element_type) { + Ok(t) => TableElementType::Val(t), + Err(()) => TableElementType::Func(), + }, + size: table.limits.initial as usize, + maximum: table.limits.maximum.map(|x| x as usize), + }) + } + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(()) +} + +/// Retrieves the tables from the table section +pub fn parse_elements_section(parser: &mut Parser, + runtime: &mut WasmRuntime, + globals: &Vec) + -> Result<(), SectionParsingError> { + loop { + let table_index = match *parser.read() { + ParserState::BeginElementSectionEntry(ref table_index) => *table_index as TableIndex, + ParserState::EndSection => break, + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::BeginInitExpressionBody => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + let offset = match *parser.read() { + ParserState::InitExpressionOperator(Operator::I32Const { value }) => { + if value < 0 { + return Err(SectionParsingError::WrongSectionContent(String::from("negative offset value",),),); + } else { + value as usize + } + } + ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => { + match globals[global_index as usize].initializer { + GlobalInit::I32Const(value) => { + if value < 0 { + return Err(SectionParsingError::WrongSectionContent(String::from("negative offset value",),),); + } else { + value as usize + } + } + GlobalInit::Import() => 0, // TODO: add runtime support + _ => panic!("should not happen"), + } + } + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::EndInitExpressionBody => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::ElementSectionEntryBody(ref elements) => { + let elems: Vec = + elements.iter().map(|&x| x as FunctionIndex).collect(); + runtime.declare_table_elements(table_index, offset, elems.as_slice()) + } + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + match *parser.read() { + ParserState::EndElementSectionEntry => (), + ref s @ _ => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), + }; + } + Ok(()) +} diff --git a/lib/wasm2cretonne/src/translation_utils.rs b/lib/wasm2cretonne/src/translation_utils.rs new file mode 100644 index 0000000000..840e568787 --- /dev/null +++ b/lib/wasm2cretonne/src/translation_utils.rs @@ -0,0 +1,138 @@ +///! Helper functions and structures for the translation. +use wasmparser; +use cretonne; +use std::u32; +use code_translator; +use module_translator; + +/// Index of a function (imported or defined) inside the WebAssembly module. +pub type FunctionIndex = usize; +/// Index of a table (imported or defined) inside the WebAssembly module. +pub type TableIndex = usize; +/// Index of a global variable (imported or defined) inside the WebAssembly module. +pub type GlobalIndex = usize; +/// Index of a linear memory (imported or defined) inside the WebAssembly module. +pub type MemoryIndex = usize; +/// Index of a signature (imported or defined) inside the WebAssembly module. +pub type SignatureIndex = usize; +/// Raw byte read from memory. +pub type RawByte = u8; +/// Pointer referring to a memory address. +pub type MemoryAddress = usize; + +/// WebAssembly import. +#[derive(Debug,Clone,Copy)] +pub enum Import { + Function { sig_index: u32 }, + Memory(Memory), + Global(Global), + Table(Table), +} + +/// WebAssembly global. +#[derive(Debug,Clone,Copy)] +pub struct Global { + pub ty: cretonne::ir::Type, + pub mutability: bool, + pub initializer: GlobalInit, +} + +/// Globals are initialized via the four `const` operators or by referring to another import. +#[derive(Debug,Clone,Copy)] +pub enum GlobalInit { + I32Const(i32), + I64Const(i64), + F32Const(u32), + F64Const(u64), + Import(), + GlobalRef(GlobalIndex), +} + +/// WebAssembly table. +#[derive(Debug,Clone,Copy)] +pub struct Table { + pub ty: TableElementType, + pub size: usize, + pub maximum: Option, +} + +/// WebAssembly table element. Can be a function or a scalar type. +#[derive(Debug,Clone,Copy)] +pub enum TableElementType { + Val(cretonne::ir::Type), + Func(), +} + +/// WebAssembly linear memory. +#[derive(Debug,Clone,Copy)] +pub struct Memory { + pub pages_count: usize, + pub maximum: Option, +} + +/// Wrapper to a `get_local` and `set_local` index. They are WebAssembly's non-SSA variables. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Local(pub u32); +impl cretonne::entity_ref::EntityRef for Local { + fn new(index: usize) -> Self { + assert!(index < (u32::MAX as usize)); + Local(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } +} +impl Default for Local { + fn default() -> Local { + Local(u32::MAX) + } +} + +/// Helper function translating wasmparser types to Cretonne types when possible. +pub fn type_to_type(ty: &wasmparser::Type) -> Result { + match *ty { + wasmparser::Type::I32 => Ok(cretonne::ir::types::I32), + wasmparser::Type::I64 => Ok(cretonne::ir::types::I64), + wasmparser::Type::F32 => Ok(cretonne::ir::types::F32), + wasmparser::Type::F64 => Ok(cretonne::ir::types::F64), + _ => Err(()), + } +} + +/// Turns a `wasmparser` `f32` into a `Cretonne` one. +pub fn f32_translation(x: wasmparser::Ieee32) -> cretonne::ir::immediates::Ieee32 { + cretonne::ir::immediates::Ieee32::with_bits(x.bits()) +} + +/// Turns a `wasmparser` `f64` into a `Cretonne` one. +pub fn f64_translation(x: wasmparser::Ieee64) -> cretonne::ir::immediates::Ieee64 { + cretonne::ir::immediates::Ieee64::with_bits(x.bits()) +} + +/// Translate a `wasmparser` type into its `Cretonne` equivalent, when possible +pub fn translate_type(ty: wasmparser::Type) -> Result, ()> { + match ty { + wasmparser::Type::EmptyBlockType => Ok(Vec::new()), + wasmparser::Type::I32 => Ok(vec![cretonne::ir::types::I32]), + wasmparser::Type::F32 => Ok(vec![cretonne::ir::types::F32]), + wasmparser::Type::I64 => Ok(vec![cretonne::ir::types::I64]), + wasmparser::Type::F64 => Ok(vec![cretonne::ir::types::F64]), + _ => panic!("unsupported return value type"), + } +} + +/// Inverts the key-value relation in the imports hashmap. Indeed, these hashmaps are built by +/// feeding the function indexes in the module but are used by the runtime with the `FuncRef` as +/// keys. +pub fn invert_hashmaps(imports: code_translator::FunctionImports) + -> module_translator::ImportMappings { + let mut new_imports = module_translator::ImportMappings::new(); + for (func_index, func_ref) in imports.functions.iter() { + new_imports.functions.insert(*func_ref, *func_index); + } + for (sig_index, sig_ref) in imports.signatures.iter() { + new_imports.signatures.insert(*sig_ref, *sig_index); + } + new_imports +} diff --git a/lib/wasmstandalone/.gitignore b/lib/wasmstandalone/.gitignore new file mode 100644 index 0000000000..4308d82204 --- /dev/null +++ b/lib/wasmstandalone/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/lib/wasmstandalone/Cargo.toml b/lib/wasmstandalone/Cargo.toml new file mode 100644 index 0000000000..a1f35f92ff --- /dev/null +++ b/lib/wasmstandalone/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wasmstandalone" +version = "0.0.0" +authors = ["The Cretonne Project Developers"] +publish = false + +[dependencies] +cretonne = { path = "../cretonne" } +cretonne-frontend = { path = "../frontend" } +wasm2cretonne = { path = "../wasm2cretonne" } +region = "0.0.8" diff --git a/lib/wasmstandalone/src/execution.rs b/lib/wasmstandalone/src/execution.rs new file mode 100644 index 0000000000..9d8a3f2839 --- /dev/null +++ b/lib/wasmstandalone/src/execution.rs @@ -0,0 +1,256 @@ +use cretonne::Context; +use cretonne::settings; +use cretonne::isa::{self, TargetIsa}; +use cretonne::verify_function; +use cretonne::verifier; +use cretonne::settings::Configurable; +use cretonne::result::CtonError; +use cretonne::ir::entities::AnyEntity; +use cretonne::ir::{self, Ebb, FuncRef, JumpTable, Function}; +use cretonne::binemit::{RelocSink, Reloc, CodeOffset}; +use wasm2cretonne::{TranslationResult, FunctionTranslation, ImportMappings, FunctionIndex}; +use std::mem::transmute; +use region::Protection; +use region::protect; +use std::collections::HashMap; +use std::ptr::write_unaligned; +use std::fmt::Write; + +type RelocRef = u16; + +// Implementation of a relocation sink that just saves all the information for later +struct StandaloneRelocSink { + ebbs: HashMap, + funcs: HashMap, + jts: HashMap, +} + +// Contains all the metadata necessary to perform relocations +enum FunctionMetaData { + Import(), + Local { + relocs: StandaloneRelocSink, + imports: ImportMappings, + il_func: Function, + }, +} + +impl RelocSink for StandaloneRelocSink { + fn reloc_ebb(&mut self, offset: CodeOffset, reloc: Reloc, ebb: Ebb) { + self.ebbs.insert(reloc.0, (ebb, offset)); + } + fn reloc_func(&mut self, offset: CodeOffset, reloc: Reloc, func: FuncRef) { + self.funcs.insert(reloc.0, (func, offset)); + } + fn reloc_jt(&mut self, offset: CodeOffset, reloc: Reloc, jt: JumpTable) { + self.jts.insert(reloc.0, (jt, offset)); + } +} + +impl StandaloneRelocSink { + fn new() -> StandaloneRelocSink { + StandaloneRelocSink { + ebbs: HashMap::new(), + funcs: HashMap::new(), + jts: HashMap::new(), + } + } +} + +/// Structure containing the compiled code of the functions, ready to be executed. +pub struct ExecutableCode { + functions_code: Vec>, + start_index: FunctionIndex, +} + +/// Executes a module that has been translated with the `StandaloneRuntime` runtime implementation. +pub fn compile_module(trans_result: &TranslationResult) -> Result { + let mut shared_builder = settings::builder(); + shared_builder + .enable("enable_verifier") + .expect("Missing enable_verifier setting"); + shared_builder + .set("is_64bit", "1") + .expect("Missing 64bits setting"); + let isa = match isa::lookup("intel") { + Err(_) => { + panic!() // The target ISA is not available. + } + Ok(mut isa_builder) => { + isa_builder + .enable("haswell") + .expect("Missing haswell setting"); + isa_builder.finish(settings::Flags::new(&shared_builder)) + } + }; + let mut functions_metatada = Vec::new(); + let mut functions_code = Vec::new(); + for (function_index, function) in trans_result.functions.iter().enumerate() { + let mut context = Context::new(); + let (il, imports) = match function { + &FunctionTranslation::Import() => { + if trans_result.start_index.is_some() && + trans_result.start_index.unwrap() == function_index { + return Err(String::from("start function should not be an import")); + } else { + functions_code.push(Vec::new()); + functions_metatada.push(FunctionMetaData::Import()); + continue; + } + } + &FunctionTranslation::Code { + ref il, + ref imports, + .. + } => (il.clone(), imports.clone()), + }; + verify_function(&il, None).unwrap(); + context.func = il; + let code_size = context + .compile(&*isa) + .map_err(|e| pretty_error(&context.func, Some(&*isa), e))? as + usize; + if code_size == 0 { + return Err(String::from("no code generated by Cretonne")); + } + let mut code_buf: Vec = Vec::with_capacity(code_size); + code_buf.resize(code_size, 0); + let mut relocsink = StandaloneRelocSink::new(); + context.emit_to_memory(code_buf.as_mut_ptr(), &mut relocsink, &*isa); + functions_metatada.push(FunctionMetaData::Local { + relocs: relocsink, + imports: imports, + il_func: context.func, + }); + functions_code.push(code_buf); + } + relocate(&functions_metatada, &mut functions_code); + // After having emmitted the code to memory, we deal with relocations + match trans_result.start_index { + None => Err(String::from("No start function defined, aborting execution")), + Some(index) => { + Ok(ExecutableCode { + functions_code, + start_index: index, + }) + } + } +} + +// Jumps to the code region of memory and execute the start function of the module. +pub fn execute(exec: ExecutableCode) -> Result<(), String> { + let code_buf = &exec.functions_code[exec.start_index]; + unsafe { + match protect(code_buf.as_ptr(), + code_buf.len(), + Protection::ReadWriteExecute) { + Ok(()) => (), + Err(err) => { + return Err(format!("failed to give executable permission to code: {}", + err.description())) + } + }; + // Rather than writing inline assembly to jump to the code region, we use the fact that + // the Rust ABI for calling a function with no arguments and no return matches the one of + // the generated code.Thanks to this, we can transmute the code region into a first-class + // Rust function and call it. + // TODO: the Rust callee-saved registers will be overwritten by the executed code, inline + // assembly spilling these registers to the stack and restoring them after the call is + // needed. + let start_func = transmute::<_, fn()>(code_buf.as_ptr()); + // The code below saves the Intel callee-saved registers. It is not activate because + // inline ASM is not supported in the release version of the Rust compiler. + /*asm!("push rax + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + " :::: "intel", "volatile");*/ + start_func(); + /*asm!("pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rax + " :::: "intel", "volatile");*/ + Ok(()) + } +} + +/// Performs the relocations inside the function bytecode, provided the necessary metadata +fn relocate(functions_metatada: &Vec, functions_code: &mut Vec>) { + // The relocations are relative to the relocation's address plus four bytes + for (func_index, function_in_memory) in functions_metatada.iter().enumerate() { + match function_in_memory { + &FunctionMetaData::Import() => continue, + &FunctionMetaData::Local { + ref relocs, + ref imports, + ref il_func, + } => { + for (_, &(func_ref, offset)) in relocs.funcs.iter() { + let target_func_index = imports.functions[&func_ref]; + let target_func_address: isize = functions_code[target_func_index].as_ptr() as + isize; + unsafe { + let reloc_address: isize = functions_code[func_index] + .as_mut_ptr() + .offset(offset as isize + 4) as + isize; + let reloc_delta_i32: i32 = (target_func_address - reloc_address) as i32; + write_unaligned(reloc_address as *mut i32, reloc_delta_i32); + } + } + for (_, &(ebb, offset)) in relocs.ebbs.iter() { + unsafe { + let reloc_address: isize = functions_code[func_index] + .as_mut_ptr() + .offset(offset as isize + 4) as + isize; + let target_ebb_address: isize = + functions_code[func_index] + .as_ptr() + .offset(il_func.offsets[ebb] as isize) as + isize; + let reloc_delta_i32: i32 = (target_ebb_address - reloc_address) as i32; + write_unaligned(reloc_address as *mut i32, reloc_delta_i32); + } + } + // TODO: deal with jumptable relocations + } + } + } +} + +/// Pretty-print a verifier error. +pub fn pretty_verifier_error(func: &Function, + isa: Option<&TargetIsa>, + err: verifier::Error) + -> String { + let mut msg = err.to_string(); + match err.location { + AnyEntity::Inst(inst) => { + write!(msg, "\n{}: {}\n\n", inst, func.dfg.display_inst(inst, isa)).unwrap() + } + _ => msg.push('\n'), + } + write!(msg, "{}", func.display(isa)).unwrap(); + msg +} + +/// Pretty-print a Cretonne error. +pub fn pretty_error(func: &ir::Function, isa: Option<&TargetIsa>, err: CtonError) -> String { + if let CtonError::Verifier(e) = err { + pretty_verifier_error(func, isa, e) + } else { + err.to_string() + } +} diff --git a/lib/wasmstandalone/src/lib.rs b/lib/wasmstandalone/src/lib.rs new file mode 100644 index 0000000000..75479a33a5 --- /dev/null +++ b/lib/wasmstandalone/src/lib.rs @@ -0,0 +1,15 @@ +//! Standalone JIT-style runtime for WebAssembly using Cretonne. Provides functions to translate +//! `get_global`, `set_global`, `current_memory`, `grow_memory`, `call_indirect` that hardcode in +//! the translation the base addresses of regions of memory that will hold the globals, tables and +//! linear memories. + +extern crate cretonne; +extern crate wasm2cretonne; +extern crate cton_frontend; +extern crate region; + +mod execution; +mod standalone; + +pub use execution::{compile_module, execute, ExecutableCode}; +pub use standalone::StandaloneRuntime; diff --git a/lib/wasmstandalone/src/standalone.rs b/lib/wasmstandalone/src/standalone.rs new file mode 100644 index 0000000000..1c3fd79bf8 --- /dev/null +++ b/lib/wasmstandalone/src/standalone.rs @@ -0,0 +1,332 @@ +use wasm2cretonne::{Local, FunctionIndex, GlobalIndex, TableIndex, MemoryIndex, RawByte, + MemoryAddress, Global, GlobalInit, Table, Memory, WasmRuntime}; +use cton_frontend::FunctionBuilder; +use cretonne::ir::{MemFlags, Value, InstBuilder, SigRef, FuncRef, ExtFuncData, FunctionName, + Signature, ArgumentType, CallConv}; +use cretonne::ir::types::*; +use cretonne::ir::condcodes::IntCC; +use cretonne::ir::immediates::Offset32; +use std::mem::transmute; +use std::ptr::copy_nonoverlapping; +use std::ptr::write; + +#[derive(Clone, Debug)] +enum TableElement { + Trap(), + Function(FunctionIndex), +} + +struct GlobalInfo { + global: Global, + offset: usize, +} + +struct GlobalsData { + data: Vec, + info: Vec, +} + +struct TableData { + data: Vec, + elements: Vec, + info: Table, +} + +struct MemoryData { + data: Vec, + info: Memory, +} + +const PAGE_SIZE: usize = 65536; + +/// Object containing the standalone runtime information. To be passed after creation as argument +/// to [`wasm2cretonne::translatemodule`](../wasm2cretonne/fn.translate_module.html). +pub struct StandaloneRuntime { + globals: GlobalsData, + tables: Vec, + memories: Vec, + instantiated: bool, + has_current_memory: Option, + has_grow_memory: Option, +} + +impl StandaloneRuntime { + /// Allocates the runtime data structures. + pub fn new() -> StandaloneRuntime { + StandaloneRuntime { + globals: GlobalsData { + data: Vec::new(), + info: Vec::new(), + }, + tables: Vec::new(), + memories: Vec::new(), + instantiated: false, + has_current_memory: None, + has_grow_memory: None, + } + } +} + +/// This trait is useful for +/// [`wasm2cretonne::translatemodule`](../wasm2cretonne/fn.translate_module.html) because it +/// tells how to translate runtime-dependent wasm instructions. These functions should not be +/// called by the user. +impl WasmRuntime for StandaloneRuntime { + fn translate_get_global(&self, + builder: &mut FunctionBuilder, + global_index: GlobalIndex) + -> Value { + debug_assert!(self.instantiated); + let ty = self.globals.info[global_index as usize].global.ty; + let offset = self.globals.info[global_index as usize].offset; + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + let addr: i64 = unsafe { transmute(self.globals.data.as_ptr()) }; + let addr_val = builder.ins().iconst(I64, addr); + builder.ins().load(ty, memflags, addr_val, memoffset) + } + fn translate_set_global(&self, + builder: &mut FunctionBuilder, + global_index: GlobalIndex, + val: Value) { + let offset = self.globals.info[global_index as usize].offset; + let memflags = MemFlags::new(); + let memoffset = Offset32::new(offset as i32); + let addr: i64 = unsafe { transmute(self.globals.data.as_ptr()) }; + let addr_val = builder.ins().iconst(I64, addr); + builder.ins().store(memflags, val, addr_val, memoffset); + } + fn translate_memory_base_address(&self, + builder: &mut FunctionBuilder, + memory_index: MemoryIndex) + -> Value { + let addr: i64 = unsafe { transmute(self.memories[memory_index].data.as_ptr()) }; + builder.ins().iconst(I64, addr) + } + fn translate_grow_memory(&mut self, + builder: &mut FunctionBuilder, + pages: Value) + -> Value { + debug_assert!(self.instantiated); + let grow_mem_func = match self.has_grow_memory { + Some(grow_mem_func) => grow_mem_func, + None => { + let sig_ref = + builder.import_signature(Signature { + call_conv: CallConv::Native, + argument_bytes: None, + argument_types: vec![ArgumentType::new(I32)], + return_types: vec![ArgumentType::new(I32)], + }); + builder.import_function(ExtFuncData { + name: FunctionName::new("grow_memory"), + signature: sig_ref, + }) + } + }; + self.has_grow_memory = Some(grow_mem_func); + let call_inst = builder.ins().call(grow_mem_func, &[pages]); + *builder.inst_results(call_inst).first().unwrap() + } + fn translate_current_memory(&mut self, builder: &mut FunctionBuilder) -> Value { + debug_assert!(self.instantiated); + let cur_mem_func = match self.has_current_memory { + Some(cur_mem_func) => cur_mem_func, + None => { + let sig_ref = builder.import_signature(Signature { + call_conv: CallConv::Native, + argument_bytes: None, + argument_types: Vec::new(), + return_types: + vec![ArgumentType::new(I32)], + }); + builder.import_function(ExtFuncData { + name: FunctionName::new("current_memory"), + signature: sig_ref, + }) + } + }; + self.has_current_memory = Some(cur_mem_func); + let call_inst = builder.ins().call(cur_mem_func, &[]); + *builder.inst_results(call_inst).first().unwrap() + } + fn translate_call_indirect<'a>(&self, + builder: &'a mut FunctionBuilder, + sig_ref: SigRef, + index_val: Value, + call_args: &[Value]) + -> &'a [Value] { + let trap_ebb = builder.create_ebb(); + let continue_ebb = builder.create_ebb(); + let size_val = builder.ins().iconst(I32, self.tables[0].info.size as i64); + let zero_val = builder.ins().iconst(I32, 0); + builder + .ins() + .br_icmp(IntCC::UnsignedLessThan, index_val, zero_val, trap_ebb, &[]); + builder + .ins() + .br_icmp(IntCC::UnsignedGreaterThanOrEqual, + index_val, + size_val, + trap_ebb, + &[]); + builder.seal_block(trap_ebb); + let offset_val = builder.ins().imul_imm(index_val, 4); + let base_table_addr: i64 = unsafe { transmute(self.tables[0].data.as_ptr()) }; + let table_addr_val = builder.ins().iconst(I32, base_table_addr); + let table_entry_addr_val = builder.ins().iadd(table_addr_val, offset_val); + let memflags = MemFlags::new(); + let memoffset = Offset32::new(0); + let table_entry_val = builder + .ins() + .load(I32, memflags, table_entry_addr_val, memoffset); + let call_inst = builder + .ins() + .call_indirect(sig_ref, table_entry_val, call_args); + builder.ins().jump(continue_ebb, &[]); + builder.seal_block(continue_ebb); + builder.switch_to_block(trap_ebb, &[]); + builder.ins().trap(); + builder.switch_to_block(continue_ebb, &[]); + builder.inst_results(call_inst) + } + + fn begin_translation(&mut self) { + debug_assert!(!self.instantiated); + self.instantiated = true; + // At instantiation, we allocate memory for the globals, the memories and the tables + // First the globals + let mut globals_data_size = 0; + for globalinfo in self.globals.info.iter_mut() { + globalinfo.offset = globals_data_size; + globals_data_size += globalinfo.global.ty.bytes() as usize; + } + self.globals.data.resize(globals_data_size as usize, 0); + for globalinfo in self.globals.info.iter() { + match globalinfo.global.initializer { + GlobalInit::I32Const(val) => unsafe { + write(self.globals + .data + .as_mut_ptr() + .offset(globalinfo.offset as isize) as + *mut i32, + val) + }, + GlobalInit::I64Const(val) => unsafe { + write(self.globals + .data + .as_mut_ptr() + .offset(globalinfo.offset as isize) as + *mut i64, + val) + }, + GlobalInit::F32Const(val) => unsafe { + write(self.globals + .data + .as_mut_ptr() + .offset(globalinfo.offset as isize) as + *mut f32, + transmute(val)) + }, + GlobalInit::F64Const(val) => unsafe { + write(self.globals + .data + .as_mut_ptr() + .offset(globalinfo.offset as isize) as + *mut f64, + transmute(val)) + }, + GlobalInit::Import() => { + // We don't initialize, this is inter-module linking + // TODO: support inter-module imports + } + GlobalInit::GlobalRef(index) => { + let ref_offset = self.globals.info[index].offset; + let size = globalinfo.global.ty.bytes(); + unsafe { + let dst = self.globals + .data + .as_mut_ptr() + .offset(globalinfo.offset as isize); + let src = self.globals.data.as_ptr().offset(ref_offset as isize); + copy_nonoverlapping(src, dst, size as usize) + } + } + } + } + } + fn next_function(&mut self) { + self.has_current_memory = None; + self.has_grow_memory = None; + } + fn declare_global(&mut self, global: Global) { + debug_assert!(!self.instantiated); + self.globals + .info + .push(GlobalInfo { + global: global, + offset: 0, + }); + } + fn declare_table(&mut self, table: Table) { + debug_assert!(!self.instantiated); + let mut elements_vec = Vec::with_capacity(table.size as usize); + elements_vec.resize(table.size as usize, TableElement::Trap()); + let mut addresses_vec = Vec::with_capacity(table.size as usize); + addresses_vec.resize(table.size as usize, 0); + self.tables + .push(TableData { + info: table, + data: addresses_vec, + elements: elements_vec, + }); + } + fn declare_table_elements(&mut self, + table_index: TableIndex, + offset: usize, + elements: &[FunctionIndex]) { + debug_assert!(!self.instantiated); + for (i, elt) in elements.iter().enumerate() { + self.tables[table_index].elements[offset as usize + i] = TableElement::Function(*elt); + } + } + fn declare_memory(&mut self, memory: Memory) { + debug_assert!(!self.instantiated); + let mut memory_vec = Vec::with_capacity(memory.pages_count as usize * PAGE_SIZE); + memory_vec.resize(memory.pages_count as usize * PAGE_SIZE, 0); + self.memories + .push(MemoryData { + info: memory, + data: memory_vec, + }); + } + fn declare_data_initialization(&mut self, + memory_index: MemoryIndex, + offset: usize, + data: &[u8]) + -> Result<(), String> { + if offset + data.len() > self.memories[memory_index].info.pages_count * PAGE_SIZE { + return Err(String::from("initialization data out of bounds")); + } + self.memories[memory_index].data[offset..offset + data.len()].copy_from_slice(data); + Ok(()) + } +} + +/// Convenience functions for the user to be called after execution for debug purposes. +impl StandaloneRuntime { + /// Returns a slice of the contents of allocated linear memory. + pub fn inspect_memory(&self, memory_index: usize, address: usize, len: usize) -> &[u8] { + &self.memories + .get(memory_index) + .expect(format!("no memory for index {}", memory_index).as_str()) + .data + [address..address + len] + } + /// Shows the value of a global variable. + pub fn inspect_global(&self, global_index: usize) -> &[u8] { + let (offset, len) = (self.globals.info[global_index].offset, + self.globals.info[global_index].global.ty.bytes() as usize); + &self.globals.data[offset..offset + len] + } +}