Dumped code from the wasm2cretonne repo.
Integrated wasm test suite translation as cretonne test Fixes #146. Fixes #143.
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
e8276ed965
commit
ee9989c4b9
@@ -16,10 +16,14 @@ path = "src/cton-util.rs"
|
|||||||
cretonne = { path = "lib/cretonne" }
|
cretonne = { path = "lib/cretonne" }
|
||||||
cretonne-reader = { path = "lib/reader" }
|
cretonne-reader = { path = "lib/reader" }
|
||||||
cretonne-frontend = { path = "lib/frontend" }
|
cretonne-frontend = { path = "lib/frontend" }
|
||||||
|
cretonne-wasm = { path = "lib/wasm" }
|
||||||
filecheck = { path = "lib/filecheck" }
|
filecheck = { path = "lib/filecheck" }
|
||||||
|
wasmparser = "0.6.1"
|
||||||
docopt = "0.8.0"
|
docopt = "0.8.0"
|
||||||
serde = "1.0.8"
|
serde = "1.0.8"
|
||||||
serde_derive = "1.0.8"
|
serde_derive = "1.0.8"
|
||||||
num_cpus = "1.5.1"
|
num_cpus = "1.5.1"
|
||||||
|
tempdir="0.3.5"
|
||||||
|
term = "0.4.6"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#[macro_use(dbg)]
|
#[macro_use(dbg)]
|
||||||
extern crate cretonne;
|
extern crate cretonne;
|
||||||
extern crate cton_reader;
|
extern crate cton_reader;
|
||||||
|
extern crate cretonne_wasm;
|
||||||
|
extern crate wasmparser;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate filecheck;
|
extern crate filecheck;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
|
extern crate tempdir;
|
||||||
|
extern crate term;
|
||||||
|
|
||||||
use cretonne::VERSION;
|
use cretonne::VERSION;
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
@@ -18,6 +22,7 @@ mod filetest;
|
|||||||
mod cat;
|
mod cat;
|
||||||
mod print_cfg;
|
mod print_cfg;
|
||||||
mod rsfilecheck;
|
mod rsfilecheck;
|
||||||
|
mod wasm;
|
||||||
|
|
||||||
const USAGE: &str = "
|
const USAGE: &str = "
|
||||||
Cretonne code generator utility
|
Cretonne code generator utility
|
||||||
@@ -27,10 +32,13 @@ Usage:
|
|||||||
cton-util cat <file>...
|
cton-util cat <file>...
|
||||||
cton-util filecheck [-v] <file>
|
cton-util filecheck [-v] <file>
|
||||||
cton-util print-cfg <file>...
|
cton-util print-cfg <file>...
|
||||||
|
cton-util wasm [-cvo] <file>...
|
||||||
cton-util --help | --version
|
cton-util --help | --version
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-v, --verbose be more verbose
|
-v, --verbose be more verbose
|
||||||
|
-c, --check checks the correctness of Cretonne IL translated from WebAssembly
|
||||||
|
-o, --optimize runs otpimization passes on translated WebAssembly functions
|
||||||
-h, --help print this help message
|
-h, --help print this help message
|
||||||
--version print the Cretonne version
|
--version print the Cretonne version
|
||||||
|
|
||||||
@@ -42,7 +50,10 @@ struct Args {
|
|||||||
cmd_cat: bool,
|
cmd_cat: bool,
|
||||||
cmd_filecheck: bool,
|
cmd_filecheck: bool,
|
||||||
cmd_print_cfg: bool,
|
cmd_print_cfg: bool,
|
||||||
|
cmd_wasm: bool,
|
||||||
arg_file: Vec<String>,
|
arg_file: Vec<String>,
|
||||||
|
flag_check: bool,
|
||||||
|
flag_optimize: bool,
|
||||||
flag_verbose: bool,
|
flag_verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +80,11 @@ fn cton_util() -> CommandResult {
|
|||||||
rsfilecheck::run(args.arg_file, args.flag_verbose)
|
rsfilecheck::run(args.arg_file, args.flag_verbose)
|
||||||
} else if args.cmd_print_cfg {
|
} else if args.cmd_print_cfg {
|
||||||
print_cfg::run(args.arg_file)
|
print_cfg::run(args.arg_file)
|
||||||
|
} else if args.cmd_wasm {
|
||||||
|
wasm::run(args.arg_file,
|
||||||
|
args.flag_verbose,
|
||||||
|
args.flag_optimize,
|
||||||
|
args.flag_check)
|
||||||
} else {
|
} else {
|
||||||
// Debugging / shouldn't happen with proper command line handling above.
|
// Debugging / shouldn't happen with proper command line handling above.
|
||||||
Err(format!("Unhandled args: {:?}", args))
|
Err(format!("Unhandled args: {:?}", args))
|
||||||
|
|||||||
227
cranelift/src/wasm.rs
Normal file
227
cranelift/src/wasm.rs
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
//! CLI tool to use the functions provided by crates [wasm2cretonne](../wasm2cretonne/index.html)
|
||||||
|
//! and [wasmstandalone](../wasmstandalone/index.html).
|
||||||
|
//!
|
||||||
|
//! Reads Wasm binary files (one Wasm module per file), translates the functions' code to Cretonne
|
||||||
|
//! IL. Can also executes the `start` function of the module by laying out the memories, globals
|
||||||
|
//! and tables, then emitting the translated code with hardcoded addresses to memory.
|
||||||
|
|
||||||
|
use cretonne_wasm::{translate_module, FunctionTranslation, DummyRuntime, WasmRuntime};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use cretonne::loop_analysis::LoopAnalysis;
|
||||||
|
use cretonne::flowgraph::ControlFlowGraph;
|
||||||
|
use cretonne::dominator_tree::DominatorTree;
|
||||||
|
use cretonne::Context;
|
||||||
|
use cretonne::result::CtonError;
|
||||||
|
use cretonne::ir;
|
||||||
|
use cretonne::ir::entities::AnyEntity;
|
||||||
|
use cretonne::isa::TargetIsa;
|
||||||
|
use cretonne::verifier;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use term;
|
||||||
|
|
||||||
|
macro_rules! vprintln {
|
||||||
|
($x: expr, $($tts:tt)*) => {
|
||||||
|
if $x {
|
||||||
|
println!($($tts)*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! vprint {
|
||||||
|
($x: expr, $($tts:tt)*) => {
|
||||||
|
if $x {
|
||||||
|
print!($($tts)*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let mut buf_reader = BufReader::new(file);
|
||||||
|
buf_reader.read_to_end(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn run(files: Vec<String>,
|
||||||
|
flag_verbose: bool,
|
||||||
|
flag_optimize: bool,
|
||||||
|
flag_check: bool)
|
||||||
|
-> Result<(), String> {
|
||||||
|
for filename in files.iter() {
|
||||||
|
let path = Path::new(&filename);
|
||||||
|
let name = String::from(path.as_os_str().to_string_lossy());
|
||||||
|
match handle_module(flag_verbose,
|
||||||
|
flag_optimize,
|
||||||
|
flag_check,
|
||||||
|
path.to_path_buf(),
|
||||||
|
name) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(message) => return Err(message),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_module(flag_verbose: bool,
|
||||||
|
flag_optimize: bool,
|
||||||
|
flag_check: bool,
|
||||||
|
path: PathBuf,
|
||||||
|
name: String)
|
||||||
|
-> Result<(), String> {
|
||||||
|
let mut terminal = term::stdout().unwrap();
|
||||||
|
terminal.fg(term::color::YELLOW).unwrap();
|
||||||
|
vprint!(flag_verbose, "Handling: ");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
vprintln!(flag_verbose, "\"{}\"", name);
|
||||||
|
terminal.fg(term::color::MAGENTA).unwrap();
|
||||||
|
vprint!(flag_verbose, "Translating...");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
let data = match path.extension() {
|
||||||
|
None => {
|
||||||
|
return Err(String::from("the file extension is not wasm or wast"));
|
||||||
|
}
|
||||||
|
Some(ext) => {
|
||||||
|
match ext.to_str() {
|
||||||
|
Some("wasm") => {
|
||||||
|
match read_wasm_file(path.clone()) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(String::from(err.description()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some("wast") => {
|
||||||
|
let tmp_dir = TempDir::new("wasm2cretonne").unwrap();
|
||||||
|
let file_path = tmp_dir.path().join("module.wasm");
|
||||||
|
File::create(file_path.clone()).unwrap();
|
||||||
|
Command::new("wast2wasm")
|
||||||
|
.arg(path.clone())
|
||||||
|
.arg("-o")
|
||||||
|
.arg(file_path.to_str().unwrap())
|
||||||
|
.output()
|
||||||
|
.or_else(|e| if let io::ErrorKind::NotFound = e.kind() {
|
||||||
|
return Err(String::from("wast2wasm not found"));
|
||||||
|
} else {
|
||||||
|
return Err(String::from(e.description()));
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
match read_wasm_file(file_path) {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(String::from(err.description()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None | Some(&_) => {
|
||||||
|
return Err(String::from("the file extension is not wasm or wast"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut dummy_runtime = DummyRuntime::new();
|
||||||
|
let translation = {
|
||||||
|
let mut runtime: &mut WasmRuntime = &mut dummy_runtime;
|
||||||
|
match translate_module(&data, runtime) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(string) => {
|
||||||
|
return Err(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
terminal.fg(term::color::GREEN).unwrap();
|
||||||
|
vprintln!(flag_verbose, " ok");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
if flag_check {
|
||||||
|
terminal.fg(term::color::MAGENTA).unwrap();
|
||||||
|
vprint!(flag_verbose, "Checking... ");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
for func in translation.functions.iter() {
|
||||||
|
let il = match func {
|
||||||
|
&FunctionTranslation::Import() => continue,
|
||||||
|
&FunctionTranslation::Code { ref il, .. } => il.clone(),
|
||||||
|
};
|
||||||
|
match verifier::verify_function(&il, None) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => return Err(pretty_verifier_error(&il, None, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terminal.fg(term::color::GREEN).unwrap();
|
||||||
|
vprintln!(flag_verbose, " ok");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
}
|
||||||
|
if flag_optimize {
|
||||||
|
terminal.fg(term::color::MAGENTA).unwrap();
|
||||||
|
vprint!(flag_verbose, "Optimizing... ");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
for func in translation.functions.iter() {
|
||||||
|
let mut il = match func {
|
||||||
|
&FunctionTranslation::Import() => continue,
|
||||||
|
&FunctionTranslation::Code { ref il, .. } => il.clone(),
|
||||||
|
};
|
||||||
|
let mut loop_analysis = LoopAnalysis::new();
|
||||||
|
let mut cfg = ControlFlowGraph::new();
|
||||||
|
cfg.compute(&il);
|
||||||
|
let mut domtree = DominatorTree::new();
|
||||||
|
domtree.compute(&mut il, &cfg);
|
||||||
|
loop_analysis.compute(&mut il, &mut cfg, &mut domtree);
|
||||||
|
let mut context = Context::new();
|
||||||
|
context.func = il;
|
||||||
|
context.cfg = cfg;
|
||||||
|
context.domtree = domtree;
|
||||||
|
context.loop_analysis = loop_analysis;
|
||||||
|
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, None) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
return Err(pretty_verifier_error(&context.func, None, err));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match context.licm() {
|
||||||
|
Ok(())=> (),
|
||||||
|
Err(error) => {
|
||||||
|
match error {
|
||||||
|
CtonError::Verifier(err) => {
|
||||||
|
return Err(pretty_verifier_error(&context.func, None, err));
|
||||||
|
}
|
||||||
|
CtonError::ImplLimitExceeded |
|
||||||
|
CtonError::CodeTooLarge => return Err(String::from(error.description())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, None) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => return Err(pretty_verifier_error(&context.func, None, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terminal.fg(term::color::GREEN).unwrap();
|
||||||
|
vprintln!(flag_verbose, " ok");
|
||||||
|
terminal.reset().unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pretty-print a verifier error.
|
||||||
|
pub fn pretty_verifier_error(func: &ir::Function,
|
||||||
|
isa: Option<&TargetIsa>,
|
||||||
|
err: verifier::Error)
|
||||||
|
-> String {
|
||||||
|
let msg = err.to_string();
|
||||||
|
let str1 = match err.location {
|
||||||
|
AnyEntity::Inst(inst) => {
|
||||||
|
format!("{}\n{}: {}\n\n",
|
||||||
|
msg,
|
||||||
|
inst,
|
||||||
|
func.dfg.display_inst(inst, isa))
|
||||||
|
}
|
||||||
|
_ => String::from(format!("{}\n", msg)),
|
||||||
|
};
|
||||||
|
format!("{}{}", str1, func.display(isa))
|
||||||
|
}
|
||||||
@@ -40,7 +40,8 @@ if [ -n "$needcheck" ]; then
|
|||||||
touch $tsfile || echo no target directory
|
touch $tsfile || echo no target directory
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PKGS="cretonne cretonne-reader cretonne-tools cretonne-frontend filecheck"
|
PKGS="cretonne cretonne-reader cretonne-tools cretonne-frontend cretonne-wasm \
|
||||||
|
filecheck "
|
||||||
cd "$topdir"
|
cd "$topdir"
|
||||||
for PKG in $PKGS
|
for PKG in $PKGS
|
||||||
do
|
do
|
||||||
|
|||||||
13
cranelift/wasmtests/arith.wast
Normal file
13
cranelift/wasmtests/arith.wast
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
(func $main (local i32)
|
||||||
|
(set_local 0 (i32.sub (i32.const 4) (i32.const 4)))
|
||||||
|
(if
|
||||||
|
(get_local 0)
|
||||||
|
(then unreachable)
|
||||||
|
(else (drop (i32.mul (i32.const 6) (get_local 0))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(start $main)
|
||||||
|
(data (i32.const 0) "abcdefgh")
|
||||||
|
)
|
||||||
10
cranelift/wasmtests/call.wast
Normal file
10
cranelift/wasmtests/call.wast
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
(module
|
||||||
|
(func $main (local i32)
|
||||||
|
(set_local 0 (i32.const 0))
|
||||||
|
(drop (call $inc))
|
||||||
|
)
|
||||||
|
(func $inc (result i32)
|
||||||
|
(i32.const 1)
|
||||||
|
)
|
||||||
|
(start $main)
|
||||||
|
)
|
||||||
22
cranelift/wasmtests/fibonacci.wast
Normal file
22
cranelift/wasmtests/fibonacci.wast
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
(func $main (local i32 i32 i32 i32)
|
||||||
|
(set_local 0 (i32.const 0))
|
||||||
|
(set_local 1 (i32.const 1))
|
||||||
|
(set_local 2 (i32.const 1))
|
||||||
|
(set_local 3 (i32.const 0))
|
||||||
|
(block
|
||||||
|
(loop
|
||||||
|
(br_if 1 (i32.gt_s (get_local 0) (i32.const 5)))
|
||||||
|
(set_local 3 (get_local 2))
|
||||||
|
(set_local 2 (i32.add (get_local 2) (get_local 1)))
|
||||||
|
(set_local 1 (get_local 3))
|
||||||
|
(set_local 0 (i32.add (get_local 0) (i32.const 1)))
|
||||||
|
(br 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(i32.store (i32.const 0) (get_local 2))
|
||||||
|
)
|
||||||
|
(start $main)
|
||||||
|
(data (i32.const 0) "0000")
|
||||||
|
)
|
||||||
8
cranelift/wasmtests/globals.wast
Normal file
8
cranelift/wasmtests/globals.wast
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
(module
|
||||||
|
(global $x (mut i32) (i32.const 4))
|
||||||
|
(memory 1)
|
||||||
|
(func $main (local i32)
|
||||||
|
(i32.store (i32.const 0) (get_global $x))
|
||||||
|
)
|
||||||
|
(start $main)
|
||||||
|
)
|
||||||
11
cranelift/wasmtests/memory.wast
Normal file
11
cranelift/wasmtests/memory.wast
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
(module
|
||||||
|
(memory 1)
|
||||||
|
(func $main (local i32)
|
||||||
|
(i32.store (i32.const 0) (i32.const 0x0))
|
||||||
|
(if (i32.load (i32.const 0))
|
||||||
|
(then (i32.store (i32.const 0) (i32.const 0xa)))
|
||||||
|
(else (i32.store (i32.const 0) (i32.const 0xb))))
|
||||||
|
)
|
||||||
|
(start $main)
|
||||||
|
(data (i32.const 0) "0000")
|
||||||
|
)
|
||||||
3
lib/wasm/.gitignore
vendored
Normal file
3
lib/wasm/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
target/
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
13
lib/wasm/Cargo.toml
Normal file
13
lib/wasm/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "cretonne-wasm"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["The Cretonne Project Developers"]
|
||||||
|
publish = false
|
||||||
|
description = "Translator from WebAssembly to Cretonne IL"
|
||||||
|
repository = "https://github.com/stoklund/cretonne"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasmparser = "0.6.1"
|
||||||
|
cretonne = { path = "../cretonne" }
|
||||||
|
cretonne-frontend = { path = "../frontend" }
|
||||||
1385
lib/wasm/src/code_translator.rs
Normal file
1385
lib/wasm/src/code_translator.rs
Normal file
File diff suppressed because it is too large
Load Diff
27
lib/wasm/src/lib.rs
Normal file
27
lib/wasm/src/lib.rs
Normal file
@@ -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};
|
||||||
288
lib/wasm/src/module_translator.rs
Normal file
288
lib/wasm/src/module_translator.rs
Normal file
@@ -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<FunctionTranslation>,
|
||||||
|
pub start_index: Option<FunctionIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<FuncRef, FunctionIndex>,
|
||||||
|
/// Find the index of a signature in the WebAssembly module thanks to a `SigRef`.
|
||||||
|
pub signatures: HashMap<SigRef, SignatureIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u8>,
|
||||||
|
runtime: &mut WasmRuntime)
|
||||||
|
-> Result<TranslationResult, String> {
|
||||||
|
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<Vec<SignatureIndex>> = None;
|
||||||
|
let mut globals = Vec::new();
|
||||||
|
let mut exports: Option<HashMap<FunctionIndex, String>> = None;
|
||||||
|
let mut next_input = ParserInput::Default;
|
||||||
|
let mut function_index: FunctionIndex = 0;
|
||||||
|
let mut function_imports_count = 0;
|
||||||
|
let mut start_index: Option<FunctionIndex> = 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<FunctionTranslation> = 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
lib/wasm/src/runtime/dummy.rs
Normal file
93
lib/wasm/src/runtime/dummy.rs
Normal file
@@ -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<Global>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Local>,
|
||||||
|
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<Local>, _: GlobalIndex, _: Value) {
|
||||||
|
// We do nothing
|
||||||
|
}
|
||||||
|
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, _: Value) -> Value {
|
||||||
|
builder.ins().iconst(I32, -1)
|
||||||
|
}
|
||||||
|
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value {
|
||||||
|
builder.ins().iconst(I32, -1)
|
||||||
|
}
|
||||||
|
fn translate_call_indirect<'a>(&self,
|
||||||
|
builder: &'a mut FunctionBuilder<Local>,
|
||||||
|
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<Local>,
|
||||||
|
_: 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
|
||||||
|
}
|
||||||
|
}
|
||||||
5
lib/wasm/src/runtime/mod.rs
Normal file
5
lib/wasm/src/runtime/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mod spec;
|
||||||
|
mod dummy;
|
||||||
|
|
||||||
|
pub use runtime::spec::WasmRuntime;
|
||||||
|
pub use runtime::dummy::DummyRuntime;
|
||||||
61
lib/wasm/src/runtime/spec.rs
Normal file
61
lib/wasm/src/runtime/spec.rs
Normal file
@@ -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<Local>,
|
||||||
|
global_index: GlobalIndex)
|
||||||
|
-> Value;
|
||||||
|
/// Translates a `set_global` wasm instruction.
|
||||||
|
fn translate_set_global(&self,
|
||||||
|
builder: &mut FunctionBuilder<Local>,
|
||||||
|
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<Local>, 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<Local>) -> Value;
|
||||||
|
/// Returns the base address of a wasm memory as a Cretonne `Value`.
|
||||||
|
fn translate_memory_base_address(&self,
|
||||||
|
builder: &mut FunctionBuilder<Local>,
|
||||||
|
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<Local>,
|
||||||
|
sig_ref: SigRef,
|
||||||
|
index_val: Value,
|
||||||
|
call_args: &[Value])
|
||||||
|
-> &'a [Value];
|
||||||
|
}
|
||||||
372
lib/wasm/src/sections_translator.rs
Normal file
372
lib/wasm/src/sections_translator.rs
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
//! 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<Vec<Signature>, SectionParsingError> {
|
||||||
|
let mut signatures: Vec<Signature> = 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<Vec<Import>, 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<Vec<SignatureIndex>, 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<HashMap<FunctionIndex, String>, SectionParsingError> {
|
||||||
|
let mut exports: HashMap<FunctionIndex, String> = 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<Vec<Memory>, SectionParsingError> {
|
||||||
|
let mut memories: Vec<Memory> = 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<Vec<Global>, 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<Global>)
|
||||||
|
-> 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<Global>)
|
||||||
|
-> 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<FunctionIndex> =
|
||||||
|
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(())
|
||||||
|
}
|
||||||
138
lib/wasm/src/translation_utils.rs
Normal file
138
lib/wasm/src/translation_utils.rs
Normal file
@@ -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<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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::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<cretonne::ir::Type, ()> {
|
||||||
|
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<Vec<cretonne::ir::Type>, ()> {
|
||||||
|
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
|
||||||
|
}
|
||||||
102
lib/wasm/tests/testsuite.rs
Normal file
102
lib/wasm/tests/testsuite.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
extern crate cretonne_wasm;
|
||||||
|
extern crate cretonne;
|
||||||
|
|
||||||
|
use cretonne_wasm::{translate_module, FunctionTranslation, DummyRuntime, WasmRuntime};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::fs;
|
||||||
|
use cretonne::ir;
|
||||||
|
use cretonne::ir::entities::AnyEntity;
|
||||||
|
use cretonne::isa::TargetIsa;
|
||||||
|
use cretonne::verifier;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn testsuite() {
|
||||||
|
let mut paths: Vec<_> = fs::read_dir("../../wasmtests")
|
||||||
|
.unwrap()
|
||||||
|
.map(|r| r.unwrap())
|
||||||
|
.collect();
|
||||||
|
paths.sort_by_key(|dir| dir.path());
|
||||||
|
for path in paths {
|
||||||
|
let path = path.path();
|
||||||
|
match handle_module(path) {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(message) => println!("{}", message),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
|
||||||
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let mut buf_reader = BufReader::new(file);
|
||||||
|
buf_reader.read_to_end(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_module(path: PathBuf) -> Result<(), String> {
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None | Some(&_) => {
|
||||||
|
return Err(String::from("the file extension is not wasm or wast"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut dummy_runtime = DummyRuntime::new();
|
||||||
|
let translation = {
|
||||||
|
let mut runtime: &mut WasmRuntime = &mut dummy_runtime;
|
||||||
|
match translate_module(&data, runtime) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(string) => {
|
||||||
|
return Err(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Pretty-print a verifier error.
|
||||||
|
pub fn pretty_verifier_error(func: &ir::Function,
|
||||||
|
isa: Option<&TargetIsa>,
|
||||||
|
err: verifier::Error)
|
||||||
|
-> String {
|
||||||
|
let msg = err.to_string();
|
||||||
|
let str1 = match err.location {
|
||||||
|
AnyEntity::Inst(inst) => {
|
||||||
|
format!("{}\n{}: {}\n\n",
|
||||||
|
msg,
|
||||||
|
inst,
|
||||||
|
func.dfg.display_inst(inst, isa))
|
||||||
|
}
|
||||||
|
_ => String::from(format!("{}\n", msg)),
|
||||||
|
};
|
||||||
|
format!("{}{}", str1, func.display(isa))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user