Improve infrastructure.
Do more encapsulation of raw pointers, use more PrimaryMaps instead of Vecs, add a table.rs for managing table storage.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -98,7 +98,7 @@ pub struct Module {
|
||||
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
|
||||
|
||||
/// Names of imported functions.
|
||||
pub imported_funcs: Vec<(String, String)>,
|
||||
pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
|
||||
|
||||
/// Types of functions, imported and local.
|
||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||
@@ -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(),
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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<F>(
|
||||
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<F>(
|
||||
}
|
||||
}
|
||||
},
|
||||
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<F>(
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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<Vec<*mut u8>, 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::<Vec<_>>();
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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<TableIndex, Vec<usize>>,
|
||||
|
||||
/// WebAssembly linear memory data.
|
||||
pub memories: PrimaryMap<MemoryIndex, LinearMemory>,
|
||||
memories: PrimaryMap<MemoryIndex, LinearMemory>,
|
||||
|
||||
/// WebAssembly global variable data.
|
||||
pub globals: Vec<u8>,
|
||||
/// WebAssembly table data.
|
||||
tables: PrimaryMap<TableIndex, Table>,
|
||||
|
||||
/// Memory base address vector pointed to by vmctx.
|
||||
pub mem_base_addrs: Vec<*mut u8>,
|
||||
vmctx_memories: PrimaryMap<MemoryIndex, VMMemory>,
|
||||
|
||||
/// WebAssembly global variable data.
|
||||
vmctx_globals: PrimaryMap<GlobalIndex, VMGlobal>,
|
||||
|
||||
/// Table storage base address vector pointed to by vmctx.
|
||||
vmctx_tables: PrimaryMap<TableIndex, VMTable>,
|
||||
|
||||
/// Context pointer used by JIT code.
|
||||
vmctx: VMContext,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@@ -33,82 +39,68 @@ impl Instance {
|
||||
compilation: &Compilation,
|
||||
data_initializers: &[DataInitializer],
|
||||
) -> Result<Self, String> {
|
||||
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::<PrimaryMap<MemoryIndex, _>>();
|
||||
|
||||
let mut vmctx_globals = instantiate_globals(module);
|
||||
|
||||
let mut vmctx_tables = tables
|
||||
.values_mut()
|
||||
.map(Table::vmtable)
|
||||
.collect::<PrimaryMap<TableIndex, _>>();
|
||||
|
||||
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<u32> {
|
||||
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<PrimaryMap<MemoryIndex, LinearMemory>, 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<TableIndex, Table> {
|
||||
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<GlobalIndex, VMGlobal> {
|
||||
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len());
|
||||
|
||||
for _ in 0..module.globals.len() {
|
||||
vmctx_globals.push(VMGlobal::default());
|
||||
}
|
||||
|
||||
vmctx_globals
|
||||
}
|
||||
|
||||
@@ -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<InvokeOutcome, String> {
|
||||
@@ -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<InvokeOutcome, String> {
|
||||
@@ -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<InvokeOutcome, String> {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
65
lib/execute/src/table.rs
Normal file
65
lib/execute/src/table.rs
Normal file
@@ -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<AnyFunc>,
|
||||
maximum: Option<u32>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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() })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<VMMemory>(), offsets.size_of_vmmemory());
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>());
|
||||
assert_eq!(size_of::<VMMemory>(), 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<VMGlobal>() <= align_of<i32>());
|
||||
assert!(align_of<VMGlobal>() >= align_of<i64>());
|
||||
assert!(align_of<VMGlobal>() >= align_of<f32>());
|
||||
assert!(align_of<VMGlobal>() >= align_of<f64>());
|
||||
assert!(align_of::<VMGlobal>() <= align_of::<i32>());
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<i64>());
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<f32>());
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_offsets() {
|
||||
let offsets = VMOffsets::new(size_of<*mut u8>());
|
||||
assert_eq!(size_of<VMGlobal>(), offsets.size_of_vmglobal());
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>());
|
||||
assert_eq!(size_of::<VMGlobal>(), 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<VMTableStorage>(), 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::<VMTable>(), 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 VMTable {
|
||||
pub fn new(base: *mut u8, current_elements: usize) -> Self {
|
||||
Self {
|
||||
base,
|
||||
current_elements,
|
||||
}
|
||||
}
|
||||
|
||||
impl VMTableStorage {
|
||||
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<VMContext>(), 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::<VMContext>(), 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::<VMGlobal>())
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
&[],
|
||||
)?;
|
||||
|
||||
@@ -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<Self, String> {
|
||||
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())
|
||||
|
||||
Reference in New Issue
Block a user