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:
Dan Gohman
2018-12-03 03:20:15 -08:00
parent fe1643733b
commit bd69768e0d
14 changed files with 360 additions and 253 deletions

View File

@@ -52,10 +52,10 @@ impl binemit::RelocSink for RelocSink {
let reloc_target = if let ExternalName::User { namespace, index } = *name { let reloc_target = if let ExternalName::User { namespace, index } = *name {
debug_assert!(namespace == 0); debug_assert!(namespace == 0);
RelocationTarget::UserFunc(FuncIndex::new(index as usize)) RelocationTarget::UserFunc(FuncIndex::new(index as usize))
} else if *name == ExternalName::testcase("grow_memory") { } else if *name == ExternalName::testcase("wasmtime_memory_grow") {
RelocationTarget::GrowMemory RelocationTarget::MemoryGrow
} else if *name == ExternalName::testcase("current_memory") { } else if *name == ExternalName::testcase("wasmtime_memory_size") {
RelocationTarget::CurrentMemory RelocationTarget::MemorySize
} else { } else {
panic!("unrecognized external name") panic!("unrecognized external name")
}; };
@@ -104,9 +104,9 @@ pub enum RelocationTarget {
/// The user function index. /// The user function index.
UserFunc(FuncIndex), UserFunc(FuncIndex),
/// Function for growing the default memory by the specified amount of pages. /// Function for growing the default memory by the specified amount of pages.
GrowMemory, MemoryGrow,
/// Function for query current size of the default linear memory. /// Function for query current size of the default linear memory.
CurrentMemory, MemorySize,
} }
/// Relocations to apply to function bodies. /// Relocations to apply to function bodies.

View File

