Import the wasm2obj experiment and minimally update it.

This commit is contained in:
Dan Gohman
2017-09-23 14:55:22 -07:00
parent 06f0b00c2d
commit 0c78a2f298
8 changed files with 300 additions and 13 deletions

View File

@@ -12,12 +12,18 @@ publish = false
name = "wasmstandalone" name = "wasmstandalone"
path = "src/main.rs" path = "src/main.rs"
[[bin]]
name = "wasm2obj"
path = "src/wasm2obj.rs"
[dependencies] [dependencies]
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" } cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
cretonne-frontend = { path = "/home/sunfish/rust/cretonne/lib/frontend" }
cretonne-reader = { path = "/home/sunfish/rust/cretonne/lib/reader" } cretonne-reader = { path = "/home/sunfish/rust/cretonne/lib/reader" }
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" } cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
cretonne-native = { path = "/home/sunfish/rust/cretonne/lib/native" } cretonne-native = { path = "/home/sunfish/rust/cretonne/lib/native" }
wasmstandalone = { path = "lib/wasmstandalone" } wasmstandalone = { path = "lib/wasmstandalone" }
wasm2obj = { path = "lib/wasm2obj" }
wasmparser = "0.8.2" wasmparser = "0.8.2"
wasmtext = { git = "https://github.com/yurydelendik/wasmtext" } wasmtext = { git = "https://github.com/yurydelendik/wasmtext" }
filecheck = "0.0.1" filecheck = "0.0.1"
@@ -27,5 +33,6 @@ serde_derive = "1.0.8"
num_cpus = "1.5.1" num_cpus = "1.5.1"
term = "0.4.6" term = "0.4.6"
tempdir = "*" tempdir = "*"
faerie = { git = "https://github.com/m4b/faerie" }
[workspace] [workspace]

