diff --git a/lib/environ/src/compilation.rs b/lib/environ/src/compilation.rs index 87b63d0a9c..427bee39c0 100644 --- a/lib/environ/src/compilation.rs +++ b/lib/environ/src/compilation.rs @@ -52,10 +52,10 @@ impl binemit::RelocSink for RelocSink { let reloc_target = if let ExternalName::User { namespace, index } = *name { debug_assert!(namespace == 0); RelocationTarget::UserFunc(FuncIndex::new(index as usize)) - } else if *name == ExternalName::testcase("grow_memory") { - RelocationTarget::GrowMemory - } else if *name == ExternalName::testcase("current_memory") { - RelocationTarget::CurrentMemory + } else if *name == ExternalName::testcase("wasmtime_memory_grow") { + RelocationTarget::MemoryGrow + } else if *name == ExternalName::testcase("wasmtime_memory_size") { + RelocationTarget::MemorySize } else { panic!("unrecognized external name") }; @@ -104,9 +104,9 @@ pub enum RelocationTarget { /// The user function index. UserFunc(FuncIndex), /// Function for growing the default memory by the specified amount of pages. - GrowMemory, + MemoryGrow, /// Function for query current size of the default linear memory. - CurrentMemory, + MemorySize, } /// Relocations to apply to function bodies. diff --git a/lib/environ/src/module.rs b/lib/environ/src/module.rs index 1e9903804b..9a52b57061 100644 --- a/lib/environ/src/module.rs +++ b/lib/environ/src/module.rs @@ -98,7 +98,7 @@ pub struct Module { pub signatures: PrimaryMap, /// Names of imported functions. - pub imported_funcs: Vec<(String, String)>, + pub imported_funcs: PrimaryMap, /// Types of functions, imported and local. pub functions: PrimaryMap, @@ -127,7 +127,7 @@ impl Module { pub fn new() -> Self { Self { signatures: PrimaryMap::new(), - imported_funcs: Vec::new(), + imported_funcs: PrimaryMap::new(), functions: PrimaryMap::new(), tables: PrimaryMap::new(), memory_plans: PrimaryMap::new(), diff --git a/lib/execute/Cargo.toml b/lib/execute/Cargo.toml index 25e3d687c8..84907f6856 100644 --- a/lib/execute/Cargo.toml +++ b/lib/execute/Cargo.toml @@ -20,15 +20,13 @@ lazy_static = "1.2.0" libc = { version = "0.2.44", default-features = false } errno = "0.2.4" cast = { version = "0.2.2", default-features = false } +memoffset = "0.2.1" [build-dependencies] cmake = "0.1.35" bindgen = "0.44.0" regex = "1.0.6" -[dev-dependencies] -memoffset = "0.2.1" - [features] default = ["std"] std = ["cranelift-codegen/std", "cranelift-wasm/std"] diff --git a/lib/execute/src/execute.rs b/lib/execute/src/execute.rs index 91f5ab28f5..c4310c5b33 100644 --- a/lib/execute/src/execute.rs +++ b/lib/execute/src/execute.rs @@ -5,15 +5,15 @@ use code::Code; use cranelift_codegen::binemit::Reloc; use cranelift_codegen::isa::TargetIsa; use cranelift_entity::{EntityRef, PrimaryMap}; -use cranelift_wasm::{DefinedFuncIndex, MemoryIndex, TableIndex}; +use cranelift_wasm::{DefinedFuncIndex, MemoryIndex}; use instance::Instance; use invoke::{invoke_by_index, InvokeOutcome}; -use memory::LinearMemory; use region::protect; use region::Protection; -use std::ptr::{self, write_unaligned}; +use std::ptr::write_unaligned; use std::string::String; use std::vec::Vec; +use vmcontext::VMContext; use wasmtime_environ::{ compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget, }; @@ -53,7 +53,7 @@ fn relocate( RelocationTarget::UserFunc(index) => match module.defined_func_index(index) { Some(f) => compilation.functions[f].as_ptr() as usize, None => { - let func = &module.imported_funcs[index.index()]; + let func = &module.imported_funcs[index]; match imports(&func.0, &func.1) { Some(ptr) => ptr, None => { @@ -62,8 +62,8 @@ fn relocate( } } }, - RelocationTarget::GrowMemory => grow_memory as usize, - RelocationTarget::CurrentMemory => current_memory as usize, + RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize, + RelocationTarget::MemorySize => wasmtime_memory_size as usize, }; let body = &mut compilation.functions[i]; @@ -93,52 +93,20 @@ fn relocate( } } -extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 { - unsafe { - // FIXME: update the VMMemory's size - let instance = (*vmctx.offset(4)) as *mut Instance; - (*instance) - .memory_mut(MemoryIndex::new(memory_index as usize)) - .grow(size) - .unwrap_or(u32::max_value()) - } +extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 { + let instance = unsafe { (&mut *vmctx).instance() }; + let memory_index = MemoryIndex::new(memory_index as usize); + + instance + .memory_grow(memory_index, size) + .unwrap_or(u32::max_value()) } -extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 { - unsafe { - // FIXME: read the VMMemory's size instead - let instance = (*vmctx.offset(4)) as *mut Instance; - (*instance) - .memory_mut(MemoryIndex::new(memory_index as usize)) - .current_size() - } -} +extern "C" fn wasmtime_memory_size(memory_index: u32, vmctx: *mut VMContext) -> u32 { + let instance = unsafe { (&mut *vmctx).instance() }; + let memory_index = MemoryIndex::new(memory_index as usize); -/// Create the VmCtx data structure for the JIT'd code to use. This must -/// match the VmCtx layout in the environment. -fn make_vmctx(instance: &mut Instance) -> Vec<*mut u8> { - debug_assert!( - instance.tables.len() <= 1, - "non-default tables is not supported" - ); - - let (default_table_ptr, default_table_len) = instance - .tables - .get_mut(TableIndex::new(0)) - .map(|table| (table.as_mut_ptr() as *mut u8, table.len())) - .unwrap_or((ptr::null_mut(), 0)); - - // FIXME: Actually use environ's VMContext struct - let mut vmctx = Vec::new(); - vmctx.push(instance.globals.as_mut_ptr()); - // FIXME: These need to be VMMemory now - vmctx.push(instance.mem_base_addrs.as_mut_ptr() as *mut u8); - // FIXME: These need to be VMTable now - vmctx.push(default_table_ptr); - vmctx.push(default_table_len as *mut u8); - vmctx.push(instance as *mut Instance as *mut u8); - - vmctx + instance.memory_size(memory_index) } /// prepares the execution context @@ -148,7 +116,7 @@ pub fn finish_instantiation( module: &Module, compilation: &Compilation, instance: &mut Instance, -) -> Result, String> { +) -> Result<(), String> { // TODO: Put all the function bodies into a page-aligned memory region, and // then make them ReadExecute rather than ReadWriteExecute. for code_buf in compilation.functions.values() { @@ -169,17 +137,9 @@ pub fn finish_instantiation( } } - // Collect all memory base addresses and Vec. - instance.mem_base_addrs = instance - .memories - .values_mut() - .map(LinearMemory::base_addr) - .collect::>(); - - let mut vmctx = make_vmctx(instance); - if let Some(start_index) = module.start_func { - let result = invoke_by_index(code, isa, module, compilation, &mut vmctx, start_index, &[])?; + let vmctx = instance.vmctx(); + let result = invoke_by_index(code, isa, module, compilation, vmctx, start_index, &[])?; match result { InvokeOutcome::Returned { values } => { assert!(values.is_empty()); @@ -190,5 +150,5 @@ pub fn finish_instantiation( } } - Ok(vmctx) + Ok(()) } diff --git a/lib/execute/src/instance.rs b/lib/execute/src/instance.rs index 8cab4cd2fe..32c22d4dc1 100644 --- a/lib/execute/src/instance.rs +++ b/lib/execute/src/instance.rs @@ -1,29 +1,35 @@ //! An `Instance` contains all the runtime state used by execution of a wasm //! module. -use cranelift_codegen::ir; use cranelift_entity::EntityRef; use cranelift_entity::PrimaryMap; use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex}; use memory::LinearMemory; use std::string::String; -use std::vec::Vec; -use wasmtime_environ::{Compilation, DataInitializer, Module, TableElements}; +use table::{AnyFunc, Table}; +use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable}; +use wasmtime_environ::{Compilation, DataInitializer, Module}; /// An Instance of a WebAssemby module. #[derive(Debug)] pub struct Instance { - /// WebAssembly table data. - pub tables: PrimaryMap>, - /// WebAssembly linear memory data. - pub memories: PrimaryMap, + memories: PrimaryMap, - /// WebAssembly global variable data. - pub globals: Vec, + /// WebAssembly table data. + tables: PrimaryMap, /// Memory base address vector pointed to by vmctx. - pub mem_base_addrs: Vec<*mut u8>, + vmctx_memories: PrimaryMap, + + /// WebAssembly global variable data. + vmctx_globals: PrimaryMap, + + /// Table storage base address vector pointed to by vmctx. + vmctx_tables: PrimaryMap, + + /// Context pointer used by JIT code. + vmctx: VMContext, } impl Instance { @@ -33,82 +39,68 @@ impl Instance { compilation: &Compilation, data_initializers: &[DataInitializer], ) -> Result { - let mut result = Self { - tables: PrimaryMap::new(), - memories: PrimaryMap::new(), - globals: Vec::new(), - mem_base_addrs: Vec::new(), - }; - result.instantiate_tables(module, compilation, &module.table_elements); - result.instantiate_memories(module, data_initializers)?; - result.instantiate_globals(module); - Ok(result) + let mut memories = instantiate_memories(module, data_initializers)?; + let mut tables = instantiate_tables(module, compilation); + + let mut vmctx_memories = memories + .values_mut() + .map(LinearMemory::vmmemory) + .collect::>(); + + let mut vmctx_globals = instantiate_globals(module); + + let mut vmctx_tables = tables + .values_mut() + .map(Table::vmtable) + .collect::>(); + + let vmctx_memories_ptr = vmctx_memories.values_mut().into_slice().as_mut_ptr(); + let vmctx_globals_ptr = vmctx_globals.values_mut().into_slice().as_mut_ptr(); + let vmctx_tables_ptr = vmctx_tables.values_mut().into_slice().as_mut_ptr(); + + Ok(Self { + memories, + tables, + vmctx_memories, + vmctx_globals, + vmctx_tables, + vmctx: VMContext::new(vmctx_memories_ptr, vmctx_globals_ptr, vmctx_tables_ptr), + }) } - /// Allocate memory in `self` for just the tables of the current module. - fn instantiate_tables( - &mut self, - module: &Module, - compilation: &Compilation, - table_initializers: &[TableElements], - ) { - debug_assert!(self.tables.is_empty()); - self.tables.reserve_exact(module.tables.len()); - for table in module.tables.values() { - let len = table.minimum as usize; - let mut v = Vec::with_capacity(len); - v.resize(len, 0); - self.tables.push(v); - } - for init in table_initializers { - debug_assert!(init.base.is_none(), "globalvar base not supported yet"); - let to_init = - &mut self.tables[init.table_index][init.offset..init.offset + init.elements.len()]; - for (i, func_idx) in init.elements.iter().enumerate() { - let code_buf = &compilation.functions[module.defined_func_index(*func_idx).expect( - "table element initializer with imported function not supported yet", - )]; - to_init[i] = code_buf.as_ptr() as usize; - } - } + /// Return the vmctx pointer to be passed into JIT code. + pub fn vmctx(&mut self) -> *mut VMContext { + &mut self.vmctx as *mut VMContext } - /// Allocate memory in `instance` for just the memories of the current module. - fn instantiate_memories( - &mut self, - module: &Module, - data_initializers: &[DataInitializer], - ) -> Result<(), String> { - debug_assert!(self.memories.is_empty()); - // Allocate the underlying memory and initialize it to all zeros. - self.memories.reserve_exact(module.memory_plans.len()); - for plan in module.memory_plans.values() { - let v = LinearMemory::new(&plan)?; - self.memories.push(v); - } - for init in data_initializers { - debug_assert!(init.base.is_none(), "globalvar base not supported yet"); - let mem_mut = self.memories[init.memory_index].as_mut(); - let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()]; - to_init.copy_from_slice(init.data); - } - Ok(()) + /// Return the offset from the vmctx pointer to its containing Instance. + pub fn vmctx_offset() -> isize { + offset_of!(Instance, vmctx) as isize } - /// Allocate memory in `instance` for just the globals of the current module, - /// without any initializers applied yet. - fn instantiate_globals(&mut self, module: &Module) { - debug_assert!(self.globals.is_empty()); - // Allocate the underlying memory and initialize it to all zeros. - let globals_data_size = module.globals.len() * 8; - self.globals.resize(globals_data_size, 0); - } - - /// Returns a mutable reference to a linear memory under the specified index. - pub fn memory_mut(&mut self, memory_index: MemoryIndex) -> &mut LinearMemory { - self.memories + /// Grow memory by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + pub fn memory_grow(&mut self, memory_index: MemoryIndex, delta: u32) -> Option { + let result = self + .memories .get_mut(memory_index) .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index())) + .grow(delta); + + // Keep current the VMContext pointers used by JIT code. + self.vmctx_memories[memory_index] = self.memories[memory_index].vmmemory(); + + result + } + + /// Returns the number of allocated wasm pages. + pub fn memory_size(&mut self, memory_index: MemoryIndex) -> u32 { + self.memories + .get(memory_index) + .unwrap_or_else(|| panic!("no memory for index {}", memory_index.index())) + .size() } /// Returns a slice of the contents of allocated linear memory. @@ -121,9 +113,64 @@ impl Instance { } /// Shows the value of a global variable. - pub fn inspect_global(&self, global_index: GlobalIndex, ty: ir::Type) -> &[u8] { - let offset = global_index.index() * 8; - let len = ty.bytes() as usize; - &self.globals[offset..offset + len] + pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal { + &self.vmctx_globals[global_index] } } + +/// Allocate memory for just the memories of the current module. +fn instantiate_memories( + module: &Module, + data_initializers: &[DataInitializer], +) -> Result, String> { + let mut memories = PrimaryMap::with_capacity(module.memory_plans.len()); + for plan in module.memory_plans.values() { + memories.push(LinearMemory::new(&plan)?); + } + + for init in data_initializers { + debug_assert!(init.base.is_none(), "globalvar base not supported yet"); + let mem_mut = memories[init.memory_index].as_mut(); + let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()]; + to_init.copy_from_slice(init.data); + } + + Ok(memories) +} + +/// Allocate memory for just the tables of the current module. +fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap { + let mut tables = PrimaryMap::with_capacity(module.tables.len()); + for table in module.tables.values() { + tables.push(Table::new(table)); + } + + for init in &module.table_elements { + debug_assert!(init.base.is_none(), "globalvar base not supported yet"); + let slice = &mut tables[init.table_index].as_mut(); + let subslice = &mut slice[init.offset..init.offset + init.elements.len()]; + for (i, func_idx) in init.elements.iter().enumerate() { + let code_buf = &compilation.functions[module.defined_func_index(*func_idx).expect( + "table element initializer with imported function not supported yet", + )]; + subslice[i] = AnyFunc { + func_ptr: code_buf.as_ptr(), + type_id: 0, // TODO: Implement signature checking. + }; + } + } + + tables +} + +/// Allocate memory for just the globals of the current module, +/// without any initializers applied yet. +fn instantiate_globals(module: &Module) -> PrimaryMap { + let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len()); + + for _ in 0..module.globals.len() { + vmctx_globals.push(VMGlobal::default()); + } + + vmctx_globals +} diff --git a/lib/execute/src/invoke.rs b/lib/execute/src/invoke.rs index 1519e90f04..b34b49aeff 100644 --- a/lib/execute/src/invoke.rs +++ b/lib/execute/src/invoke.rs @@ -11,6 +11,7 @@ use std::ptr; use std::string::String; use std::vec::Vec; use traphandlers::call_wasm; +use vmcontext::VMContext; use wasmtime_environ::{Compilation, Export, Module, RelocSink}; /// A runtime value. @@ -91,7 +92,7 @@ pub fn invoke( isa: &isa::TargetIsa, module: &Module, compilation: &Compilation, - vmctx: &mut Vec<*mut u8>, + vmctx: *mut VMContext, function: &str, args: &[Value], ) -> Result { @@ -109,7 +110,7 @@ pub fn invoke_by_index( isa: &isa::TargetIsa, module: &Module, compilation: &Compilation, - vmctx: &mut Vec<*mut u8>, + vmctx: *mut VMContext, fn_index: FuncIndex, args: &[Value], ) -> Result { @@ -138,21 +139,14 @@ pub fn invoke_by_index( return Err("failed to install signal handlers".to_string()); } - call_through_wrapper( - code, - isa, - exec_code_buf as usize, - vmctx.as_ptr() as usize, - args, - &sig, - ) + call_through_wrapper(code, isa, exec_code_buf as usize, vmctx, args, &sig) } fn call_through_wrapper( code: &mut Code, isa: &isa::TargetIsa, callee: usize, - vmctx: usize, + vmctx: *mut VMContext, args: &[Value], sig: &ir::Signature, ) -> Result { diff --git a/lib/execute/src/lib.rs b/lib/execute/src/lib.rs index 55b690c50d..537de949b4 100644 --- a/lib/execute/src/lib.rs +++ b/lib/execute/src/lib.rs @@ -40,7 +40,6 @@ extern crate alloc; #[macro_use] extern crate lazy_static; extern crate libc; -#[cfg(test)] #[macro_use] extern crate memoffset; extern crate cast; @@ -52,13 +51,16 @@ mod invoke; mod memory; mod mmap; mod signalhandlers; +mod table; mod traphandlers; +mod vmcontext; pub use code::Code; pub use execute::{compile_and_link_module, finish_instantiation}; pub use instance::Instance; pub use invoke::{invoke, InvokeOutcome, Value}; pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind}; +pub use vmcontext::VMContext; #[cfg(not(feature = "std"))] mod std { diff --git a/lib/execute/src/memory.rs b/lib/execute/src/memory.rs index 4ee93b609e..b55e28a278 100644 --- a/lib/execute/src/memory.rs +++ b/lib/execute/src/memory.rs @@ -1,16 +1,16 @@ -//! Memory management for linear memory. +//! Memory management for linear memories. +//! +//! `LinearMemory` is to WebAssembly linear memories what `Table` is to WebAssembly tables. use cast; use mmap::Mmap; use region; -use std::fmt; use std::string::String; +use vmcontext::VMMemory; use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; /// A linear memory instance. -/// -/// This linear memory has a stable base address and at the same time allows -/// for dynamical growing. +#[derive(Debug)] pub struct LinearMemory { mmap: Mmap, current: u32, @@ -61,13 +61,8 @@ impl LinearMemory { }) } - /// Returns an base address of this linear memory. - pub fn base_addr(&mut self) -> *mut u8 { - self.mmap.as_mut_ptr() - } - - /// Returns a number of allocated wasm pages. - pub fn current_size(&self) -> u32 { + /// Returns the number of allocated wasm pages. + pub fn size(&self) -> u32 { assert_eq!(self.mmap.len() % WASM_PAGE_SIZE as usize, 0); let num_pages = self.mmap.len() / WASM_PAGE_SIZE as usize; cast::u32(num_pages).unwrap() @@ -131,14 +126,9 @@ impl LinearMemory { Some(prev_pages) } -} -impl fmt::Debug for LinearMemory { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LinearMemory") - .field("current", &self.current) - .field("maximum", &self.maximum) - .finish() + pub fn vmmemory(&mut self) -> VMMemory { + VMMemory::new(self.mmap.as_mut_ptr(), self.mmap.len()) } } diff --git a/lib/execute/src/mmap.rs b/lib/execute/src/mmap.rs index 9969452469..6b9fdab071 100644 --- a/lib/execute/src/mmap.rs +++ b/lib/execute/src/mmap.rs @@ -16,6 +16,7 @@ fn round_up_to_page_size(size: usize, page_size: usize) -> usize { /// A simple struct consisting of a page-aligned pointer to page-aligned /// and initially-zeroed memory and a length. +#[derive(Debug)] pub struct Mmap { ptr: *mut u8, len: usize, diff --git a/lib/execute/src/table.rs b/lib/execute/src/table.rs new file mode 100644 index 0000000000..0aef5a4c4c --- /dev/null +++ b/lib/execute/src/table.rs @@ -0,0 +1,65 @@ +//! Memory management for tables. +//! +//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. + +use cranelift_wasm::{self, TableElementType}; +use std::ptr; +use vmcontext::VMTable; + +#[derive(Debug, Clone)] +pub struct AnyFunc { + pub func_ptr: *const u8, + pub type_id: usize, +} + +impl Default for AnyFunc { + fn default() -> Self { + Self { + func_ptr: ptr::null(), + type_id: 0, + } + } +} + +/// A table instance. +#[derive(Debug)] +pub struct Table { + vec: Vec, + maximum: Option, +} + +impl Table { + /// Create a new table instance with specified minimum and maximum number of pages. + pub fn new(table: &cranelift_wasm::Table) -> Self { + match table.ty { + TableElementType::Func => (), + TableElementType::Val(ty) => { + unimplemented!("tables of types other than anyfunc ({})", ty) + } + }; + + let mut vec = Vec::new(); + vec.resize(table.minimum as usize, AnyFunc::default()); + + Self { + vec, + maximum: table.maximum, + } + } + + pub fn vmtable(&mut self) -> VMTable { + VMTable::new(self.vec.as_mut_ptr() as *mut u8, self.vec.len()) + } +} + +impl AsRef<[AnyFunc]> for Table { + fn as_ref(&self) -> &[AnyFunc] { + self.vec.as_slice() + } +} + +impl AsMut<[AnyFunc]> for Table { + fn as_mut(&mut self) -> &mut [AnyFunc] { + self.vec.as_mut_slice() + } +} diff --git a/lib/execute/src/traphandlers.rs b/lib/execute/src/traphandlers.rs index 28f3861e17..405ea6b780 100644 --- a/lib/execute/src/traphandlers.rs +++ b/lib/execute/src/traphandlers.rs @@ -74,10 +74,9 @@ impl ScopeGuard { impl Drop for ScopeGuard { fn drop(&mut self) { let orig_num_bufs = self.orig_num_bufs; - // TODO: Use `shrink_to` once it stablizes. JMP_BUFS.with(|bufs| { bufs.borrow_mut() - .resize(orig_num_bufs, unsafe { mem::uninitialized() }) + .resize(orig_num_bufs, unsafe { mem::zeroed() }) }); } } diff --git a/lib/execute/src/vmcontext.rs b/lib/execute/src/vmcontext.rs index c50a5cec26..f7889c3db9 100644 --- a/lib/execute/src/vmcontext.rs +++ b/lib/execute/src/vmcontext.rs @@ -1,14 +1,19 @@ //! This file declares `VMContext` and several related structs which contain //! fields that JIT code accesses directly. -use std::ptr::{size_of, align_of}; +use cranelift_entity::EntityRef; +use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex}; +use instance::Instance; +use std::mem::size_of; +use std::slice; /// The main fields a JIT needs to access to utilize a WebAssembly linear, /// memory, namely the start address and the size in bytes. -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C)] pub struct VMMemory { - pub base: *mut u8, - pub current_length: usize, + base: *mut u8, + current_length: usize, // If more elements are added here, remember to add offset_of tests below! } @@ -18,14 +23,24 @@ mod test { #[test] fn check_vmmemory_offsets() { - let offsets = VMOffsets::new(size_of<*mut u8>()); - assert_eq!(size_of(), offsets.size_of_vmmemory()); + let offsets = VMOffsets::new(size_of::<*mut u8>()); + assert_eq!(size_of::(), offsets.size_of_vmmemory()); assert_eq!(offset_of!(VMMemory, base), offsets.vmmemory_base()); - assert_eq!(offset_of!(VMMemory, current_length), offsets.vmmemory_current_length()); + assert_eq!( + offset_of!(VMMemory, current_length), + offsets.vmmemory_current_length() + ); } } impl VMMemory { + pub fn new(base: *mut u8, current_length: usize) -> Self { + Self { + base, + current_length, + } + } + pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.base, self.current_length) } } @@ -47,38 +62,50 @@ impl VMMemory { } } -#[repr(C, packed, align(8))] +/// The storage for a WebAssembly global. +/// +/// TODO: Pack the globals more densely, rather than using the same size +/// for every type. +#[derive(Debug, Clone)] +#[repr(C, align(8))] pub struct VMGlobal { - pub storage: [u8; 8], + storage: [u8; 8], // If more elements are added here, remember to add offset_of tests below! } -/// The storage for a WebAssembly global. #[cfg(test)] mod test { + use std::mem::align_of; use wasmtime_environ::VMOffsets; #[test] fn check_vmglobal_alignment() { - assert!(align_of() <= align_of()); - assert!(align_of() >= align_of()); - assert!(align_of() >= align_of()); - assert!(align_of() >= align_of()); + assert!(align_of::() <= align_of::()); + assert!(align_of::() >= align_of::()); + assert!(align_of::() >= align_of::()); + assert!(align_of::() >= align_of::()); } #[test] fn check_vmglobal_offsets() { - let offsets = VMOffsets::new(size_of<*mut u8>()); - assert_eq!(size_of(), offsets.size_of_vmglobal()); + let offsets = VMOffsets::new(size_of::<*mut u8>()); + assert_eq!(size_of::(), offsets.size_of_vmglobal()); } } +impl Default for VMGlobal { + fn default() -> Self { + VMGlobal { storage: [0; 8] } + } +} + +#[derive(Debug)] /// The main fields a JIT needs to access to utilize a WebAssembly table, /// namely the start address and the number of elements. -#[repr(C, packed)] -pub struct VMTableStorage { - pub base: *mut u8, - pub current_elements: usize, +#[repr(C)] +pub struct VMTable { + base: *mut u8, + current_elements: usize, // If more elements are added here, remember to add offset_of tests below! } @@ -88,20 +115,30 @@ mod test { #[test] fn check_vmtable_offsets() { - let offsets = VMOffsets::new(size_of<*mut u8>()); - assert_eq!(size_of(), offsets.size_of_vmtable()); - assert_eq!(offset_of!(VMTableStorage, base), offsets.vmtable_base()); - assert_eq!(offset_of!(VMTableStorage, current_elements), offsets.vmtable_current_elements()); + let offsets = VMOffsets::new(size_of::<*mut u8>()); + assert_eq!(size_of::(), offsets.size_of_vmtable()); + assert_eq!(offset_of!(VMTable, base), offsets.vmtable_base()); + assert_eq!( + offset_of!(VMTable, current_elements), + offsets.vmtable_current_elements() + ); } } -impl VMTableStorage { +impl VMTable { + pub fn new(base: *mut u8, current_elements: usize) -> Self { + Self { + base, + current_elements, + } + } + pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.base, self.current_length) } + unsafe { slice::from_raw_parts(self.base, self.current_elements) } } pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.base, self.current_length) } + unsafe { slice::from_raw_parts_mut(self.base, self.current_elements) } } pub fn as_ptr(&self) -> *const u8 { @@ -113,26 +150,24 @@ impl VMTableStorage { } pub fn len(&self) -> usize { - self.current_length + self.current_elements } } /// The VM "context", which is pointed to by the `vmctx` arg in Cranelift. /// This has pointers to the globals, memories, tables, and other runtime /// state associated with the current instance. -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C)] pub struct VMContext { /// A pointer to an array of `VMMemory` instances, indexed by /// WebAssembly memory index. - pub memories: *mut VMMemory, + memories: *mut VMMemory, /// A pointer to an array of globals. - pub globals: *mut u8, - /// A pointer to an array of `VMTableStorage` instances, indexed by + globals: *mut VMGlobal, + /// A pointer to an array of `VMTable` instances, indexed by /// WebAssembly table index. - pub tables: *mut VMTableStorage, - /// A pointer to extra runtime state that isn't directly accessed - /// from JIT code. - pub instance: *mut u8, + tables: *mut VMTable, // If more elements are added here, remember to add offset_of tests below! } @@ -142,41 +177,63 @@ mod test { #[test] fn check_vmctx_offsets() { - let offsets = VMOffsets::new(size_of<*mut u8>()); - assert_eq!(size_of(), offsets.size_of_vmctx()); - assert_eq!(offset_of!(VMContext, globals), offsets.vmctx_globals()); + let offsets = VMOffsets::new(size_of::<*mut u8>()); + assert_eq!(size_of::(), offsets.size_of_vmctx()); assert_eq!(offset_of!(VMContext, memories), offsets.vmctx_memories()); + assert_eq!(offset_of!(VMContext, globals), offsets.vmctx_globals()); assert_eq!(offset_of!(VMContext, tables), offsets.vmctx_tables()); assert_eq!(offset_of!(VMContext, instance), offsets.vmctx_instance()); } } impl VMContext { - unsafe pub fn global_storage(&mut self, index: usize) -> *mut u8 { - globals.add(index * global_size) + /// Create a new `VMContext` instance. + pub fn new(memories: *mut VMMemory, globals: *mut VMGlobal, tables: *mut VMTable) -> Self { + Self { + memories, + globals, + tables, + } } - unsafe pub fn global_i32(&mut self, index: usize) -> &mut i32 { - self.global_storage(index) as &mut i32 + /// Return the base pointer of the globals array. + pub unsafe fn global_storage(&mut self, index: GlobalIndex) -> *mut VMGlobal { + self.globals.add(index.index() * size_of::()) } - unsafe pub fn global_i64(&mut self, index: usize) -> &mut i64 { - self.global_storage(index) as &mut i64 + /// Return a mutable reference to global `index` which has type i32. + pub unsafe fn global_i32(&mut self, index: GlobalIndex) -> &mut i32 { + &mut *(self.global_storage(index) as *mut i32) } - unsafe pub fn global_f32(&mut self, index: usize) -> &mut f32 { - self.global_storage(index) as &mut f32 + /// Return a mutable reference to global `index` which has type i64. + pub unsafe fn global_i64(&mut self, index: GlobalIndex) -> &mut i64 { + &mut *(self.global_storage(index) as *mut i64) } - unsafe pub fn global_f64(&mut self, index: usize) -> &mut f64 { - self.global_storage(index) as &mut f64 + /// Return a mutable reference to global `index` which has type f32. + pub unsafe fn global_f32(&mut self, index: GlobalIndex) -> &mut f32 { + &mut *(self.global_storage(index) as *mut f32) } - unsafe pub fn memory(&mut self, index: usize) -> &mut VMMemory { - memories.add(index) as &mut VMMemory + /// Return a mutable reference to global `index` which has type f64. + pub unsafe fn global_f64(&mut self, index: GlobalIndex) -> &mut f64 { + &mut *(self.global_storage(index) as *mut f64) } - unsafe pub fn table(&mut self, index: usize) -> &mut VMTableStorage { - tables.add(index) as &mut VMTableStorage + /// Return a mutable reference to linear memory `index`. + pub unsafe fn memory(&mut self, index: MemoryIndex) -> &mut VMMemory { + &mut *self.memories.add(index.index()) + } + + /// Return a mutable reference to table `index`. + pub unsafe fn table(&mut self, index: TableIndex) -> &mut VMTable { + &mut *self.tables.add(index.index()) + } + + /// Return a mutable reference to the associated `Instance`. + pub unsafe fn instance(&mut self) -> &mut Instance { + &mut *((self as *mut VMContext as *mut u8).offset(-Instance::vmctx_offset()) + as *mut Instance) } } diff --git a/src/wasmtime.rs b/src/wasmtime.rs index e7162a1cbb..e3e66445bb 100644 --- a/src/wasmtime.rs +++ b/src/wasmtime.rs @@ -167,7 +167,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri &translation.lazy.data_initializers, )?; - let mut context = finish_instantiation( + finish_instantiation( &mut code, isa, &translation.module, @@ -181,7 +181,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri isa, &translation.module, &compilation, - &mut context, + instance.vmctx(), &f, &[], )?; diff --git a/tests/wast.rs b/tests/wast.rs index e1d6d89240..b050b0fa81 100644 --- a/tests/wast.rs +++ b/tests/wast.rs @@ -19,9 +19,6 @@ use wasmtime_execute::{ struct InstanceWorld { module: Module, - context: Vec<*mut u8>, - // FIXME - #[allow(dead_code)] instance: Instance, compilation: Compilation, } @@ -30,7 +27,7 @@ impl InstanceWorld { fn new(code: &mut Code, isa: &isa::TargetIsa, data: &[u8]) -> Result { let mut module = Module::new(); let tunables = Tunables::default(); - let (context, instance, compilation) = { + let (instance, compilation) = { let translation = { let environ = ModuleEnvironment::new(isa, &mut module, tunables); @@ -46,16 +43,13 @@ impl InstanceWorld { &translation.lazy.data_initializers, )?; - ( - finish_instantiation(code, isa, &translation.module, &compilation, &mut instance)?, - instance, - compilation, - ) + finish_instantiation(code, isa, &translation.module, &compilation, &mut instance)?; + + (instance, compilation) }; Ok(Self { module, - context, instance, compilation, }) @@ -73,7 +67,7 @@ impl InstanceWorld { isa, &self.module, &self.compilation, - &mut self.context, + self.instance.vmctx(), &f, args, ).map_err(|e| e.to_string())