@@ -98,7 +98,7 @@ pub struct Module {
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>, pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
/// Names of imported functions. /// Names of imported functions.
pub imported_funcs: Vec<(String, String)>, pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
/// Types of functions, imported and local. /// Types of functions, imported and local.
pub functions: PrimaryMap<FuncIndex, SignatureIndex>, pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
@@ -127,7 +127,7 @@ impl Module {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
signatures: PrimaryMap::new(), signatures: PrimaryMap::new(),
imported_funcs: Vec::new(), imported_funcs: PrimaryMap::new(),
functions: PrimaryMap::new(), functions: PrimaryMap::new(),
tables: PrimaryMap::new(), tables: PrimaryMap::new(),
memory_plans: PrimaryMap::new(), memory_plans: PrimaryMap::new(),

View File

@@ -20,15 +20,13 @@ lazy_static = "1.2.0"
libc = { version = "0.2.44", default-features = false } libc = { version = "0.2.44", default-features = false }
errno = "0.2.4" errno = "0.2.4"
cast = { version = "0.2.2", default-features = false } cast = { version = "0.2.2", default-features = false }
memoffset = "0.2.1"
[build-dependencies] [build-dependencies]
cmake = "0.1.35" cmake = "0.1.35"
bindgen = "0.44.0" bindgen = "0.44.0"
regex = "1.0.6" regex = "1.0.6"
[dev-dependencies]
memoffset = "0.2.1"
[features] [features]
default = ["std"] default = ["std"]
std = ["cranelift-codegen/std", "cranelift-wasm/std"] std = ["cranelift-codegen/std", "cranelift-wasm/std"]

View File

@@ -5,15 +5,15 @@ use code::Code;
use cranelift_codegen::binemit::Reloc; use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::{DefinedFuncIndex, MemoryIndex, TableIndex}; use cranelift_wasm::{DefinedFuncIndex, MemoryIndex};
use instance::Instance; use instance::Instance;
use invoke::{invoke_by_index, InvokeOutcome}; use invoke::{invoke_by_index, InvokeOutcome};
use memory::LinearMemory;
use region::protect; use region::protect;
use region::Protection; use region::Protection;
use std::ptr::{self, write_unaligned}; use std::ptr::write_unaligned;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use vmcontext::VMContext;
use wasmtime_environ::{ use wasmtime_environ::{
compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget, compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget,
}; };
@@ -53,7 +53,7 @@ fn relocate<F>(
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) { RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
Some(f) => compilation.functions[f].as_ptr() as usize, Some(f) => compilation.functions[f].as_ptr() as usize,
None => { None => {
let func = &module.imported_funcs[index.index()]; let func = &module.imported_funcs[index];
match imports(&func.0, &func.1) { match imports(&func.0, &func.1) {
Some(ptr) => ptr, Some(ptr) => ptr,
None => { None => {
@@ -62,8 +62,8 @@ fn relocate<F>(
} }
} }
}, },
RelocationTarget::GrowMemory => grow_memory as usize, RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
RelocationTarget::CurrentMemory => current_memory as usize, RelocationTarget::MemorySize => wasmtime_memory_size as usize,
}; };
let body = &mut compilation.functions[i]; 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 { extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
unsafe { let instance = unsafe { (&mut *vmctx).instance() };
// FIXME: update the VMMemory's size let memory_index = MemoryIndex::new(memory_index as usize);
let instance = (*vmctx.offset(4)) as *mut Instance;
(*instance) instance
.memory_mut(MemoryIndex::new(memory_index as usize)) .memory_grow(memory_index, size)
.grow(size) .unwrap_or(u32::max_value())
.unwrap_or(u32::max_value())
}
} }
extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 { extern "C" fn wasmtime_memory_size(memory_index: u32, vmctx: *mut VMContext) -> u32 {
unsafe { let instance = unsafe { (&mut *vmctx).instance() };
// FIXME: read the VMMemory's size instead let memory_index = MemoryIndex::new(memory_index as usize);
let instance = (*vmctx.offset(4)) as *mut Instance;
(*instance)
.memory_mut(MemoryIndex::new(memory_index as usize))
.current_size()
}
}
/// Create the VmCtx data structure for the JIT'd code to use. This must instance.memory_size(memory_index)
/// 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
} }
/// prepares the execution context /// prepares the execution context
@@ -148,7 +116,7 @@ pub fn finish_instantiation(
module: &Module, module: &Module,
compilation: &Compilation, compilation: &Compilation,
instance: &mut Instance, instance: &mut Instance,
) -> Result<Vec<*mut u8>, String> { ) -> Result<(), String> {
// TODO: Put all the function bodies into a page-aligned memory region, and // TODO: Put all the function bodies into a page-aligned memory region, and
// then make them ReadExecute rather than ReadWriteExecute. // then make them ReadExecute rather than ReadWriteExecute.
for code_buf in compilation.functions.values() { 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 { 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 { match result {
InvokeOutcome::Returned { values } => { InvokeOutcome::Returned { values } => {
assert!(values.is_empty()); assert!(values.is_empty());
@@ -190,5 +150,5 @@ pub fn finish_instantiation(
} }
} }
Ok(vmctx) Ok(())
} }

View File

@@ -1,29 +1,35 @@
//! An `Instance` contains all the runtime state used by execution of a wasm //! An `Instance` contains all the runtime state used by execution of a wasm
//! module. //! module.
use cranelift_codegen::ir;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex}; use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
use memory::LinearMemory; use memory::LinearMemory;
use std::string::String; use std::string::String;
use std::vec::Vec; use table::{AnyFunc, Table};
use wasmtime_environ::{Compilation, DataInitializer, Module, TableElements}; use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
use wasmtime_environ::{Compilation, DataInitializer, Module};
/// An Instance of a WebAssemby module. /// An Instance of a WebAssemby module.
#[derive(Debug)] #[derive(Debug)]
pub struct Instance { pub struct Instance {
/// WebAssembly table data.
pub tables: PrimaryMap<TableIndex, Vec<usize>>,
/// WebAssembly linear memory data. /// WebAssembly linear memory data.
pub memories: PrimaryMap<MemoryIndex, LinearMemory>, memories: PrimaryMap<MemoryIndex, LinearMemory>,
/// WebAssembly global variable data. /// WebAssembly table data.
pub globals: Vec<u8>, tables: PrimaryMap<TableIndex, Table>,
/// Memory base address vector pointed to by vmctx. /// 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 { impl Instance {
@@ -33,82 +39,68 @@ impl Instance {
compilation: &Compilation, compilation: &Compilation,
data_initializers: &[DataInitializer], data_initializers: &[DataInitializer],
) -> Result<Self, String> { ) -> Result<Self, String> {
let mut result = Self { let mut memories = instantiate_memories(module, data_initializers)?;
tables: PrimaryMap::new(), let mut tables = instantiate_tables(module, compilation);
memories: PrimaryMap::new(),
globals: Vec::new(), let mut vmctx_memories = memories
mem_base_addrs: Vec::new(), .values_mut()
}; .map(LinearMemory::vmmemory)
result.instantiate_tables(module, compilation, &module.table_elements); .collect::<PrimaryMap<MemoryIndex, _>>();
result.instantiate_memories(module, data_initializers)?;
result.instantiate_globals(module); let mut vmctx_globals = instantiate_globals(module);
Ok(result)
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. /// Return the vmctx pointer to be passed into JIT code.
fn instantiate_tables( pub fn vmctx(&mut self) -> *mut VMContext {
&mut self, &mut self.vmctx as *mut VMContext
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;
}
}
} }
/// Allocate memory in `instance` for just the memories of the current module. /// Return the offset from the vmctx pointer to its containing Instance.
fn instantiate_memories( pub fn vmctx_offset() -> isize {
&mut self, offset_of!(Instance, vmctx) as isize
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(())
} }
/// Allocate memory in `instance` for just the globals of the current module, /// Grow memory by the specified amount of pages.
/// without any initializers applied yet. ///
fn instantiate_globals(&mut self, module: &Module) { /// Returns `None` if memory can't be grown by the specified amount
debug_assert!(self.globals.is_empty()); /// of pages.
// Allocate the underlying memory and initialize it to all zeros. pub fn memory_grow(&mut self, memory_index: MemoryIndex, delta: u32) -> Option<u32> {
let globals_data_size = module.globals.len() * 8; let result = self
self.globals.resize(globals_data_size, 0); .memories
}
/// 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
.get_mut(memory_index) .get_mut(memory_index)
.unwrap_or_else(|| panic!("no memory for index {}", memory_index.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. /// Returns a slice of the contents of allocated linear memory.
@@ -121,9 +113,64 @@ impl Instance {
} }
/// Shows the value of a global variable. /// Shows the value of a global variable.
pub fn inspect_global(&self, global_index: GlobalIndex, ty: ir::Type) -> &[u8] { pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal {
let offset = global_index.index() * 8; &self.vmctx_globals[global_index]
let len = ty.bytes() as usize;
&self.globals[offset..offset + len]
} }
} }
/// 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
}