3
lib/wasm2obj/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
target/
**/*.rs.bk
Cargo.lock

11
lib/wasm2obj/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "wasm2obj"
version = "0.0.0"
authors = ["The Cretonne Project Developers"]
publish = false
[dependencies]
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
cretonne-frontend = { path = "/home/sunfish/rust/cretonne/lib/frontend" }
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
faerie = { git = "https://github.com/m4b/faerie" }

View File

@@ -0,0 +1,116 @@
use cretonne::Context;
use cretonne::settings;
use cretonne::isa::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 cton_wasm::TranslationResult;
use std::collections::HashMap;
use std::fmt::Write;
use faerie::Artifact;
type RelocRef = u16;
// Implementation of a relocation sink that just saves all the information for later
struct FaerieRelocSink {
ebbs: HashMap<RelocRef, (Ebb, CodeOffset)>,
funcs: HashMap<RelocRef, (FuncRef, CodeOffset)>,
jts: HashMap<RelocRef, (JumpTable, CodeOffset)>,
}
impl RelocSink for FaerieRelocSink {
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 FaerieRelocSink {
fn new() -> FaerieRelocSink {
FaerieRelocSink {
ebbs: HashMap::new(),
funcs: HashMap::new(),
jts: HashMap::new(),
}
}
}
/// Emits a module that has been emitted with the `WasmRuntime` runtime
/// implementation to a native object file.
pub fn emit_module(
trans_result: &TranslationResult,
obj: &mut Artifact,
isa: &TargetIsa,
) -> Result<(), String> {
debug_assert!(
trans_result.start_index.is_none() ||
trans_result.start_index.unwrap() >= trans_result.function_imports_count,
"imported start functions not supported yet"
);
let mut shared_builder = settings::builder();
shared_builder.enable("enable_verifier").expect(
"Missing enable_verifier setting",
);
for function in &trans_result.functions {
let mut context = Context::new();
verify_function(function, isa).unwrap();
context.func = function.clone(); // TODO: Avoid this clone.
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<u8> = Vec::with_capacity(code_size);
code_buf.resize(code_size, 0);
let mut relocsink = FaerieRelocSink::new();
context.emit_to_memory(code_buf.as_mut_ptr(), &mut relocsink, &*isa);
// FIXME: get the real linkage name of the function
obj.add_code("the_function_name", code_buf);
assert!(relocsink.jts.is_empty(), "jump tables not yet implemented");
assert!(relocsink.ebbs.is_empty(), "ebb relocs not yet implemented");
assert!(
relocsink.funcs.is_empty(),
"function relocs not yet implemented"
);
// FIXME: handle imports
}
Ok(())
}
/// Pretty-print a verifier error.
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.
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()
}
}

7
lib/wasm2obj/src/lib.rs Normal file
View File

@@ -0,0 +1,7 @@
extern crate cretonne;
extern crate cton_wasm;
extern crate faerie;
mod emit_module;
pub use emit_module::emit_module;

View File

@@ -16,7 +16,7 @@ use std::ptr::copy_nonoverlapping;
use std::ptr::write; use std::ptr::write;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum TableElement { pub enum TableElement {
Trap(), Trap(),
Function(FunctionIndex), Function(FunctionIndex),
} }
@@ -31,15 +31,22 @@ struct GlobalsData {
info: Vec<GlobalInfo>, info: Vec<GlobalInfo>,
} }
struct TableData { /// A description of a WebAssembly table.
data: Vec<usize>, pub struct TableData {
elements: Vec<TableElement>, /// The data stored in the table.
info: Table, pub data: Vec<u8>,
/// Function indices to be stored in the table.
pub elements: Vec<TableElement>,
/// The description of the table.
pub info: Table,
} }
struct MemoryData { /// A description of a WebAssembly linear memory.
data: Vec<u8>, pub struct MemoryData {
info: Memory, /// The data stored in the memory.
pub data: Vec<u8>,
/// The description of the memory.
pub info: Memory,
} }
const PAGE_SIZE: usize = 65536; const PAGE_SIZE: usize = 65536;
@@ -58,8 +65,10 @@ pub struct StandaloneRuntime {
imported_funcs: Vec<ir::FunctionName>, imported_funcs: Vec<ir::FunctionName>,
globals: GlobalsData, globals: GlobalsData,
tables: Vec<TableData>, /// WebAssembly tables.
memories: Vec<MemoryData>, pub tables: Vec<TableData>,
/// WebAssembly linear memories.
pub memories: Vec<MemoryData>,
instantiated: bool, instantiated: bool,
has_current_memory: Option<FuncRef>, has_current_memory: Option<FuncRef>,

View File

@@ -1,5 +1,5 @@
//! CLI tool to use the functions provided by crates [wasm2cretonne](../wasm2cretonne/index.html) //! CLI tool to use the functions provided by the [wasmstandalone](../wasmstandalone/index.html)
//! and [wasmstandalone](../wasmstandalone/index.html). //! crate.
//! //!
//! Reads Wasm binary files (one Wasm module per file), translates the functions' code to Cretonne //! 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 //! IL. Can also executes the `start` function of the module by laying out the memories, globals

134
src/wasm2obj.rs Normal file
View File

@@ -0,0 +1,134 @@
//! Translation from wasm to native object files.
//!
//! Reads a Wasm binary file, translates the functions' code to Cretonne
//! IL, then translates it to native code, and writes it out to a native
//! object file with relocations.
extern crate cton_wasm;
extern crate wasm2obj;
extern crate cretonne;
extern crate cton_native;
extern crate docopt;
#[macro_use]
extern crate serde_derive;
extern crate wasmstandalone;
extern crate faerie;
use cton_wasm::translate_module;
use cretonne::settings;
use wasm2obj::emit_module;
use std::path::PathBuf;
use std::fs::File;
use std::error::Error;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use docopt::Docopt;
use std::path::Path;
use std::process;
use std::fmt::format;
use faerie::{Artifact, Elf, Target};
const USAGE: &str = "
Wasm to native object translation utility.
Takes a binary WebAssembly module into a native object file.
The translation is dependent on the runtime chosen.
The default is a dummy runtime that produces placeholder values.
Usage:
wasm2obj <file> -o <output>
wasm2obj --help | --version
Options:
-v, --verbose displays the module and translated functions
-h, --help print this help message
--version print the Cretonne version
";
#[derive(Deserialize, Debug, Clone)]
struct Args {
arg_file: String,
arg_output: String,
}
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 main() {
let args: Args = Docopt::new(USAGE)
.and_then(|d| {
d.help(true)
.version(Some(String::from("0.0.0")))
.deserialize()
})
.unwrap_or_else(|e| e.exit());
let path = Path::new(&args.arg_file);
match handle_module(path.to_path_buf(), &args.arg_output) {
Ok(()) => {}
Err(message) => {
println!(" error: {}", message);
process::exit(1);
}
}
}
fn handle_module(path: PathBuf, output: &str) -> Result<(), String> {
let data = match read_wasm_file(path) {
Ok(data) => data,
Err(err) => {
return Err(String::from(err.description()));
}
};
let (flag_builder, isa_builder) = cton_native::builders().unwrap_or_else(|_| {
panic!("host machine is not a supported target");
});
let isa = isa_builder.finish(settings::Flags::new(&flag_builder));
let mut runtime = wasmstandalone::StandaloneRuntime::with_flags(isa.flags().clone());
let translation = {
match translate_module(&data, &mut runtime) {
Ok(x) => x,
Err(string) => {
return Err(string);
}
}
};
// FIXME: Make the target a parameter.
// FIXME: Make the output filename a parameter.
let mut obj = Artifact::new(Target::X86_64, Some(String::from(output)));
emit_module(&translation, &mut obj, &*isa)?;
if !runtime.tables.is_empty() {
if runtime.tables.len() > 1 {
return Err(String::from("multiple tables not supported yet"));
}
obj.add_data("table", runtime.tables[0].data.clone());
}
if !runtime.memories.is_empty() {
if runtime.memories.len() > 1 {
return Err(String::from("multiple memories not supported yet"));
}
obj.add_data("memory", runtime.memories[0].data.clone());
}
// FIXME: Make the format a parameter.
let file = ::std::fs::File::create(Path::new(output)).map_err(|x| {
format(format_args!("{}", x))
})?;
obj.write::<Elf>(file).map_err(
|x| format(format_args!("{}", x)),
)?;
Ok(())
}