Support imports.
This commit is contained in:
@@ -5,66 +5,282 @@ use code::Code;
|
||||
use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||
use cranelift_wasm::{DefinedFuncIndex, MemoryIndex};
|
||||
use cranelift_wasm::{
|
||||
DefinedFuncIndex, Global, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
||||
};
|
||||
use export::{ExportValue, Resolver};
|
||||
use instance::Instance;
|
||||
use invoke::{invoke_by_index, InvokeOutcome};
|
||||
use region::protect;
|
||||
use region::Protection;
|
||||
use region::{protect, Protection};
|
||||
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,
|
||||
compile_module, Compilation, MemoryPlan, MemoryStyle, Module, ModuleTranslation, Relocation,
|
||||
RelocationTarget, TablePlan, TableStyle,
|
||||
};
|
||||
|
||||
/// Executes a module that has been translated with the `wasmtime-environ` environment
|
||||
/// implementation.
|
||||
pub fn compile_and_link_module<'data, 'module, F>(
|
||||
pub fn compile_and_link_module<'data, 'module>(
|
||||
isa: &TargetIsa,
|
||||
translation: &ModuleTranslation<'data, 'module>,
|
||||
imports: F,
|
||||
) -> Result<Compilation, String>
|
||||
where
|
||||
F: Fn(&str, &str) -> Option<usize>,
|
||||
{
|
||||
resolver: &mut Resolver,
|
||||
) -> Result<Compilation, String> {
|
||||
let (mut compilation, relocations) = compile_module(&translation, isa)?;
|
||||
|
||||
for (index, (ref module, ref field)) in translation.module.imported_funcs.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Function { address, signature } => {
|
||||
let import_signature =
|
||||
&translation.module.signatures[translation.module.functions[index]];
|
||||
if signature != *import_signature {
|
||||
return Err(format!(
|
||||
"{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
||||
module, field,
|
||||
signature, import_signature,
|
||||
));
|
||||
}
|
||||
compilation.resolved_func_imports.push(address);
|
||||
}
|
||||
ExportValue::Table { .. }
|
||||
| ExportValue::Memory { .. }
|
||||
| ExportValue::Global { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: export not compatible with function import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => return Err(format!("{}/{}: no provided import function", module, field)),
|
||||
}
|
||||
}
|
||||
for (index, (ref module, ref field)) in translation.module.imported_globals.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Global { address, global } => {
|
||||
let imported_global = translation.module.globals[index];
|
||||
if !is_global_compatible(&global, &imported_global) {
|
||||
return Err(format!(
|
||||
"{}/{}: exported global incompatible with global import",
|
||||
module, field,
|
||||
));
|
||||
}
|
||||
compilation.resolved_global_imports.push(address as usize);
|
||||
}
|
||||
ExportValue::Table { .. }
|
||||
| ExportValue::Memory { .. }
|
||||
| ExportValue::Function { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: exported global incompatible with global import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(format!(
|
||||
"no provided import global for {}/{}",
|
||||
module, field
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index, (ref module, ref field)) in translation.module.imported_tables.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Table { address, table } => {
|
||||
let import_table = &translation.module.table_plans[index];
|
||||
if !is_table_compatible(&table, import_table) {
|
||||
return Err(format!(
|
||||
"{}/{}: exported table incompatible with table import",
|
||||
module, field,
|
||||
));
|
||||
}
|
||||
compilation.resolved_table_imports.push(address as usize);
|
||||
}
|
||||
ExportValue::Global { .. }
|
||||
| ExportValue::Memory { .. }
|
||||
| ExportValue::Function { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: export not compatible with table import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => return Err(format!("no provided import table for {}/{}", module, field)),
|
||||
}
|
||||
}
|
||||
for (index, (ref module, ref field)) in translation.module.imported_memories.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Memory { address, memory } => {
|
||||
let import_memory = &translation.module.memory_plans[index];
|
||||
if is_memory_compatible(&memory, import_memory) {
|
||||
return Err(format!(
|
||||
"{}/{}: exported memory incompatible with memory import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
compilation.resolved_memory_imports.push(address as usize);
|
||||
}
|
||||
ExportValue::Table { .. }
|
||||
| ExportValue::Global { .. }
|
||||
| ExportValue::Function { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: export not compatible with memory import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(format!(
|
||||
"no provided import memory for {}/{}",
|
||||
module, field
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply relocations, now that we have virtual addresses for everything.
|
||||
relocate(&mut compilation, &relocations, &translation.module, imports);
|
||||
relocate(&mut compilation, &relocations, &translation.module)?;
|
||||
|
||||
Ok(compilation)
|
||||
}
|
||||
|
||||
fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
||||
match imported.initializer {
|
||||
GlobalInit::Import => (),
|
||||
_ => panic!("imported Global should have an Imported initializer"),
|
||||
}
|
||||
|
||||
let Global {
|
||||
ty: exported_ty,
|
||||
mutability: exported_mutability,
|
||||
initializer: _exported_initializer,
|
||||
} = exported;
|
||||
let Global {
|
||||
ty: imported_ty,
|
||||
mutability: imported_mutability,
|
||||
initializer: _imported_initializer,
|
||||
} = imported;
|
||||
exported_ty == imported_ty && imported_mutability == exported_mutability
|
||||
}
|
||||
|
||||
fn is_table_style_compatible(exported_style: &TableStyle, imported_style: &TableStyle) -> bool {
|
||||
match exported_style {
|
||||
TableStyle::CallerChecksSignature => match imported_style {
|
||||
TableStyle::CallerChecksSignature => true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_table_element_type_compatible(
|
||||
exported_type: TableElementType,
|
||||
imported_type: TableElementType,
|
||||
) -> bool {
|
||||
match exported_type {
|
||||
TableElementType::Func => match imported_type {
|
||||
TableElementType::Func => true,
|
||||
_ => false,
|
||||
},
|
||||
TableElementType::Val(exported_val_ty) => match imported_type {
|
||||
TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool {
|
||||
let TablePlan {
|
||||
table:
|
||||
Table {
|
||||
ty: exported_ty,
|
||||
minimum: exported_minimum,
|
||||
maximum: exported_maximum,
|
||||
},
|
||||
style: exported_style,
|
||||
} = exported;
|
||||
let TablePlan {
|
||||
table:
|
||||
Table {
|
||||
ty: imported_ty,
|
||||
minimum: imported_minimum,
|
||||
maximum: imported_maximum,
|
||||
},
|
||||
style: imported_style,
|
||||
} = imported;
|
||||
|
||||
is_table_element_type_compatible(*exported_ty, *imported_ty)
|
||||
&& imported_minimum >= exported_minimum
|
||||
&& imported_maximum <= exported_maximum
|
||||
&& is_table_style_compatible(imported_style, exported_style)
|
||||
}
|
||||
|
||||
fn is_memory_style_compatible(exported_style: &MemoryStyle, imported_style: &MemoryStyle) -> bool {
|
||||
match exported_style {
|
||||
MemoryStyle::Dynamic => match imported_style {
|
||||
MemoryStyle::Dynamic => true,
|
||||
_ => false,
|
||||
},
|
||||
MemoryStyle::Static {
|
||||
bound: imported_bound,
|
||||
} => match imported_style {
|
||||
MemoryStyle::Static {
|
||||
bound: exported_bound,
|
||||
} => exported_bound >= imported_bound,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
|
||||
let MemoryPlan {
|
||||
memory:
|
||||
Memory {
|
||||
minimum: exported_minimum,
|
||||
maximum: exported_maximum,
|
||||
shared: exported_shared,
|
||||
},
|
||||
style: exported_style,
|
||||
offset_guard_size: exported_offset_guard_size,
|
||||
} = exported;
|
||||
let MemoryPlan {
|
||||
memory:
|
||||
Memory {
|
||||
minimum: imported_minimum,
|
||||
maximum: imported_maximum,
|
||||
shared: imported_shared,
|
||||
},
|
||||
style: imported_style,
|
||||
offset_guard_size: imported_offset_guard_size,
|
||||
} = imported;
|
||||
|
||||
imported_minimum >= exported_minimum
|
||||
&& imported_maximum <= exported_maximum
|
||||
&& exported_shared == imported_shared
|
||||
&& is_memory_style_compatible(exported_style, imported_style)
|
||||
&& exported_offset_guard_size >= imported_offset_guard_size
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn __rust_probestack();
|
||||
}
|
||||
|
||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata
|
||||
fn relocate<F>(
|
||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
||||
fn relocate(
|
||||
compilation: &mut Compilation,
|
||||
relocations: &PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||
module: &Module,
|
||||
imports: F,
|
||||
) where
|
||||
F: Fn(&str, &str) -> Option<usize>,
|
||||
{
|
||||
// The relocations are relative to the relocation's address plus four bytes
|
||||
// TODO: Support architectures other than x64, and other reloc kinds.
|
||||
) -> Result<(), String> {
|
||||
// The relocations are relative to the relocation's address plus four bytes.
|
||||
for (i, function_relocs) in relocations.iter() {
|
||||
for r in function_relocs {
|
||||
let target_func_address: usize = match r.reloc_target {
|
||||
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];
|
||||
match imports(&func.0, &func.1) {
|
||||
Some(ptr) => ptr,
|
||||
None => {
|
||||
panic!("no provided import function for {}/{}", &func.0, &func.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => compilation.resolved_func_imports[index],
|
||||
},
|
||||
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
||||
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
||||
@@ -111,6 +327,7 @@ fn relocate<F>(
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
||||
|
||||
76
lib/execute/src/export.rs
Normal file
76
lib/execute/src/export.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_wasm::Global;
|
||||
use vmcontext::{VMGlobal, VMMemory, VMTable};
|
||||
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||
|
||||
/// The value of an export passed from one instance to another.
|
||||
pub enum ExportValue {
|
||||
/// A function export value.
|
||||
Function {
|
||||
/// The address of the native-code function.
|
||||
address: usize,
|
||||
/// The function signature declaration, used for compatibilty checking.
|
||||
signature: ir::Signature,
|
||||
},
|
||||
|
||||
/// A table export value.
|
||||
Table {
|
||||
/// The address of the table descriptor.
|
||||
address: *mut VMTable,
|
||||
/// The table declaration, used for compatibilty checking.
|
||||
table: TablePlan,
|
||||
},
|
||||
|
||||
/// A memory export value.
|
||||
Memory {
|
||||
/// The address of the memory descriptor.
|
||||
address: *mut VMMemory,
|
||||
/// The memory declaration, used for compatibilty checking.
|
||||
memory: MemoryPlan,
|
||||
},
|
||||
|
||||
/// A global export value.
|
||||
Global {
|
||||
/// The address of the global storage.
|
||||
address: *mut VMGlobal,
|
||||
/// The global declaration, used for compatibilty checking.
|
||||
global: Global,
|
||||
},
|
||||
}
|
||||
|
||||
impl ExportValue {
|
||||
/// Construct a function export value.
|
||||
pub fn function(address: usize, signature: ir::Signature) -> Self {
|
||||
ExportValue::Function { address, signature }
|
||||
}
|
||||
|
||||
/// Construct a table export value.
|
||||
pub fn table(address: *mut VMTable, table: TablePlan) -> Self {
|
||||
ExportValue::Table { address, table }
|
||||
}
|
||||
|
||||
/// Construct a memory export value.
|
||||
pub fn memory(address: *mut VMMemory, memory: MemoryPlan) -> Self {
|
||||
ExportValue::Memory { address, memory }
|
||||
}
|
||||
|
||||
/// Construct a global export value.
|
||||
pub fn global(address: *mut VMGlobal, global: Global) -> Self {
|
||||
ExportValue::Global { address, global }
|
||||
}
|
||||
}
|
||||
|
||||
/// Import resolver connects imports with available exported values.
|
||||
pub trait Resolver {
|
||||
/// Resolve the given module/field combo.
|
||||
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue>;
|
||||
}
|
||||
|
||||
/// `Resolver` implementation that always resolves to `None`.
|
||||
pub struct NullResolver {}
|
||||
|
||||
impl Resolver for NullResolver {
|
||||
fn resolve(&mut self, _module: &str, _field: &str) -> Option<ExportValue> {
|
||||
None
|
||||
}
|
||||
}
|
||||
39
lib/execute/src/get.rs
Normal file
39
lib/execute/src/get.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
//! Support for reading the value of a wasm global from outside the module.
|
||||
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_wasm::GlobalIndex;
|
||||
use invoke::Value;
|
||||
use std::string::String;
|
||||
use vmcontext::VMContext;
|
||||
use wasmtime_environ::{Export, Module};
|
||||
|
||||
/// Jumps to the code region of memory and invoke the exported function
|
||||
pub fn get(module: &Module, vmctx: *mut VMContext, global_name: &str) -> Result<Value, String> {
|
||||
let global_index = match module.exports.get(global_name) {
|
||||
Some(Export::Global(index)) => *index,
|
||||
Some(_) => return Err(format!("exported item \"{}\" is not a global", global_name)),
|
||||
None => return Err(format!("no export named \"{}\"", global_name)),
|
||||
};
|
||||
|
||||
get_by_index(module, vmctx, global_index)
|
||||
}
|
||||
|
||||
pub fn get_by_index(
|
||||
module: &Module,
|
||||
vmctx: *mut VMContext,
|
||||
global_index: GlobalIndex,
|
||||
) -> Result<Value, String> {
|
||||
// TODO: Return Err if the index is out of bounds.
|
||||
unsafe {
|
||||
let vmctx = &mut *vmctx;
|
||||
let vmglobal = vmctx.global(global_index);
|
||||
let definition = vmglobal.get_definition(module.is_imported_global(global_index));
|
||||
Ok(match module.globals[global_index].ty {
|
||||
ir::types::I32 => Value::I32(*definition.as_i32()),
|
||||
ir::types::I64 => Value::I64(*definition.as_i64()),
|
||||
ir::types::F32 => Value::F32(*definition.as_f32_bits()),
|
||||
ir::types::F64 => Value::F64(*definition.as_f64_bits()),
|
||||
other => return Err(format!("global with type {} not supported", other)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||
use memory::LinearMemory;
|
||||
use sig_registry::SignatureRegistry;
|
||||
use std::ptr;
|
||||
use std::string::String;
|
||||
use table::Table;
|
||||
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
||||
@@ -44,7 +45,7 @@ impl Instance {
|
||||
compilation: &Compilation,
|
||||
data_initializers: &[DataInitializer],
|
||||
) -> Result<Self, String> {
|
||||
let mut sig_registry = SignatureRegistry::new();
|
||||
let mut sig_registry = instantiate_signatures(module);
|
||||
let mut memories = instantiate_memories(module, data_initializers)?;
|
||||
let mut tables = instantiate_tables(module, compilation, &mut sig_registry);
|
||||
|
||||
@@ -131,6 +132,14 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate_signatures(module: &Module) -> SignatureRegistry {
|
||||
let mut sig_registry = SignatureRegistry::new();
|
||||
for (sig_index, sig) in module.signatures.iter() {
|
||||
sig_registry.register(sig_index, sig);
|
||||
}
|
||||
sig_registry
|
||||
}
|
||||
|
||||
/// Allocate memory for just the memories of the current module.
|
||||
fn instantiate_memories(
|
||||
module: &Module,
|
||||
@@ -171,7 +180,7 @@ fn instantiate_tables(
|
||||
let code_buf = &compilation.functions[module
|
||||
.defined_func_index(*func_idx)
|
||||
.expect("table element initializer with imported function not supported yet")];
|
||||
let type_id = sig_registry.register(callee_sig, &module.signatures[callee_sig]);
|
||||
let type_id = sig_registry.lookup(callee_sig);
|
||||
subslice[i] = VMCallerCheckedAnyfunc {
|
||||
func_ptr: code_buf.as_ptr(),
|
||||
type_id,
|
||||
@@ -187,8 +196,13 @@ fn instantiate_tables(
|
||||
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());
|
||||
for (index, global) in module.globals.iter() {
|
||||
if module.is_imported_global(index) {
|
||||
// FIXME: get the actual import
|
||||
vmctx_globals.push(VMGlobal::import(ptr::null_mut()));
|
||||
} else {
|
||||
vmctx_globals.push(VMGlobal::definition(global));
|
||||
}
|
||||
}
|
||||
|
||||
vmctx_globals
|
||||
|
||||
@@ -114,12 +114,16 @@ pub fn invoke_by_index(
|
||||
fn_index: FuncIndex,
|
||||
args: &[Value],
|
||||
) -> Result<InvokeOutcome, String> {
|
||||
let code_buf = &compilation.functions[module
|
||||
.defined_func_index(fn_index)
|
||||
.expect("imported start functions not supported yet")];
|
||||
let sig = &module.signatures[module.functions[fn_index]];
|
||||
// TODO: Return Err if fn_index is out of bounds.
|
||||
let exec_code_buf = match module.defined_func_index(fn_index) {
|
||||
Some(def_fn_index) => {
|
||||
let code_buf = &compilation.functions[def_fn_index];
|
||||
code.allocate_copy_of_slice(&code_buf)?.as_ptr() as usize
|
||||
}
|
||||
None => compilation.resolved_func_imports[fn_index],
|
||||
};
|
||||
|
||||
let exec_code_buf = code.allocate_copy_of_slice(&code_buf)?.as_ptr();
|
||||
let sig = &module.signatures[module.functions[fn_index]];
|
||||
|
||||
// TODO: Move this out to be done once per thread rather than per call.
|
||||
let mut traps = TrapContext {
|
||||
@@ -138,7 +142,7 @@ 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, args, &sig)
|
||||
call_through_wrapper(code, isa, exec_code_buf, vmctx, args, &sig)
|
||||
}
|
||||
|
||||
fn call_through_wrapper(
|
||||
|
||||
@@ -43,6 +43,8 @@ extern crate cast;
|
||||
|
||||
mod code;
|
||||
mod execute;
|
||||
mod export;
|
||||
mod get;
|
||||
mod instance;
|
||||
mod invoke;
|
||||
mod libcalls;
|
||||
@@ -57,10 +59,12 @@ mod world;
|
||||
|
||||
pub use code::Code;
|
||||
pub use execute::{compile_and_link_module, finish_instantiation};
|
||||
pub use export::{ExportValue, NullResolver, Resolver};
|
||||
pub use get::get;
|
||||
pub use instance::Instance;
|
||||
pub use invoke::{invoke, InvokeOutcome, Value};
|
||||
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
||||
pub use vmcontext::VMContext;
|
||||
pub use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
|
||||
pub use world::InstanceWorld;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
|
||||
@@ -124,8 +124,9 @@ impl LinearMemory {
|
||||
Some(prev_pages)
|
||||
}
|
||||
|
||||
/// Return a `VMMemory` for exposing the memory to JIT code.
|
||||
pub fn vmmemory(&mut self) -> VMMemory {
|
||||
VMMemory::new(self.mmap.as_mut_ptr(), self.mmap.len())
|
||||
VMMemory::definition(self.mmap.as_mut_ptr(), self.mmap.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use cast;
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_entity::SecondaryMap;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::SignatureIndex;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use vmcontext::VMSignatureId;
|
||||
@@ -11,14 +11,14 @@ use vmcontext::VMSignatureId;
|
||||
#[derive(Debug)]
|
||||
pub struct SignatureRegistry {
|
||||
signature_hash: HashMap<ir::Signature, VMSignatureId>,
|
||||
signature_ids: SecondaryMap<SignatureIndex, VMSignatureId>,
|
||||
signature_ids: PrimaryMap<SignatureIndex, VMSignatureId>,
|
||||
}
|
||||
|
||||
impl SignatureRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
signature_hash: HashMap::new(),
|
||||
signature_ids: SecondaryMap::new(),
|
||||
signature_ids: PrimaryMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,12 @@ impl SignatureRegistry {
|
||||
}
|
||||
|
||||
/// Register the given signature.
|
||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) -> VMSignatureId {
|
||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) {
|
||||
// TODO: Refactor this interface so that we're not passing in redundant
|
||||
// information.
|
||||
debug_assert_eq!(sig_index.index(), self.signature_ids.len());
|
||||
use cranelift_entity::EntityRef;
|
||||
|
||||
let len = self.signature_hash.len();
|
||||
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||
@@ -37,7 +42,11 @@ impl SignatureRegistry {
|
||||
sig_id
|
||||
}
|
||||
};
|
||||
self.signature_ids[sig_index] = sig_id;
|
||||
sig_id
|
||||
self.signature_ids.push(sig_id);
|
||||
}
|
||||
|
||||
/// Return the identifying runtime index for the given signature.
|
||||
pub fn lookup(&mut self, sig_index: SignatureIndex) -> VMSignatureId {
|
||||
self.signature_ids[sig_index]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,9 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `VMTable` for exposing the table to JIT code.
|
||||
pub fn vmtable(&mut self) -> VMTable {
|
||||
VMTable::new(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
||||
VMTable::definition(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,86 @@
|
||||
//! fields that JIT code accesses directly.
|
||||
|
||||
use cranelift_entity::EntityRef;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||
use cranelift_wasm::{Global, GlobalIndex, GlobalInit, MemoryIndex, TableIndex};
|
||||
use instance::Instance;
|
||||
use std::mem::size_of;
|
||||
use std::fmt;
|
||||
use std::ptr;
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||
/// memory defined within the instance, namely the start address and the
|
||||
/// size in bytes.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMMemory {
|
||||
pub struct VMMemoryDefinition {
|
||||
/// The start address.
|
||||
base: *mut u8,
|
||||
/// The current size of linear memory in bytes.
|
||||
current_length: usize,
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmmemory_definition {
|
||||
use super::VMMemoryDefinition;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmmemory_definition_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMMemoryDefinition>(),
|
||||
usize::from(offsets.size_of_vmmemory_definition())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemoryDefinition, base),
|
||||
usize::from(offsets.vmmemory_definition_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemoryDefinition, current_length),
|
||||
usize::from(offsets.vmmemory_definition_current_length())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||
/// memory imported from another instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMMemoryImport {
|
||||
/// A pointer to the imported memory description.
|
||||
from: *mut VMMemoryDefinition,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmmemory_import {
|
||||
use super::VMMemoryImport;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmmemory_import_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMMemoryImport>(),
|
||||
usize::from(offsets.size_of_vmmemory_import())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemoryImport, from),
|
||||
usize::from(offsets.vmmemory_import_from())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly linear
|
||||
/// memory. It must know whether the memory is defined within the instance
|
||||
/// or imported.
|
||||
#[repr(C)]
|
||||
pub union VMMemory {
|
||||
/// A linear memory defined within the instance.
|
||||
definition: VMMemoryDefinition,
|
||||
|
||||
/// An imported linear memory.
|
||||
import: VMMemoryImport,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -31,71 +97,155 @@ mod test_vmmemory {
|
||||
size_of::<VMMemory>(),
|
||||
usize::from(offsets.size_of_vmmemory())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemory, base),
|
||||
usize::from(offsets.vmmemory_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemory, current_length),
|
||||
usize::from(offsets.vmmemory_current_length())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMMemory {
|
||||
pub fn new(base: *mut u8, current_length: usize) -> Self {
|
||||
/// Construct a `VMMemoryDefinition` variant of `VMMemory`.
|
||||
pub fn definition(base: *mut u8, current_length: usize) -> Self {
|
||||
Self {
|
||||
base,
|
||||
current_length,
|
||||
definition: VMMemoryDefinition {
|
||||
base,
|
||||
current_length,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.base, self.current_length) }
|
||||
/// Construct a `VMMemoryImmport` variant of `VMMemory`.
|
||||
pub fn import(from: *mut VMMemoryDefinition) -> Self {
|
||||
Self {
|
||||
import: VMMemoryImport { from },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.base, self.current_length) }
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.current_length
|
||||
/// Get the underlying `VMMemoryDefinition`.
|
||||
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMMemoryDefinition {
|
||||
if is_import {
|
||||
&mut *self.import.from
|
||||
} else {
|
||||
&mut self.definition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The storage for a WebAssembly global.
|
||||
impl fmt::Debug for VMMemory {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "VMMemory {{")?;
|
||||
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||
write!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The storage for a WebAssembly global defined within the instance.
|
||||
///
|
||||
/// TODO: Pack the globals more densely, rather than using the same size
|
||||
/// for every type.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct VMGlobal {
|
||||
pub struct VMGlobalDefinition {
|
||||
storage: [u8; 8],
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal {
|
||||
use super::VMGlobal;
|
||||
mod test_vmglobal_definition {
|
||||
use super::VMGlobalDefinition;
|
||||
use std::mem::{align_of, size_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>());
|
||||
fn check_vmglobal_definition_alignment() {
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>());
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>());
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>());
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_definition_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMGlobalDefinition>(),
|
||||
usize::from(offsets.size_of_vmglobal_definition())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMGlobalDefinition {
|
||||
pub unsafe fn as_i32(&mut self) -> &mut i32 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i32)
|
||||
}
|
||||
|
||||
pub unsafe fn as_i64(&mut self) -> &mut i64 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i64)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f32(&mut self) -> &mut f32 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f32)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f32_bits(&mut self) -> &mut u32 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u32)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f64(&mut self) -> &mut f64 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f64)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f64_bits(&mut self) -> &mut u64 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly global
|
||||
/// variable imported from another instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMGlobalImport {
|
||||
/// A pointer to the imported global variable description.
|
||||
from: *mut VMGlobalDefinition,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal_import {
|
||||
use super::VMGlobalImport;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_import_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMGlobalImport>(),
|
||||
usize::from(offsets.size_of_vmglobal_import())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMGlobalImport, from),
|
||||
usize::from(offsets.vmglobal_import_from())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly global
|
||||
/// variable. It must know whether the global variable is defined within the
|
||||
/// instance or imported.
|
||||
#[repr(C)]
|
||||
pub union VMGlobal {
|
||||
/// A global variable defined within the instance.
|
||||
definition: VMGlobalDefinition,
|
||||
|
||||
/// An imported global variable.
|
||||
import: VMGlobalImport,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal {
|
||||
use super::VMGlobal;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
@@ -106,20 +256,122 @@ mod test_vmglobal {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMGlobal {
|
||||
fn default() -> Self {
|
||||
VMGlobal { storage: [0; 8] }
|
||||
impl VMGlobal {
|
||||
/// Construct a `VMGlobalDefinition` variant of `VMGlobal`.
|
||||
pub fn definition(global: &Global) -> Self {
|
||||
let mut result = VMGlobalDefinition { storage: [0; 8] };
|
||||
unsafe {
|
||||
match global.initializer {
|
||||
GlobalInit::I32Const(x) => *result.as_i32() = x,
|
||||
GlobalInit::I64Const(x) => *result.as_i64() = x,
|
||||
GlobalInit::F32Const(x) => *result.as_f32_bits() = x,
|
||||
GlobalInit::F64Const(x) => *result.as_f64_bits() = x,
|
||||
GlobalInit::GetGlobal(_x) => unimplemented!("globals init with get_global"),
|
||||
GlobalInit::Import => panic!("attempting to initialize imported global"),
|
||||
}
|
||||
}
|
||||
Self { definition: result }
|
||||
}
|
||||
|
||||
/// Construct a `VMGlobalImmport` variant of `VMGlobal`.
|
||||
pub fn import(from: *mut VMGlobalDefinition) -> Self {
|
||||
Self {
|
||||
import: VMGlobalImport { from },
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the underlying `VMGlobalDefinition`.
|
||||
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMGlobalDefinition {
|
||||
if is_import {
|
||||
&mut *self.import.from
|
||||
} else {
|
||||
&mut self.definition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly table,
|
||||
/// namely the start address and the number of elements.
|
||||
impl fmt::Debug for VMGlobal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "VMGlobal {{")?;
|
||||
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||
write!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly table
|
||||
/// defined within the instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMTable {
|
||||
pub struct VMTableDefinition {
|
||||
base: *mut u8,
|
||||
current_elements: usize,
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmtable_definition {
|
||||
use super::VMTableDefinition;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmtable_definition_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMTableDefinition>(),
|
||||
usize::from(offsets.size_of_vmtable_definition())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTableDefinition, base),
|
||||
usize::from(offsets.vmtable_definition_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTableDefinition, current_elements),
|
||||
usize::from(offsets.vmtable_definition_current_elements())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly table
|
||||
/// imported from another instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMTableImport {
|
||||
/// A pointer to the imported table description.
|
||||
from: *mut VMTableDefinition,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmtable_import {
|
||||
use super::VMTableImport;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmtable_import_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMTableImport>(),
|
||||
usize::from(offsets.size_of_vmtable_import())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTableImport, from),
|
||||
usize::from(offsets.vmtable_import_from())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly table.
|
||||
/// It must know whether the table is defined within the instance
|
||||
/// or imported.
|
||||
#[repr(C)]
|
||||
pub union VMTable {
|
||||
/// A table defined within the instance.
|
||||
definition: VMTableDefinition,
|
||||
|
||||
/// An imported table.
|
||||
import: VMTableImport,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -132,43 +384,44 @@ mod test_vmtable {
|
||||
fn check_vmtable_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(size_of::<VMTable>(), usize::from(offsets.size_of_vmtable()));
|
||||
assert_eq!(
|
||||
offset_of!(VMTable, base),
|
||||
usize::from(offsets.vmtable_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTable, current_elements),
|
||||
usize::from(offsets.vmtable_current_elements())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMTable {
|
||||
pub fn new(base: *mut u8, current_elements: usize) -> Self {
|
||||
/// Construct a `VMTableDefinition` variant of `VMTable`.
|
||||
pub fn definition(base: *mut u8, current_elements: usize) -> Self {
|
||||
Self {
|
||||
base,
|
||||
current_elements,
|
||||
definition: VMTableDefinition {
|
||||
base,
|
||||
current_elements,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.base, self.current_elements) }
|
||||
/// Construct a `VMTableImmport` variant of `VMTable`.
|
||||
pub fn import(from: *mut VMTableDefinition) -> Self {
|
||||
Self {
|
||||
import: VMTableImport { from },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.base, self.current_elements) }
|
||||
/// Get the underlying `VMTableDefinition`.
|
||||
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMTableDefinition {
|
||||
if is_import {
|
||||
&mut *self.import.from
|
||||
} else {
|
||||
&mut self.definition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.current_elements
|
||||
impl fmt::Debug for VMTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "VMTable {{")?;
|
||||
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||
write!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +491,10 @@ impl Default for VMCallerCheckedAnyfunc {
|
||||
/// 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.
|
||||
///
|
||||
/// TODO: The number of memories, globals, tables, and signature IDs does
|
||||
/// not change dynamically, and pointer arrays are not indexed dynamically,
|
||||
/// so these fields could all be contiguously allocated.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct VMContext {
|
||||
@@ -300,28 +557,8 @@ impl VMContext {
|
||||
}
|
||||
|
||||
/// 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>())
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
pub unsafe fn global(&mut self, index: GlobalIndex) -> &mut VMGlobal {
|
||||
&mut *self.globals.add(index.index())
|
||||
}
|
||||
|
||||
/// Return a mutable reference to linear memory `index`.
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex};
|
||||
use export::Resolver;
|
||||
use std::str;
|
||||
use vmcontext::VMGlobal;
|
||||
use wasmtime_environ::{Compilation, Module, ModuleEnvironment, Tunables};
|
||||
use {compile_and_link_module, finish_instantiation, invoke, Code, Instance, InvokeOutcome, Value};
|
||||
use {
|
||||
compile_and_link_module, finish_instantiation, get, invoke, Code, Instance, InvokeOutcome,
|
||||
Value,
|
||||
};
|
||||
|
||||
/// A module, an instance of that module, and accompanying compilation artifacts.
|
||||
///
|
||||
@@ -14,8 +20,14 @@ pub struct InstanceWorld {
|
||||
|
||||
impl InstanceWorld {
|
||||
/// Create a new `InstanceWorld` by compiling the wasm module in `data` and instatiating it.
|
||||
pub fn new(code: &mut Code, isa: &isa::TargetIsa, data: &[u8]) -> Result<Self, String> {
|
||||
pub fn new(
|
||||
code: &mut Code,
|
||||
isa: &isa::TargetIsa,
|
||||
data: &[u8],
|
||||
resolver: &mut Resolver,
|
||||
) -> Result<Self, String> {
|
||||
let mut module = Module::new();
|
||||
// TODO: Allow the tunables to be overridden.
|
||||
let tunables = Tunables::default();
|
||||
let (instance, compilation) = {
|
||||
let translation = {
|
||||
@@ -24,9 +36,7 @@ impl InstanceWorld {
|
||||
environ.translate(&data).map_err(|e| e.to_string())?
|
||||
};
|
||||
|
||||
let imports_resolver = |_env: &str, _function: &str| None;
|
||||
|
||||
let compilation = compile_and_link_module(isa, &translation, &imports_resolver)?;
|
||||
let compilation = compile_and_link_module(isa, &translation, resolver)?;
|
||||
let mut instance = Instance::new(
|
||||
translation.module,
|
||||
&compilation,
|
||||
@@ -64,4 +74,19 @@ impl InstanceWorld {
|
||||
)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Read a global in this `InstanceWorld` by name.
|
||||
pub fn get(&mut self, global_name: &str) -> Result<Value, String> {
|
||||
get(&self.module, self.instance.vmctx(), global_name).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Returns a slice of the contents of allocated linear memory.
|
||||
pub fn inspect_memory(&self, memory_index: MemoryIndex, address: usize, len: usize) -> &[u8] {
|
||||
self.instance.inspect_memory(memory_index, address, len)
|
||||
}
|
||||
|
||||
/// Shows the value of a global variable.
|
||||
pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal {
|
||||
self.instance.inspect_global(global_index)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user