View File

@@ -11,6 +11,7 @@ use std::ptr;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use traphandlers::call_wasm; use traphandlers::call_wasm;
use vmcontext::VMContext;
use wasmtime_environ::{Compilation, Export, Module, RelocSink}; use wasmtime_environ::{Compilation, Export, Module, RelocSink};
/// A runtime value. /// A runtime value.
@@ -91,7 +92,7 @@ pub fn invoke(
isa: &isa::TargetIsa, isa: &isa::TargetIsa,
module: &Module, module: &Module,
compilation: &Compilation, compilation: &Compilation,
vmctx: &mut Vec<*mut u8>, vmctx: *mut VMContext,
function: &str, function: &str,
args: &[Value], args: &[Value],
) -> Result<InvokeOutcome, String> { ) -> Result<InvokeOutcome, String> {
@@ -109,7 +110,7 @@ pub fn invoke_by_index(
isa: &isa::TargetIsa, isa: &isa::TargetIsa,
module: &Module, module: &Module,
compilation: &Compilation, compilation: &Compilation,
vmctx: &mut Vec<*mut u8>, vmctx: *mut VMContext,
fn_index: FuncIndex, fn_index: FuncIndex,
args: &[Value], args: &[Value],
) -> Result<InvokeOutcome, String> { ) -> Result<InvokeOutcome, String> {
@@ -138,21 +139,14 @@ pub fn invoke_by_index(
return Err("failed to install signal handlers".to_string()); return Err("failed to install signal handlers".to_string());
} }
call_through_wrapper( call_through_wrapper(code, isa, exec_code_buf as usize, vmctx, args, &sig)
code,
isa,
exec_code_buf as usize,
vmctx.as_ptr() as usize,
args,
&sig,
)
} }
fn call_through_wrapper( fn call_through_wrapper(
code: &mut Code, code: &mut Code,
isa: &isa::TargetIsa, isa: &isa::TargetIsa,
callee: usize, callee: usize,
vmctx: usize, vmctx: *mut VMContext,
args: &[Value], args: &[Value],
sig: &ir::Signature, sig: &ir::Signature,
) -> Result<InvokeOutcome, String> { ) -> Result<InvokeOutcome, String> {

View File

@@ -40,7 +40,6 @@ extern crate alloc;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate libc; extern crate libc;
#[cfg(test)]
#[macro_use] #[macro_use]
extern crate memoffset; extern crate memoffset;
extern crate cast; extern crate cast;
@@ -52,13 +51,16 @@ mod invoke;
mod memory; mod memory;
mod mmap; mod mmap;
mod signalhandlers; mod signalhandlers;
mod table;
mod traphandlers; mod traphandlers;
mod vmcontext;
pub use code::Code; pub use code::Code;
pub use execute::{compile_and_link_module, finish_instantiation}; pub use execute::{compile_and_link_module, finish_instantiation};
pub use instance::Instance; pub use instance::Instance;
pub use invoke::{invoke, InvokeOutcome, Value}; pub use invoke::{invoke, InvokeOutcome, Value};
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind}; pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
pub use vmcontext::VMContext;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
mod std { mod std {

View File

@@ -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 cast;
use mmap::Mmap; use mmap::Mmap;
use region; use region;
use std::fmt;
use std::string::String; use std::string::String;
use vmcontext::VMMemory;
use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE};
/// A linear memory instance. /// A linear memory instance.
/// #[derive(Debug)]
/// This linear memory has a stable base address and at the same time allows
/// for dynamical growing.
pub struct LinearMemory { pub struct LinearMemory {
mmap: Mmap, mmap: Mmap,
current: u32, current: u32,
@@ -61,13 +61,8 @@ impl LinearMemory {
}) })
} }
/// Returns an base address of this linear memory. /// Returns the number of allocated wasm pages.
pub fn base_addr(&mut self) -> *mut u8 { pub fn size(&self) -> u32 {
self.mmap.as_mut_ptr()
}
/// Returns a number of allocated wasm pages.
pub fn current_size(&self) -> u32 {
assert_eq!(self.mmap.len() % WASM_PAGE_SIZE as usize, 0); assert_eq!(self.mmap.len() % WASM_PAGE_SIZE as usize, 0);
let num_pages = self.mmap.len() / WASM_PAGE_SIZE as usize; let num_pages = self.mmap.len() / WASM_PAGE_SIZE as usize;
cast::u32(num_pages).unwrap() cast::u32(num_pages).unwrap()
@@ -131,14 +126,9 @@ impl LinearMemory {
Some(prev_pages) Some(prev_pages)
} }
}
impl fmt::Debug for LinearMemory { pub fn vmmemory(&mut self) -> VMMemory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { VMMemory::new(self.mmap.as_mut_ptr(), self.mmap.len())
f.debug_struct("LinearMemory")
.field("current", &self.current)
.field("maximum", &self.maximum)
.finish()
} }
} }

