From f276a021cbb889c369b9e062838be8a604065541 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 22 Feb 2018 09:44:02 -0800 Subject: [PATCH] Update wasmstandalone for API changes. This updates to the latest faerie and cretonne API changes. --- Cargo.toml | 6 +-- lib/execute/Cargo.toml | 2 +- lib/execute/src/lib.rs | 33 ++++++++++++---- lib/obj/src/emit_module.rs | 19 +++++++--- lib/runtime/Cargo.toml | 2 +- lib/runtime/src/instance.rs | 13 +++++-- lib/runtime/src/lib.rs | 76 +++++++++++++++++++++++++++---------- src/main.rs | 3 +- src/wasm2obj.rs | 8 ++-- 9 files changed, 115 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 61a5982e73..66f4095f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,9 +25,9 @@ cretonne-native = { git = "https://github.com/stoklund/cretonne.git" } wasmstandalone_runtime = { path = "lib/runtime" } wasmstandalone_execute = { path = "lib/execute" } wasmstandalone_obj = { path = "lib/obj" } -docopt = "0.8.0" -serde = "1.0.8" -serde_derive = "1.0.8" +docopt = "0.8.3" +serde = "1.0.27" +serde_derive = "1.0.27" tempdir = "*" faerie = { git = "https://github.com/m4b/faerie" } diff --git a/lib/execute/Cargo.toml b/lib/execute/Cargo.toml index 9d14af0dcd..83f18a2dc9 100644 --- a/lib/execute/Cargo.toml +++ b/lib/execute/Cargo.toml @@ -10,5 +10,5 @@ license = "MIT/Apache-2.0" [dependencies] cretonne = { git = "https://github.com/stoklund/cretonne.git" } cretonne-wasm = { git = "https://github.com/stoklund/cretonne.git" } -region = "0.0.8" +region = "0.1.1" wasmstandalone_runtime = { path = "../runtime" } diff --git a/lib/execute/src/lib.rs b/lib/execute/src/lib.rs index cb59d9e9ee..1449a7f1d0 100644 --- a/lib/execute/src/lib.rs +++ b/lib/execute/src/lib.rs @@ -38,22 +38,36 @@ fn relocate(compilation: &mut Compilation, relocations: &wasmstandalone_runtime: // The relocations are relative to the relocation's address plus four bytes // TODO: Support architectures other than x64, and other reloc kinds. for (i, function_relocs) in relocations.iter().enumerate() { - for &(_reloc, func_index, offset) in function_relocs { - let target_func_address: isize = compilation.functions[func_index].as_ptr() as isize; + for ref r in function_relocs { + let target_func_address: isize = compilation.functions[r.func_index].as_ptr() as isize; let body = &mut compilation.functions[i]; unsafe { - let reloc_address: isize = body.as_mut_ptr().offset(offset as isize + 4) as isize; - let reloc_delta_i32: i32 = (target_func_address - reloc_address) as i32; + let reloc_address = body.as_mut_ptr().offset(r.offset as isize + 4) as isize; + let reloc_addend = r.addend as isize; + let reloc_delta_i32 = (target_func_address - reloc_address + reloc_addend) as i32; write_unaligned(reloc_address as *mut i32, reloc_delta_i32); } } } } +/// Create the VmCtx data structure for the JIT'd code to use. This must +/// match the VmCtx layout in the runtime. +fn make_vmctx(instance: &mut wasmstandalone_runtime::Instance) -> Vec<*mut u8> { + let mut memories = Vec::new(); + let mut vmctx = Vec::new(); + vmctx.push(instance.globals.as_mut_ptr()); + for mem in &mut instance.memories { + memories.push(mem.as_mut_ptr()); + } + vmctx.push(memories.as_mut_ptr() as *mut u8); + vmctx +} + /// Jumps to the code region of memory and execute the start function of the module. pub fn execute( compilation: &wasmstandalone_runtime::Compilation, - _instance: &wasmstandalone_runtime::Instance, + instance: &mut wasmstandalone_runtime::Instance, ) -> Result<(), String> { let start_index = compilation.module.start_func.ok_or_else(|| { String::from("No start function defined, aborting execution") @@ -70,17 +84,20 @@ pub fn execute( Err(err) => { return Err(format!( "failed to give executable permission to code: {}", - err.description() + err )) } } + + let vmctx = make_vmctx(instance); + // 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. unsafe { - let start_func = transmute::<_, fn()>(code_buf.as_ptr()); - start_func(); + let start_func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr()); + start_func(vmctx.as_ptr()); } Ok(()) } diff --git a/lib/obj/src/emit_module.rs b/lib/obj/src/emit_module.rs index b73e880803..6e37f4f187 100644 --- a/lib/obj/src/emit_module.rs +++ b/lib/obj/src/emit_module.rs @@ -1,9 +1,8 @@ +use cretonne::ir; use cretonne::settings; use cretonne::settings::Configurable; use faerie::Artifact; use wasmstandalone_runtime; -use std::error::Error; -use std::str; /// Emits a module that has been emitted with the `WasmRuntime` runtime /// implementation to a native object file. @@ -28,11 +27,19 @@ pub fn emit_module<'module>( let body = &compilation.functions[i]; let external_name = wasmstandalone_runtime::get_func_name(compilation.module.imported_funcs.len() + i); - let string_name = str::from_utf8(external_name.as_ref()).map_err(|err| { - err.description().to_string() - })?; + let func_index = match external_name { + ir::ExternalName::User { namespace, index } => { + debug_assert!(namespace == 0); + index + } + _ => panic!(), + }; - obj.add_code(string_name, body.clone()); + let string_name = format!("wasm_function[{}]", func_index); + + obj.define(string_name, body.clone()).map_err(|err| { + format!("{}", err) + })?; } Ok(()) diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index d7c7b7109d..15b2c7fef3 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -10,4 +10,4 @@ license = "Apache-2.0" [dependencies] cretonne = { git = "https://github.com/stoklund/cretonne.git" } cretonne-wasm = { git = "https://github.com/stoklund/cretonne.git" } -wasmparser = "0.13.0" +wasmparser = "0.14.1" diff --git a/lib/runtime/src/instance.rs b/lib/runtime/src/instance.rs index 7b07ad0bd7..9b1ac563a1 100644 --- a/lib/runtime/src/instance.rs +++ b/lib/runtime/src/instance.rs @@ -4,6 +4,7 @@ use cretonne::ir; use cton_wasm::GlobalIndex; use module::Module; +use DataInitializer; const PAGE_SIZE: usize = 65536; @@ -22,14 +23,14 @@ pub struct Instance { impl Instance { /// Create a new `Instance`. - pub fn new(module: &Module) -> Self { + pub fn new(module: &Module, data_initializers: &[DataInitializer]) -> Self { let mut result = Self { tables: Vec::new(), memories: Vec::new(), globals: Vec::new(), }; result.instantiate_tables(module); - result.instantiate_memories(module); + result.instantiate_memories(module, data_initializers); result.instantiate_globals(module); result } @@ -49,7 +50,7 @@ impl Instance { /// Allocate memory in `instance` for just the memories of the current module, /// without any initializers applied yet. - fn instantiate_memories(&mut self, module: &Module) { + fn instantiate_memories(&mut self, module: &Module, data_initializers: &[DataInitializer]) { debug_assert!(self.memories.is_empty()); // Allocate the underlying memory and initialize it to all zeros. self.memories.reserve_exact(module.memories.len()); @@ -59,6 +60,12 @@ impl Instance { v.resize(len, 0); self.memories.push(v); } + for init in data_initializers { + debug_assert!(init.base.is_none(), "globalvar base not supported yet"); + let to_init = &mut self.memories[init.memory_index][init.offset.. + init.offset + init.data.len()]; + to_init.copy_from_slice(init.data); + } } /// Allocate memory in `instance` for just the globals of the current module, diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index 8c91be56f2..2d79227089 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -28,12 +28,12 @@ use cretonne::ir; use cretonne::isa; use cretonne::settings; use cretonne::binemit; -use std::str::from_utf8; use std::error::Error; /// Compute a `ir::ExternalName` for a given wasm function index. pub fn get_func_name(func_index: FunctionIndex) -> cretonne::ir::ExternalName { - ir::ExternalName::new(format!("wasm_0x{:x}", func_index)) + debug_assert!(func_index as u32 as FunctionIndex == func_index); + ir::ExternalName::user(0, func_index as u32) } /// An entity to export. @@ -48,12 +48,11 @@ pub enum Export { Global(GlobalIndex), } -type RelocRef = u16; - -// Implementation of a relocation sink that just saves all the information for later -struct RelocSink<'func> { +/// Implementation of a relocation sink that just saves all the information for later +pub struct RelocSink<'func> { func: &'func ir::Function, - pub func_relocs: Vec<(RelocRef, FunctionIndex, binemit::CodeOffset)>, + /// Relocations recorded for the function. + pub func_relocs: Vec, } impl<'func> binemit::RelocSink for RelocSink<'func> { @@ -71,14 +70,21 @@ impl<'func> binemit::RelocSink for RelocSink<'func> { offset: binemit::CodeOffset, reloc: binemit::Reloc, name: &ExternalName, + addend: binemit::Addend, ) { - let name_bytes: &[u8] = name.as_ref(); - let name = from_utf8(name_bytes).unwrap(); - // See `get_func_name`; names are encoded as `wasm_0x...`, so grab the - // part after `0x...` and convert it back to an integer to get the index. // FIXME: Handle grow_memory/current_memory. - let func_index = FunctionIndex::from_str_radix(&name[7..], 16).unwrap(); - self.func_relocs.push((reloc.0, func_index, offset)); + let func_index = if let ExternalName::User { namespace, index } = *name { + debug_assert!(namespace == 0); + index + } else { + panic!("unrecognized external name") + } as usize; + self.func_relocs.push(Relocation { + reloc, + func_index, + offset, + addend, + }); } fn reloc_jt( &mut self, @@ -100,6 +106,18 @@ impl<'func> RelocSink<'func> { } } +/// A data initializer for linear memory. +pub struct DataInitializer<'data> { + /// The index of the memory to initialize. + pub memory_index: MemoryIndex, + /// Optionally a globalvar base to initialize at. + pub base: Option, + /// A constant offset to initialize at. + pub offset: usize, + /// The initialization data. + pub data: &'data [u8], +} + /// References to the input wasm data buffer to be decoded and processed later. /// separately from the main module translation. pub struct LazyContents<'data> { @@ -107,7 +125,7 @@ pub struct LazyContents<'data> { pub function_body_inputs: Vec<&'data [u8]>, /// References to the data initializers. - pub data_initializers: Vec<(MemoryIndex, Option, usize, &'data [u8])>, + pub data_initializers: Vec>, } impl<'data> LazyContents<'data> { @@ -259,12 +277,13 @@ impl<'module_environment> cton_wasm::FuncEnvironment for FuncEnvironment<'module base: memories_base, offset: Offset32::new(offset32), }); - func.create_heap(ir::HeapData { + let h = func.create_heap(ir::HeapData { base: ir::HeapBase::GlobalVar(heap_base), min_size: 0.into(), guard_size: 0x8000_0000.into(), style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() }, - }) + }); + h } fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef { @@ -320,8 +339,9 @@ impl<'module_environment> cton_wasm::FuncEnvironment for FuncEnvironment<'module params: vec![AbiParam::new(I32)], returns: vec![AbiParam::new(I32)], }); + // FIXME: Use a real ExternalName system. pos.func.import_function(ExtFuncData { - name: ExternalName::new("grow_memory"), + name: ExternalName::testcase("grow_memory"), signature: sig_ref, }) }); @@ -344,8 +364,9 @@ impl<'module_environment> cton_wasm::FuncEnvironment for FuncEnvironment<'module params: Vec::new(), returns: vec![AbiParam::new(I32)], }); + // FIXME: Use a real ExternalName system. pos.func.import_function(ExtFuncData { - name: ExternalName::new("current_memory"), + name: ExternalName::testcase("current_memory"), signature: sig_ref, }) }); @@ -446,12 +467,12 @@ impl<'data, 'module> cton_wasm::ModuleEnvironment<'data> for ModuleEnvironment<' data: &'data [u8], ) { debug_assert!(base.is_none(), "global-value offsets not supported yet"); - self.lazy.data_initializers.push(( + self.lazy.data_initializers.push(DataInitializer { memory_index, base, offset, data, - )); + }); } fn declare_func_export(&mut self, func_index: FunctionIndex, name: &str) { @@ -493,8 +514,21 @@ impl<'data, 'module> cton_wasm::ModuleEnvironment<'data> for ModuleEnvironment<' } } +/// A record of a relocation to perform. +#[derive(Debug)] +pub struct Relocation { + /// The relocation code. + pub reloc: binemit::Reloc, + /// The function index. + pub func_index: FunctionIndex, + /// The offset where to apply the relocation. + pub offset: binemit::CodeOffset, + /// The addend to add to the relocation value. + pub addend: binemit::Addend, +} + /// Relocations to apply to function bodies. -pub type Relocations = Vec>; +pub type Relocations = Vec>; /// The result of translating via `ModuleEnvironment`. pub struct ModuleTranslation<'data, 'module> { diff --git a/src/main.rs b/src/main.rs index 194dbba9fd..8892b87bca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,7 +126,8 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri let translation = environ.finish_translation(); let instance = match compile_module(isa, &translation) { Ok(compilation) => { - let mut instance = Instance::new(compilation.module); + let mut instance = + Instance::new(compilation.module, &translation.lazy.data_initializers); execute(&compilation, &mut instance)?; instance } diff --git a/src/wasm2obj.rs b/src/wasm2obj.rs index d629140f81..fd98a08233 100644 --- a/src/wasm2obj.rs +++ b/src/wasm2obj.rs @@ -95,12 +95,14 @@ fn handle_module(path: PathBuf, output: &str) -> Result<(), String> { let mut environ = wasmstandalone_runtime::ModuleEnvironment::new(isa.flags(), &mut module); translate_module(&data, &mut environ)?; - let mut obj = Artifact::new(faerie_target(&*isa)?, Some(String::from(output))); + let mut obj = Artifact::new(faerie_target(&*isa)?, String::from(output)); // FIXME: We need to initialize memory in a way that supports alternate // memory spaces, imported base addresses, and offsets. - for &(_mem_index, _base, _offset, data) in &environ.lazy.data_initializers { - obj.add_data("memory", Vec::from(data)); + for init in &environ.lazy.data_initializers { + obj.define("memory", Vec::from(init.data)).map_err(|err| { + format!("{}", err) + })?; } let translation = environ.finish_translation();