View File

@@ -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 /// A simple struct consisting of a page-aligned pointer to page-aligned
/// and initially-zeroed memory and a length. /// and initially-zeroed memory and a length.
#[derive(Debug)]
pub struct Mmap { pub struct Mmap {
ptr: *mut u8, ptr: *mut u8,
len: usize, len: usize,

65
lib/execute/src/table.rs Normal file
View 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()
}
}

View File

@@ -74,10 +74,9 @@ impl ScopeGuard {
impl Drop for ScopeGuard { impl Drop for ScopeGuard {
fn drop(&mut self) { fn drop(&mut self) {
let orig_num_bufs = self.orig_num_bufs; let orig_num_bufs = self.orig_num_bufs;
// TODO: Use `shrink_to` once it stablizes.
JMP_BUFS.with(|bufs| { JMP_BUFS.with(|bufs| {
bufs.borrow_mut() bufs.borrow_mut()
.resize(orig_num_bufs, unsafe { mem::uninitialized() }) .resize(orig_num_bufs, unsafe { mem::zeroed() })
}); });
} }
} }

View File

@@ -1,14 +1,19 @@
//! This file declares `VMContext` and several related structs which contain //! This file declares `VMContext` and several related structs which contain
//! fields that JIT code accesses directly. //! 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, /// The main fields a JIT needs to access to utilize a WebAssembly linear,
/// memory, namely the start address and the size in bytes. /// memory, namely the start address and the size in bytes.
#[repr(C, packed)] #[derive(Debug)]
#[repr(C)]
pub struct VMMemory { pub struct VMMemory {
pub base: *mut u8, base: *mut u8,
pub current_length: usize, current_length: usize,
// If more elements are added here, remember to add offset_of tests below! // If more elements are added here, remember to add offset_of tests below!
} }
@@ -18,14 +23,24 @@ mod test {
#[test] #[test]
fn check_vmmemory_offsets() { fn check_vmmemory_offsets() {
let offsets = VMOffsets::new(size_of<*mut u8>()); let offsets = VMOffsets::new(size_of::<*mut u8>());
assert_eq!(size_of<VMMemory>(), offsets.size_of_vmmemory()); assert_eq!(size_of::<VMMemory>(), offsets.size_of_vmmemory());
assert_eq!(offset_of!(VMMemory, base), offsets.vmmemory_base()); 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 { impl VMMemory {
pub fn new(base: *mut u8, current_length: usize) -> Self {
Self {
base,
current_length,
}
}
pub fn as_slice(&self) -> &[u8] { 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_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 struct VMGlobal {
pub storage: [u8; 8], storage: [u8; 8],
// If more elements are added here, remember to add offset_of tests below! // If more elements are added here, remember to add offset_of tests below!
} }
/// The storage for a WebAssembly global.
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::mem::align_of;
use wasmtime_environ::VMOffsets; use wasmtime_environ::VMOffsets;
#[test] #[test]
fn check_vmglobal_alignment() { fn check_vmglobal_alignment() {
assert!(align_of<VMGlobal>() <= align_of<i32>()); assert!(align_of::<VMGlobal>() <= align_of::<i32>());
assert!(align_of<VMGlobal>() >= align_of<i64>()); assert!(align_of::<VMGlobal>() >= align_of::<i64>());
assert!(align_of<VMGlobal>() >= align_of<f32>()); assert!(align_of::<VMGlobal>() >= align_of::<f32>());
assert!(align_of<VMGlobal>() >= align_of<f64>()); assert!(align_of::<VMGlobal>() >= align_of::<f64>());
} }
#[test] #[test]
fn check_vmglobal_offsets() { fn check_vmglobal_offsets() {
let offsets = VMOffsets::new(size_of<*mut u8>()); let offsets = VMOffsets::new(size_of::<*mut u8>());
assert_eq!(size_of<VMGlobal>(), offsets.size_of_vmglobal()); 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, /// The main fields a JIT needs to access to utilize a WebAssembly table,
/// namely the start address and the number of elements. /// namely the start address and the number of elements.
#[repr(C, packed)] #[repr(C)]
pub struct VMTableStorage { pub struct VMTable {
pub base: *mut u8, base: *mut u8,
pub current_elements: usize, current_elements: usize,
// If more elements are added here, remember to add offset_of tests below! // If more elements are added here, remember to add offset_of tests below!
} }
@@ -88,20 +115,30 @@ mod test {
#[test] #[test]
fn check_vmtable_offsets() { fn check_vmtable_offsets() {
let offsets = VMOffsets::new(size_of<*mut u8>()); let offsets = VMOffsets::new(size_of::<*mut u8>());
assert_eq!(size_of<VMTableStorage>(), offsets.size_of_vmtable()); assert_eq!(size_of::<VMTable>(), offsets.size_of_vmtable());
assert_eq!(offset_of!(VMTableStorage, base), offsets.vmtable_base()); assert_eq!(offset_of!(VMTable, base), offsets.vmtable_base());
assert_eq!(offset_of!(VMTableStorage, current_elements), offsets.vmtable_current_elements()); 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] { 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] { 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 { pub fn as_ptr(&self) -> *const u8 {
@@ -113,26 +150,24 @@ impl VMTableStorage {
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.current_length self.current_elements
} }
} }
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift. /// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
/// This has pointers to the globals, memories, tables, and other runtime /// This has pointers to the globals, memories, tables, and other runtime
/// state associated with the current instance. /// state associated with the current instance.
#[repr(C, packed)] #[derive(Debug)]
#[repr(C)]
pub struct VMContext { pub struct VMContext {
/// A pointer to an array of `VMMemory` instances, indexed by /// A pointer to an array of `VMMemory` instances, indexed by
/// WebAssembly memory index. /// WebAssembly memory index.
pub memories: *mut VMMemory, memories: *mut VMMemory,
/// A pointer to an array of globals. /// A pointer to an array of globals.
pub globals: *mut u8, globals: *mut VMGlobal,
/// A pointer to an array of `VMTableStorage` instances, indexed by /// A pointer to an array of `VMTable` instances, indexed by
/// WebAssembly table index. /// WebAssembly table index.
pub tables: *mut VMTableStorage, tables: *mut VMTable,
/// A pointer to extra runtime state that isn't directly accessed
/// from JIT code.
pub instance: *mut u8,
// If more elements are added here, remember to add offset_of tests below! // If more elements are added here, remember to add offset_of tests below!
} }
@@ -142,41 +177,63 @@ mod test {
#[test] #[test]
fn check_vmctx_offsets() { fn check_vmctx_offsets() {
let offsets = VMOffsets::new(size_of<*mut u8>()); let offsets = VMOffsets::new(size_of::<*mut u8>());
assert_eq!(size_of<VMContext>(), offsets.size_of_vmctx()); assert_eq!(size_of::<VMContext>(), offsets.size_of_vmctx());
assert_eq!(offset_of!(VMContext, globals), offsets.vmctx_globals());
assert_eq!(offset_of!(VMContext, memories), offsets.vmctx_memories()); 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, tables), offsets.vmctx_tables());
assert_eq!(offset_of!(VMContext, instance), offsets.vmctx_instance()); assert_eq!(offset_of!(VMContext, instance), offsets.vmctx_instance());
} }
} }
impl VMContext { impl VMContext {
unsafe pub fn global_storage(&mut self, index: usize) -> *mut u8 { /// Create a new `VMContext` instance.
globals.add(index * global_size) 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 { /// Return the base pointer of the globals array.
self.global_storage(index) as &mut i32 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 { /// Return a mutable reference to global `index` which has type i32.
self.global_storage(index) as &mut i64 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 { /// Return a mutable reference to global `index` which has type i64.
self.global_storage(index) as &mut f32 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 { /// Return a mutable reference to global `index` which has type f32.
self.global_storage(index) as &mut f64 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 { /// Return a mutable reference to global `index` which has type f64.
memories.add(index) as &mut VMMemory 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 { /// Return a mutable reference to linear memory `index`.
tables.add(index) as &mut VMTableStorage 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)
} }
} }

View File

@@ -167,7 +167,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
&translation.lazy.data_initializers, &translation.lazy.data_initializers,
)?; )?;
let mut context = finish_instantiation( finish_instantiation(
&mut code, &mut code,
isa, isa,
&translation.module, &translation.module,
@@ -181,7 +181,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
isa, isa,
&translation.module, &translation.module,
&compilation, &compilation,
&mut context, instance.vmctx(),
&f, &f,
&[], &[],
)?; )?;

View File

@@ -19,9 +19,6 @@ use wasmtime_execute::{
struct InstanceWorld { struct InstanceWorld {
module: Module, module: Module,
context: Vec<*mut u8>,
// FIXME
#[allow(dead_code)]
instance: Instance, instance: Instance,
compilation: Compilation, compilation: Compilation,
} }
@@ -30,7 +27,7 @@ impl InstanceWorld {
fn new(code: &mut Code, isa: &isa::TargetIsa, data: &[u8]) -> Result<Self, String> { fn new(code: &mut Code, isa: &isa::TargetIsa, data: &[u8]) -> Result<Self, String> {
let mut module = Module::new(); let mut module = Module::new();
let tunables = Tunables::default(); let tunables = Tunables::default();
let (context, instance, compilation) = { let (instance, compilation) = {
let translation = { let translation = {
let environ = ModuleEnvironment::new(isa, &mut module, tunables); let environ = ModuleEnvironment::new(isa, &mut module, tunables);
@@ -46,16 +43,13 @@ impl InstanceWorld {
&translation.lazy.data_initializers, &translation.lazy.data_initializers,
)?; )?;
( finish_instantiation(code, isa, &translation.module, &compilation, &mut instance)?;
finish_instantiation(code, isa, &translation.module, &compilation, &mut instance)?,
instance, (instance, compilation)
compilation,
)
}; };
Ok(Self { Ok(Self {
module, module,
context,
instance, instance,
compilation, compilation,
}) })
@@ -73,7 +67,7 @@ impl InstanceWorld {
isa, isa,
&self.module, &self.module,
&self.compilation, &self.compilation,
&mut self.context, self.instance.vmctx(),
&f, &f,
args, args,
).map_err(|e| e.to_string()) ).map_err(|e| e.to_string())