Initial support for function, table, memory, and global imports.
This commit is contained in:
@@ -27,6 +27,7 @@ cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branc
|
|||||||
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
wasmtime-environ = { path = "lib/environ" }
|
wasmtime-environ = { path = "lib/environ" }
|
||||||
|
wasmtime-runtime = { path = "lib/runtime" }
|
||||||
wasmtime-execute = { path = "lib/execute" }
|
wasmtime-execute = { path = "lib/execute" }
|
||||||
wasmtime-obj = { path = "lib/obj" }
|
wasmtime-obj = { path = "lib/obj" }
|
||||||
wasmtime-wast = { path = "lib/wast" }
|
wasmtime-wast = { path = "lib/wast" }
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", bran
|
|||||||
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cast = { version = "0.2.2", default-features = false }
|
cast = { version = "0.2.2", default-features = false }
|
||||||
failure = "0.1.3"
|
failure = { version = "0.1.3", default-features = false }
|
||||||
failure_derive = "0.1.3"
|
failure_derive = { version = "0.1.3", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ use cranelift_codegen::ir;
|
|||||||
use cranelift_codegen::ir::ExternalName;
|
use cranelift_codegen::ir::ExternalName;
|
||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
use cranelift_codegen::{CodegenError, Context};
|
use cranelift_codegen::{CodegenError, Context};
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmError};
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmError};
|
||||||
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, FuncEnvironment};
|
use func_environ::{
|
||||||
|
get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name,
|
||||||
|
get_memory32_grow_name, get_memory32_size_name, FuncEnvironment,
|
||||||
|
};
|
||||||
use module::Module;
|
use module::Module;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
@@ -49,13 +52,17 @@ impl binemit::RelocSink for RelocSink {
|
|||||||
name: &ExternalName,
|
name: &ExternalName,
|
||||||
addend: binemit::Addend,
|
addend: binemit::Addend,
|
||||||
) {
|
) {
|
||||||
let reloc_target = if *name == get_memory_grow_name() {
|
let reloc_target = if *name == get_memory32_grow_name() {
|
||||||
RelocationTarget::MemoryGrow
|
RelocationTarget::Memory32Grow
|
||||||
} else if *name == get_memory_size_name() {
|
} else if *name == get_imported_memory32_grow_name() {
|
||||||
RelocationTarget::MemorySize
|
RelocationTarget::ImportedMemory32Grow
|
||||||
|
} else if *name == get_memory32_size_name() {
|
||||||
|
RelocationTarget::Memory32Size
|
||||||
|
} else if *name == get_imported_memory32_size_name() {
|
||||||
|
RelocationTarget::ImportedMemory32Size
|
||||||
} else if let ExternalName::User { namespace, index } = *name {
|
} else 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::from_u32(index))
|
||||||
} else if let ExternalName::LibCall(libcall) = *name {
|
} else if let ExternalName::LibCall(libcall) = *name {
|
||||||
RelocationTarget::LibCall(libcall)
|
RelocationTarget::LibCall(libcall)
|
||||||
} else {
|
} else {
|
||||||
@@ -107,10 +114,14 @@ pub enum RelocationTarget {
|
|||||||
UserFunc(FuncIndex),
|
UserFunc(FuncIndex),
|
||||||
/// A compiler-generated libcall.
|
/// A compiler-generated libcall.
|
||||||
LibCall(ir::LibCall),
|
LibCall(ir::LibCall),
|
||||||
/// Function for growing the default memory by the specified amount of pages.
|
/// Function for growing a locally-defined 32-bit memory by the specified amount of pages.
|
||||||
MemoryGrow,
|
Memory32Grow,
|
||||||
/// Function for query current size of the default linear memory.
|
/// Function for growing an imported 32-bit memory by the specified amount of pages.
|
||||||
MemorySize,
|
ImportedMemory32Grow,
|
||||||
|
/// Function for query current size of a locally-defined 32-bit linear memory.
|
||||||
|
Memory32Size,
|
||||||
|
/// Function for query current size of an imported 32-bit linear memory.
|
||||||
|
ImportedMemory32Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relocations to apply to function bodies.
|
/// Relocations to apply to function bodies.
|
||||||
|
|||||||
@@ -1,721 +0,0 @@
|
|||||||
use cast;
|
|
||||||
use cranelift_codegen::cursor::FuncCursor;
|
|
||||||
use cranelift_codegen::ir;
|
|
||||||
use cranelift_codegen::ir::condcodes::*;
|
|
||||||
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
|
|
||||||
use cranelift_codegen::ir::types::*;
|
|
||||||
use cranelift_codegen::ir::{
|
|
||||||
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
|
|
||||||
};
|
|
||||||
use cranelift_codegen::isa;
|
|
||||||
use cranelift_entity::EntityRef;
|
|
||||||
use cranelift_wasm::{
|
|
||||||
self, translate_module, FuncIndex, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex,
|
|
||||||
SignatureIndex, Table, TableIndex, WasmResult,
|
|
||||||
};
|
|
||||||
use module::{
|
|
||||||
DataInitializer, Export, LazyContents, MemoryPlan, MemoryStyle, Module, TableElements,
|
|
||||||
TablePlan, TableStyle,
|
|
||||||
};
|
|
||||||
use std::clone::Clone;
|
|
||||||
use std::string::String;
|
|
||||||
use std::vec::Vec;
|
|
||||||
use tunables::Tunables;
|
|
||||||
use vmoffsets::VMOffsets;
|
|
||||||
use WASM_PAGE_SIZE;
|
|
||||||
|
|
||||||
/// Compute a `ir::ExternalName` for a given wasm function index.
|
|
||||||
pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
|
|
||||||
ir::ExternalName::user(0, func_index.as_u32())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute a `ir::ExternalName` for the `memory.grow` libcall.
|
|
||||||
pub fn get_memory_grow_name() -> ir::ExternalName {
|
|
||||||
ir::ExternalName::user(1, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute a `ir::ExternalName` for the `memory.size` libcall.
|
|
||||||
pub fn get_memory_size_name() -> ir::ExternalName {
|
|
||||||
ir::ExternalName::user(1, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Object containing the standalone environment information. To be passed after creation as
|
|
||||||
/// argument to `compile_module`.
|
|
||||||
pub struct ModuleEnvironment<'data, 'module> {
|
|
||||||
/// Compilation setting flags.
|
|
||||||
isa: &'module isa::TargetIsa,
|
|
||||||
|
|
||||||
/// Module information.
|
|
||||||
module: &'module mut Module,
|
|
||||||
|
|
||||||
/// References to information to be decoded later.
|
|
||||||
lazy: LazyContents<'data>,
|
|
||||||
|
|
||||||
/// Tunable parameters.
|
|
||||||
tunables: Tunables,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'data, 'module> ModuleEnvironment<'data, 'module> {
|
|
||||||
/// Allocates the enironment data structures with the given isa.
|
|
||||||
pub fn new(
|
|
||||||
isa: &'module isa::TargetIsa,
|
|
||||||
module: &'module mut Module,
|
|
||||||
tunables: Tunables,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
isa,
|
|
||||||
module,
|
|
||||||
lazy: LazyContents::new(),
|
|
||||||
tunables,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pointer_type(&self) -> ir::Type {
|
|
||||||
self.isa.frontend_config().pointer_type()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translate the given wasm module data using this environment. This consumes the
|
|
||||||
/// `ModuleEnvironment` with its mutable reference to the `Module` and produces a
|
|
||||||
/// `ModuleTranslation` with an immutable reference to the `Module` (which has
|
|
||||||
/// become fully populated).
|
|
||||||
pub fn translate(mut self, data: &'data [u8]) -> WasmResult<ModuleTranslation<'data, 'module>> {
|
|
||||||
translate_module(data, &mut self)?;
|
|
||||||
|
|
||||||
Ok(ModuleTranslation {
|
|
||||||
isa: self.isa,
|
|
||||||
module: self.module,
|
|
||||||
lazy: self.lazy,
|
|
||||||
tunables: self.tunables,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The FuncEnvironment implementation for use by the `ModuleEnvironment`.
|
|
||||||
pub struct FuncEnvironment<'module_environment> {
|
|
||||||
/// Compilation setting flags.
|
|
||||||
isa: &'module_environment isa::TargetIsa,
|
|
||||||
|
|
||||||
/// The module-level environment which this function-level environment belongs to.
|
|
||||||
module: &'module_environment Module,
|
|
||||||
|
|
||||||
/// The Cranelift global holding the vmctx address.
|
|
||||||
vmctx: Option<ir::GlobalValue>,
|
|
||||||
|
|
||||||
/// The Cranelift global holding the base address of the memories vector.
|
|
||||||
memories_base: Option<ir::GlobalValue>,
|
|
||||||
|
|
||||||
/// The Cranelift global holding the base address of the tables vector.
|
|
||||||
tables_base: Option<ir::GlobalValue>,
|
|
||||||
|
|
||||||
/// The Cranelift global holding the base address of the globals vector.
|
|
||||||
globals_base: Option<ir::GlobalValue>,
|
|
||||||
|
|
||||||
/// The Cranelift global holding the base address of the signature IDs vector.
|
|
||||||
signature_ids_base: Option<ir::GlobalValue>,
|
|
||||||
|
|
||||||
/// The external function declaration for implementing wasm's `memory.size`.
|
|
||||||
memory_size_extfunc: Option<FuncRef>,
|
|
||||||
|
|
||||||
/// The external function declaration for implementing wasm's `memory.grow`.
|
|
||||||
memory_grow_extfunc: Option<FuncRef>,
|
|
||||||
|
|
||||||
/// Offsets to struct fields accessed by JIT code.
|
|
||||||
offsets: VMOffsets,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'module_environment> FuncEnvironment<'module_environment> {
|
|
||||||
pub fn new(
|
|
||||||
isa: &'module_environment isa::TargetIsa,
|
|
||||||
module: &'module_environment Module,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
isa,
|
|
||||||
module,
|
|
||||||
vmctx: None,
|
|
||||||
memories_base: None,
|
|
||||||
tables_base: None,
|
|
||||||
globals_base: None,
|
|
||||||
signature_ids_base: None,
|
|
||||||
memory_size_extfunc: None,
|
|
||||||
memory_grow_extfunc: None,
|
|
||||||
offsets: VMOffsets::new(isa.pointer_bytes()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transform the call argument list in preparation for making a call.
|
|
||||||
fn get_real_call_args(func: &Function, call_args: &[ir::Value]) -> Vec<ir::Value> {
|
|
||||||
let mut real_call_args = Vec::with_capacity(call_args.len() + 1);
|
|
||||||
real_call_args.extend_from_slice(call_args);
|
|
||||||
real_call_args.push(func.special_param(ArgumentPurpose::VMContext).unwrap());
|
|
||||||
real_call_args
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
|
|
||||||
self.vmctx.unwrap_or_else(|| {
|
|
||||||
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
|
|
||||||
self.vmctx = Some(vmctx);
|
|
||||||
vmctx
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This trait is useful for `translate_module` because it tells how to translate
|
|
||||||
/// enironment-dependent wasm instructions. These functions should not be called by the user.
|
|
||||||
impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|
||||||
for ModuleEnvironment<'data, 'module>
|
|
||||||
{
|
|
||||||
fn target_config(&self) -> isa::TargetFrontendConfig {
|
|
||||||
self.isa.frontend_config()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_signature(&mut self, sig: &ir::Signature) {
|
|
||||||
let sig = translate_signature(sig.clone(), self.pointer_type());
|
|
||||||
// TODO: Deduplicate signatures.
|
|
||||||
self.module.signatures.push(sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature {
|
|
||||||
&self.module.signatures[sig_index]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) {
|
|
||||||
debug_assert_eq!(
|
|
||||||
self.module.functions.len(),
|
|
||||||
self.module.imported_funcs.len(),
|
|
||||||
"Imported functions must be declared first"
|
|
||||||
);
|
|
||||||
self.module.functions.push(sig_index);
|
|
||||||
|
|
||||||
self.module
|
|
||||||
.imported_funcs
|
|
||||||
.push((String::from(module), String::from(field)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_num_func_imports(&self) -> usize {
|
|
||||||
self.module.imported_funcs.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
|
|
||||||
self.module.functions.push(sig_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
|
|
||||||
self.module.functions[func_index]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_global_import(&mut self, global: Global, module: &str, field: &str) {
|
|
||||||
debug_assert_eq!(
|
|
||||||
self.module.globals.len(),
|
|
||||||
self.module.imported_globals.len(),
|
|
||||||
"Imported globals must be declared first"
|
|
||||||
);
|
|
||||||
self.module.globals.push(global);
|
|
||||||
|
|
||||||
self.module
|
|
||||||
.imported_globals
|
|
||||||
.push((String::from(module), String::from(field)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_global(&mut self, global: Global) {
|
|
||||||
self.module.globals.push(global);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_global(&self, global_index: GlobalIndex) -> &Global {
|
|
||||||
&self.module.globals[global_index]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) {
|
|
||||||
debug_assert_eq!(
|
|
||||||
self.module.table_plans.len(),
|
|
||||||
self.module.imported_tables.len(),
|
|
||||||
"Imported tables must be declared first"
|
|
||||||
);
|
|
||||||
let plan = TablePlan::for_table(table, &self.tunables);
|
|
||||||
self.module.table_plans.push(plan);
|
|
||||||
|
|
||||||
self.module
|
|
||||||
.imported_tables
|
|
||||||
.push((String::from(module), String::from(field)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_table(&mut self, table: Table) {
|
|
||||||
let plan = TablePlan::for_table(table, &self.tunables);
|
|
||||||
self.module.table_plans.push(plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_table_elements(
|
|
||||||
&mut self,
|
|
||||||
table_index: TableIndex,
|
|
||||||
base: Option<GlobalIndex>,
|
|
||||||
offset: usize,
|
|
||||||
elements: Vec<FuncIndex>,
|
|
||||||
) {
|
|
||||||
self.module.table_elements.push(TableElements {
|
|
||||||
table_index,
|
|
||||||
base,
|
|
||||||
offset,
|
|
||||||
elements,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_memory_import(&mut self, memory: Memory, module: &str, field: &str) {
|
|
||||||
debug_assert_eq!(
|
|
||||||
self.module.memory_plans.len(),
|
|
||||||
self.module.imported_memories.len(),
|
|
||||||
"Imported memories must be declared first"
|
|
||||||
);
|
|
||||||
let plan = MemoryPlan::for_memory(memory, &self.tunables);
|
|
||||||
self.module.memory_plans.push(plan);
|
|
||||||
|
|
||||||
self.module
|
|
||||||
.imported_memories
|
|
||||||
.push((String::from(module), String::from(field)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_memory(&mut self, memory: Memory) {
|
|
||||||
let plan = MemoryPlan::for_memory(memory, &self.tunables);
|
|
||||||
self.module.memory_plans.push(plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_data_initialization(
|
|
||||||
&mut self,
|
|
||||||
memory_index: MemoryIndex,
|
|
||||||
base: Option<GlobalIndex>,
|
|
||||||
offset: usize,
|
|
||||||
data: &'data [u8],
|
|
||||||
) {
|
|
||||||
self.lazy.data_initializers.push(DataInitializer {
|
|
||||||
memory_index,
|
|
||||||
base,
|
|
||||||
offset,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) {
|
|
||||||
self.module
|
|
||||||
.exports
|
|
||||||
.insert(String::from(name), Export::Function(func_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_table_export(&mut self, table_index: TableIndex, name: &str) {
|
|
||||||
self.module
|
|
||||||
.exports
|
|
||||||
.insert(String::from(name), Export::Table(table_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) {
|
|
||||||
self.module
|
|
||||||
.exports
|
|
||||||
.insert(String::from(name), Export::Memory(memory_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) {
|
|
||||||
self.module
|
|
||||||
.exports
|
|
||||||
.insert(String::from(name), Export::Global(global_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_start_func(&mut self, func_index: FuncIndex) {
|
|
||||||
debug_assert!(self.module.start_func.is_none());
|
|
||||||
self.module.start_func = Some(func_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> {
|
|
||||||
self.lazy.function_body_inputs.push(body_bytes);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
|
|
||||||
fn target_config(&self) -> isa::TargetFrontendConfig {
|
|
||||||
self.isa.frontend_config()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
|
|
||||||
let pointer_type = self.pointer_type();
|
|
||||||
|
|
||||||
let vmctx = self.vmctx(func);
|
|
||||||
let mut globals_base = self.globals_base.unwrap_or_else(|| {
|
|
||||||
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: vmctx,
|
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_globals())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
self.globals_base = Some(new_base);
|
|
||||||
new_base
|
|
||||||
});
|
|
||||||
let mut offset = self.offsets.index_vmglobal(index.as_u32());
|
|
||||||
|
|
||||||
// For imported memories, the `VMGlobal` array contains a pointer to
|
|
||||||
// the `VMGlobalDefinition` rather than containing the `VMGlobalDefinition`
|
|
||||||
// inline, so we do an extra indirection.
|
|
||||||
if self.module.is_imported_global(index) {
|
|
||||||
globals_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: globals_base,
|
|
||||||
offset: Offset32::new(self.offsets.index_vmglobal_import_from(index.as_u32())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
offset = self.offsets.index_vmglobal(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalVariable::Memory {
|
|
||||||
gv: globals_base,
|
|
||||||
offset: offset.into(),
|
|
||||||
ty: self.module.globals[index].ty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
|
|
||||||
let pointer_type = self.pointer_type();
|
|
||||||
|
|
||||||
let vmctx = self.vmctx(func);
|
|
||||||
let mut memories_base = self.memories_base.unwrap_or_else(|| {
|
|
||||||
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: vmctx,
|
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_memories())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
self.memories_base = Some(new_base);
|
|
||||||
new_base
|
|
||||||
});
|
|
||||||
let mut base_offset = self.offsets.index_vmmemory_definition_base(index.as_u32());
|
|
||||||
let mut current_length_offset = self
|
|
||||||
.offsets
|
|
||||||
.index_vmmemory_definition_current_length(index.as_u32());
|
|
||||||
|
|
||||||
// For imported memories, the `VMMemory` array contains a pointer to
|
|
||||||
// the `VMMemoryDefinition` rather than containing the `VMMemoryDefinition`
|
|
||||||
// inline, so we do an extra indirection.
|
|
||||||
if self.module.is_imported_memory(index) {
|
|
||||||
memories_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: memories_base,
|
|
||||||
offset: Offset32::new(self.offsets.index_vmmemory_import_from(index.as_u32())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
base_offset = self.offsets.index_vmmemory_definition_base(0);
|
|
||||||
current_length_offset = self.offsets.index_vmmemory_definition_current_length(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a declared maximum, we can make this a "static" heap, which is
|
|
||||||
// allocated up front and never moved.
|
|
||||||
let (offset_guard_size, heap_style, readonly_base) = match self.module.memory_plans[index] {
|
|
||||||
MemoryPlan {
|
|
||||||
memory: _,
|
|
||||||
style: MemoryStyle::Dynamic,
|
|
||||||
offset_guard_size,
|
|
||||||
} => {
|
|
||||||
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: memories_base,
|
|
||||||
offset: Offset32::new(current_length_offset),
|
|
||||||
global_type: I32,
|
|
||||||
readonly: false,
|
|
||||||
});
|
|
||||||
(
|
|
||||||
Uimm64::new(offset_guard_size),
|
|
||||||
ir::HeapStyle::Dynamic {
|
|
||||||
bound_gv: heap_bound,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MemoryPlan {
|
|
||||||
memory: _,
|
|
||||||
style: MemoryStyle::Static { bound },
|
|
||||||
offset_guard_size,
|
|
||||||
} => (
|
|
||||||
Uimm64::new(offset_guard_size),
|
|
||||||
ir::HeapStyle::Static {
|
|
||||||
bound: Uimm64::new(u64::from(bound) * u64::from(WASM_PAGE_SIZE)),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: memories_base,
|
|
||||||
offset: Offset32::new(base_offset),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: readonly_base,
|
|
||||||
});
|
|
||||||
func.create_heap(ir::HeapData {
|
|
||||||
base: heap_base,
|
|
||||||
min_size: 0.into(),
|
|
||||||
offset_guard_size,
|
|
||||||
style: heap_style,
|
|
||||||
index_type: I32,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {
|
|
||||||
let pointer_type = self.pointer_type();
|
|
||||||
|
|
||||||
let vmctx = self.vmctx(func);
|
|
||||||
let mut tables_base = self.tables_base.unwrap_or_else(|| {
|
|
||||||
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: vmctx,
|
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_tables())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
self.tables_base = Some(new_base);
|
|
||||||
new_base
|
|
||||||
});
|
|
||||||
let mut base_offset = self.offsets.index_vmtable_definition_base(index.as_u32());
|
|
||||||
let mut current_elements_offset = self
|
|
||||||
.offsets
|
|
||||||
.index_vmtable_definition_current_elements(index.as_u32());
|
|
||||||
|
|
||||||
// For imported tables, the `VMTable` array contains a pointer to
|
|
||||||
// the `VMTableDefinition` rather than containing the `VMTableDefinition`
|
|
||||||
// inline, so we do an extra indirection.
|
|
||||||
if self.module.is_imported_table(index) {
|
|
||||||
tables_base = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: tables_base,
|
|
||||||
offset: Offset32::new(self.offsets.index_vmtable_import_from(index.as_u32())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
base_offset = self.offsets.index_vmtable_definition_base(0);
|
|
||||||
current_elements_offset = self.offsets.index_vmtable_definition_current_elements(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: tables_base,
|
|
||||||
offset: Offset32::new(base_offset),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: false,
|
|
||||||
});
|
|
||||||
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: tables_base,
|
|
||||||
offset: Offset32::new(current_elements_offset),
|
|
||||||
global_type: I32,
|
|
||||||
readonly: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let element_size = match self.module.table_plans[index].style {
|
|
||||||
TableStyle::CallerChecksSignature => {
|
|
||||||
u64::from(self.offsets.size_of_vmcaller_checked_anyfunc())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
func.create_table(ir::TableData {
|
|
||||||
base_gv,
|
|
||||||
min_size: Uimm64::new(0),
|
|
||||||
bound_gv,
|
|
||||||
element_size: Uimm64::new(element_size),
|
|
||||||
index_type: I32,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
|
|
||||||
func.import_signature(self.module.signatures[index].clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
|
|
||||||
let sigidx = self.module.functions[index];
|
|
||||||
let signature = func.import_signature(self.module.signatures[sigidx].clone());
|
|
||||||
let name = get_func_name(index);
|
|
||||||
// We currently allocate all code segments independently, so nothing
|
|
||||||
// is colocated.
|
|
||||||
let colocated = false;
|
|
||||||
func.import_function(ir::ExtFuncData {
|
|
||||||
name,
|
|
||||||
signature,
|
|
||||||
colocated,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_call_indirect(
|
|
||||||
&mut self,
|
|
||||||
mut pos: FuncCursor,
|
|
||||||
table_index: TableIndex,
|
|
||||||
table: ir::Table,
|
|
||||||
sig_index: SignatureIndex,
|
|
||||||
sig_ref: ir::SigRef,
|
|
||||||
callee: ir::Value,
|
|
||||||
call_args: &[ir::Value],
|
|
||||||
) -> WasmResult<ir::Inst> {
|
|
||||||
let pointer_type = self.pointer_type();
|
|
||||||
|
|
||||||
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
|
|
||||||
|
|
||||||
// Dereference table_entry_addr to get the function address.
|
|
||||||
let mut mem_flags = ir::MemFlags::new();
|
|
||||||
mem_flags.set_notrap();
|
|
||||||
mem_flags.set_aligned();
|
|
||||||
let func_addr = pos.ins().load(
|
|
||||||
pointer_type,
|
|
||||||
mem_flags,
|
|
||||||
table_entry_addr,
|
|
||||||
i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// If necessary, check the signature.
|
|
||||||
match self.module.table_plans[table_index].style {
|
|
||||||
TableStyle::CallerChecksSignature => {
|
|
||||||
let sig_id_size = self.offsets.size_of_vmsignature_id();
|
|
||||||
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
|
|
||||||
|
|
||||||
let vmctx = self.vmctx(pos.func);
|
|
||||||
let signature_ids_base = self.signature_ids_base.unwrap_or_else(|| {
|
|
||||||
let new_base = pos.func.create_global_value(ir::GlobalValueData::Load {
|
|
||||||
base: vmctx,
|
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_signature_ids())),
|
|
||||||
global_type: pointer_type,
|
|
||||||
readonly: true,
|
|
||||||
});
|
|
||||||
self.signature_ids_base = Some(new_base);
|
|
||||||
new_base
|
|
||||||
});
|
|
||||||
let sig_ids = pos.ins().global_value(pointer_type, signature_ids_base);
|
|
||||||
|
|
||||||
// Load the caller ID.
|
|
||||||
// TODO: Factor this out into a MemFlags constructor, as it's used a lot.
|
|
||||||
let mut mem_flags = ir::MemFlags::new();
|
|
||||||
mem_flags.set_notrap();
|
|
||||||
mem_flags.set_aligned();
|
|
||||||
let caller_sig_id = pos.ins().load(
|
|
||||||
sig_id_type,
|
|
||||||
mem_flags,
|
|
||||||
sig_ids,
|
|
||||||
cast::i32(
|
|
||||||
sig_index
|
|
||||||
.as_u32()
|
|
||||||
.checked_mul(u32::from(sig_id_size))
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load the callee ID.
|
|
||||||
let mut mem_flags = ir::MemFlags::new();
|
|
||||||
mem_flags.set_notrap();
|
|
||||||
mem_flags.set_aligned();
|
|
||||||
let callee_sig_id = pos.ins().load(
|
|
||||||
sig_id_type,
|
|
||||||
mem_flags,
|
|
||||||
table_entry_addr,
|
|
||||||
i32::from(self.offsets.vmcaller_checked_anyfunc_type_id()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check that they match.
|
|
||||||
let cmp = pos.ins().icmp(IntCC::Equal, callee_sig_id, caller_sig_id);
|
|
||||||
pos.ins().trapz(cmp, ir::TrapCode::BadSignature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args);
|
|
||||||
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_call(
|
|
||||||
&mut self,
|
|
||||||
mut pos: FuncCursor,
|
|
||||||
_callee_index: FuncIndex,
|
|
||||||
callee: ir::FuncRef,
|
|
||||||
call_args: &[ir::Value],
|
|
||||||
) -> WasmResult<ir::Inst> {
|
|
||||||
let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args);
|
|
||||||
Ok(pos.ins().call(callee, &real_call_args))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_memory_grow(
|
|
||||||
&mut self,
|
|
||||||
mut pos: FuncCursor,
|
|
||||||
index: MemoryIndex,
|
|
||||||
_heap: ir::Heap,
|
|
||||||
val: ir::Value,
|
|
||||||
) -> WasmResult<ir::Value> {
|
|
||||||
let memory_grow_func = self.memory_grow_extfunc.unwrap_or_else(|| {
|
|
||||||
let sig_ref = pos.func.import_signature(Signature {
|
|
||||||
params: vec![
|
|
||||||
AbiParam::new(I32),
|
|
||||||
AbiParam::new(I32),
|
|
||||||
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
|
||||||
],
|
|
||||||
returns: vec![AbiParam::new(I32)],
|
|
||||||
call_conv: self.isa.frontend_config().default_call_conv,
|
|
||||||
});
|
|
||||||
// We currently allocate all code segments independently, so nothing
|
|
||||||
// is colocated.
|
|
||||||
let colocated = false;
|
|
||||||
pos.func.import_function(ExtFuncData {
|
|
||||||
name: get_memory_grow_name(),
|
|
||||||
signature: sig_ref,
|
|
||||||
colocated,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
self.memory_grow_extfunc = Some(memory_grow_func);
|
|
||||||
let memory_index = pos.ins().iconst(I32, index.index() as i64);
|
|
||||||
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
|
|
||||||
let call_inst = pos
|
|
||||||
.ins()
|
|
||||||
.call(memory_grow_func, &[val, memory_index, vmctx]);
|
|
||||||
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn translate_memory_size(
|
|
||||||
&mut self,
|
|
||||||
mut pos: FuncCursor,
|
|
||||||
index: MemoryIndex,
|
|
||||||
_heap: ir::Heap,
|
|
||||||
) -> WasmResult<ir::Value> {
|
|
||||||
let memory_size_func = self.memory_size_extfunc.unwrap_or_else(|| {
|
|
||||||
let sig_ref = pos.func.import_signature(Signature {
|
|
||||||
params: vec![
|
|
||||||
AbiParam::new(I32),
|
|
||||||
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
|
||||||
],
|
|
||||||
returns: vec![AbiParam::new(I32)],
|
|
||||||
call_conv: self.isa.frontend_config().default_call_conv,
|
|
||||||
});
|
|
||||||
// We currently allocate all code segments independently, so nothing
|
|
||||||
// is colocated.
|
|
||||||
let colocated = false;
|
|
||||||
pos.func.import_function(ExtFuncData {
|
|
||||||
name: get_memory_size_name(),
|
|
||||||
signature: sig_ref,
|
|
||||||
colocated,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
self.memory_size_extfunc = Some(memory_size_func);
|
|
||||||
let memory_index = pos.ins().iconst(I32, index.index() as i64);
|
|
||||||
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
|
|
||||||
let call_inst = pos.ins().call(memory_size_func, &[memory_index, vmctx]);
|
|
||||||
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The result of translating via `ModuleEnvironment`.
|
|
||||||
pub struct ModuleTranslation<'data, 'module> {
|
|
||||||
/// Compilation setting flags.
|
|
||||||
pub isa: &'module isa::TargetIsa,
|
|
||||||
|
|
||||||
/// Module information.
|
|
||||||
pub module: &'module Module,
|
|
||||||
|
|
||||||
/// Pointers into the raw data buffer.
|
|
||||||
pub lazy: LazyContents<'data>,
|
|
||||||
|
|
||||||
/// Tunable parameters.
|
|
||||||
pub tunables: Tunables,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'data, 'module> ModuleTranslation<'data, 'module> {
|
|
||||||
/// Return a new `FuncEnvironment` for translating a function.
|
|
||||||
pub fn func_env(&self) -> FuncEnvironment {
|
|
||||||
FuncEnvironment::new(self.isa, &self.module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add environment-specific function parameters.
|
|
||||||
pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir::Signature {
|
|
||||||
sig.params
|
|
||||||
.push(AbiParam::special(pointer_type, ArgumentPurpose::VMContext));
|
|
||||||
sig
|
|
||||||
}
|
|
||||||
660
lib/environ/src/func_environ.rs
Normal file
660
lib/environ/src/func_environ.rs
Normal file
@@ -0,0 +1,660 @@
|
|||||||
|
use cast;
|
||||||
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_codegen::ir::condcodes::*;
|
||||||
|
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
|
||||||
|
use cranelift_codegen::ir::types::*;
|
||||||
|
use cranelift_codegen::ir::{
|
||||||
|
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
|
||||||
|
};
|
||||||
|
use cranelift_codegen::isa;
|
||||||
|
use cranelift_entity::EntityRef;
|
||||||
|
use cranelift_wasm::{
|
||||||
|
self, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex,
|
||||||
|
GlobalVariable, MemoryIndex, SignatureIndex, TableIndex, WasmResult,
|
||||||
|
};
|
||||||
|
use module::{MemoryPlan, MemoryStyle, Module, TableStyle};
|
||||||
|
use std::clone::Clone;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use vmoffsets::VMOffsets;
|
||||||
|
use WASM_PAGE_SIZE;
|
||||||
|
|
||||||
|
/// Compute an `ir::ExternalName` for a given wasm function index.
|
||||||
|
pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
|
||||||
|
ir::ExternalName::user(0, func_index.as_u32())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute an `ir::ExternalName` for the `memory.grow` libcall for
|
||||||
|
/// 32-bit locally-defined memories.
|
||||||
|
pub fn get_memory32_grow_name() -> ir::ExternalName {
|
||||||
|
ir::ExternalName::user(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute an `ir::ExternalName` for the `memory.grow` libcall for
|
||||||
|
/// 32-bit imported memories.
|
||||||
|
pub fn get_imported_memory32_grow_name() -> ir::ExternalName {
|
||||||
|
ir::ExternalName::user(1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute an `ir::ExternalName` for the `memory.size` libcall for
|
||||||
|
/// 32-bit locally-defined memories.
|
||||||
|
pub fn get_memory32_size_name() -> ir::ExternalName {
|
||||||
|
ir::ExternalName::user(1, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute an `ir::ExternalName` for the `memory.size` libcall for
|
||||||
|
/// 32-bit imported memories.
|
||||||
|
pub fn get_imported_memory32_size_name() -> ir::ExternalName {
|
||||||
|
ir::ExternalName::user(1, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The FuncEnvironment implementation for use by the `ModuleEnvironment`.
|
||||||
|
pub struct FuncEnvironment<'module_environment> {
|
||||||
|
/// Compilation setting flags.
|
||||||
|
isa: &'module_environment isa::TargetIsa,
|
||||||
|
|
||||||
|
/// The module-level environment which this function-level environment belongs to.
|
||||||
|
module: &'module_environment Module,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the vmctx address.
|
||||||
|
vmctx: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the imported functions table.
|
||||||
|
imported_functions_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the imported tables table.
|
||||||
|
imported_tables_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the imported memories table.
|
||||||
|
imported_memories_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the imported globals table.
|
||||||
|
imported_globals_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the tables vector.
|
||||||
|
tables_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the memories vector.
|
||||||
|
memories_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the globals vector.
|
||||||
|
globals_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The Cranelift global holding the base address of the signature IDs vector.
|
||||||
|
signature_ids_base: Option<ir::GlobalValue>,
|
||||||
|
|
||||||
|
/// The external function declaration for implementing wasm's `memory.size`
|
||||||
|
/// for locally-defined 32-bit memories.
|
||||||
|
memory32_size_extfunc: Option<FuncRef>,
|
||||||
|
|
||||||
|
/// The external function declaration for implementing wasm's `memory.size`
|
||||||
|
/// for imported 32-bit memories.
|
||||||
|
imported_memory32_size_extfunc: Option<FuncRef>,
|
||||||
|
|
||||||
|
/// The external function declaration for implementing wasm's `memory.grow`
|
||||||
|
/// for locally-defined memories.
|
||||||
|
memory_grow_extfunc: Option<FuncRef>,
|
||||||
|
|
||||||
|
/// The external function declaration for implementing wasm's `memory.grow`
|
||||||
|
/// for imported memories.
|
||||||
|
imported_memory_grow_extfunc: Option<FuncRef>,
|
||||||
|
|
||||||
|
/// Offsets to struct fields accessed by JIT code.
|
||||||
|
offsets: VMOffsets,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||||
|
pub fn new(
|
||||||
|
isa: &'module_environment isa::TargetIsa,
|
||||||
|
module: &'module_environment Module,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
isa,
|
||||||
|
module,
|
||||||
|
vmctx: None,
|
||||||
|
imported_functions_base: None,
|
||||||
|
imported_tables_base: None,
|
||||||
|
imported_memories_base: None,
|
||||||
|
imported_globals_base: None,
|
||||||
|
tables_base: None,
|
||||||
|
memories_base: None,
|
||||||
|
globals_base: None,
|
||||||
|
signature_ids_base: None,
|
||||||
|
memory32_size_extfunc: None,
|
||||||
|
imported_memory32_size_extfunc: None,
|
||||||
|
memory_grow_extfunc: None,
|
||||||
|
imported_memory_grow_extfunc: None,
|
||||||
|
offsets: VMOffsets::new(isa.pointer_bytes()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_type(&self) -> ir::Type {
|
||||||
|
self.isa.frontend_config().pointer_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transform the call argument list in preparation for making a call.
|
||||||
|
fn get_real_call_args(func: &Function, call_args: &[ir::Value]) -> Vec<ir::Value> {
|
||||||
|
let mut real_call_args = Vec::with_capacity(call_args.len() + 1);
|
||||||
|
real_call_args.extend_from_slice(call_args);
|
||||||
|
real_call_args.push(func.special_param(ArgumentPurpose::VMContext).unwrap());
|
||||||
|
real_call_args
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.vmctx.unwrap_or_else(|| {
|
||||||
|
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
|
||||||
|
self.vmctx = Some(vmctx);
|
||||||
|
vmctx
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_imported_functions_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.imported_functions_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_imported_functions())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.imported_functions_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_imported_tables_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.imported_tables_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_imported_tables())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.imported_tables_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_imported_memories_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.imported_memories_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_imported_memories())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.imported_memories_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_imported_globals_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.imported_globals_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_imported_globals())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.imported_globals_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tables_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.tables_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_tables())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.tables_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memories_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.memories_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_memories())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.memories_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_globals_base(&mut self, func: &mut Function) -> ir::GlobalValue {
|
||||||
|
self.globals_base.unwrap_or_else(|| {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let vmctx = self.vmctx(func);
|
||||||
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_globals())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.globals_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory_grow_sig(&self, func: &mut Function) -> ir::SigRef {
|
||||||
|
func.import_signature(Signature {
|
||||||
|
params: vec![
|
||||||
|
AbiParam::new(I32),
|
||||||
|
AbiParam::new(I32),
|
||||||
|
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
||||||
|
],
|
||||||
|
returns: vec![AbiParam::new(I32)],
|
||||||
|
call_conv: self.isa.frontend_config().default_call_conv,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the memory.grow function to call for the given index, along with the
|
||||||
|
/// translated index value to pass to it.
|
||||||
|
fn get_memory_grow_func(
|
||||||
|
&mut self,
|
||||||
|
func: &mut Function,
|
||||||
|
index: MemoryIndex,
|
||||||
|
) -> (FuncRef, usize) {
|
||||||
|
if self.module.is_imported_memory(index) {
|
||||||
|
let extfunc = self.imported_memory_grow_extfunc.unwrap_or_else(|| {
|
||||||
|
let sig_ref = self.get_memory_grow_sig(func);
|
||||||
|
func.import_function(ExtFuncData {
|
||||||
|
name: get_imported_memory32_grow_name(),
|
||||||
|
signature: sig_ref,
|
||||||
|
// We currently allocate all code segments independently, so nothing
|
||||||
|
// is colocated.
|
||||||
|
colocated: false,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
self.imported_memory_grow_extfunc = Some(extfunc);
|
||||||
|
(extfunc, index.index())
|
||||||
|
} else {
|
||||||
|
let extfunc = self.memory_grow_extfunc.unwrap_or_else(|| {
|
||||||
|
let sig_ref = self.get_memory_grow_sig(func);
|
||||||
|
func.import_function(ExtFuncData {
|
||||||
|
name: get_memory32_grow_name(),
|
||||||
|
signature: sig_ref,
|
||||||
|
// We currently allocate all code segments independently, so nothing
|
||||||
|
// is colocated.
|
||||||
|
colocated: false,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
self.memory_grow_extfunc = Some(extfunc);
|
||||||
|
(
|
||||||
|
extfunc,
|
||||||
|
self.module.defined_memory_index(index).unwrap().index(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_memory32_size_sig(&self, func: &mut Function) -> ir::SigRef {
|
||||||
|
func.import_signature(Signature {
|
||||||
|
params: vec![
|
||||||
|
AbiParam::new(I32),
|
||||||
|
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
||||||
|
],
|
||||||
|
returns: vec![AbiParam::new(I32)],
|
||||||
|
call_conv: self.isa.frontend_config().default_call_conv,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the memory.size function to call for the given index, along with the
|
||||||
|
/// translated index value to pass to it.
|
||||||
|
fn get_memory_size_func(
|
||||||
|
&mut self,
|
||||||
|
func: &mut Function,
|
||||||
|
index: MemoryIndex,
|
||||||
|
) -> (FuncRef, usize) {
|
||||||
|
if self.module.is_imported_memory(index) {
|
||||||
|
let extfunc = self.imported_memory32_size_extfunc.unwrap_or_else(|| {
|
||||||
|
let sig_ref = self.get_memory32_size_sig(func);
|
||||||
|
func.import_function(ExtFuncData {
|
||||||
|
name: get_imported_memory32_size_name(),
|
||||||
|
signature: sig_ref,
|
||||||
|
// We currently allocate all code segments independently, so nothing
|
||||||
|
// is colocated.
|
||||||
|
colocated: false,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
self.imported_memory32_size_extfunc = Some(extfunc);
|
||||||
|
(extfunc, index.index())
|
||||||
|
} else {
|
||||||
|
let extfunc = self.memory32_size_extfunc.unwrap_or_else(|| {
|
||||||
|
let sig_ref = self.get_memory32_size_sig(func);
|
||||||
|
func.import_function(ExtFuncData {
|
||||||
|
name: get_memory32_size_name(),
|
||||||
|
signature: sig_ref,
|
||||||
|
// We currently allocate all code segments independently, so nothing
|
||||||
|
// is colocated.
|
||||||
|
colocated: false,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
self.memory32_size_extfunc = Some(extfunc);
|
||||||
|
(
|
||||||
|
extfunc,
|
||||||
|
self.module.defined_memory_index(index).unwrap().index(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
|
||||||
|
fn target_config(&self) -> isa::TargetFrontendConfig {
|
||||||
|
self.isa.frontend_config()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
|
let (table, def_index) = if let Some(def_index) = self.module.defined_table_index(index) {
|
||||||
|
let table = self.get_tables_base(func);
|
||||||
|
(table, def_index)
|
||||||
|
} else {
|
||||||
|
let imported_tables_base = self.get_imported_tables_base(func);
|
||||||
|
let from_offset = self.offsets.index_vmtable_import_from(index);
|
||||||
|
let table = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: imported_tables_base,
|
||||||
|
offset: Offset32::new(from_offset),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
(table, DefinedTableIndex::new(0))
|
||||||
|
};
|
||||||
|
let base_offset = self.offsets.index_vmtable_definition_base(def_index);
|
||||||
|
let current_elements_offset = self
|
||||||
|
.offsets
|
||||||
|
.index_vmtable_definition_current_elements(def_index);
|
||||||
|
|
||||||
|
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: table,
|
||||||
|
offset: Offset32::new(base_offset),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: false,
|
||||||
|
});
|
||||||
|
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: table,
|
||||||
|
offset: Offset32::new(current_elements_offset),
|
||||||
|
global_type: self.offsets.type_of_vmtable_definition_current_elements(),
|
||||||
|
readonly: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let element_size = match self.module.table_plans[index].style {
|
||||||
|
TableStyle::CallerChecksSignature => {
|
||||||
|
u64::from(self.offsets.size_of_vmcaller_checked_anyfunc())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
func.create_table(ir::TableData {
|
||||||
|
base_gv,
|
||||||
|
min_size: Uimm64::new(0),
|
||||||
|
bound_gv,
|
||||||
|
element_size: Uimm64::new(element_size),
|
||||||
|
index_type: I32,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
|
let (memory, def_index) = if let Some(def_index) = self.module.defined_memory_index(index) {
|
||||||
|
let memory = self.get_memories_base(func);
|
||||||
|
(memory, def_index)
|
||||||
|
} else {
|
||||||
|
let imported_memories_base = self.get_imported_memories_base(func);
|
||||||
|
let from_offset = self.offsets.index_vmmemory_import_from(index);
|
||||||
|
let memory = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: imported_memories_base,
|
||||||
|
offset: Offset32::new(from_offset),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
(memory, DefinedMemoryIndex::new(0))
|
||||||
|
};
|
||||||
|
let base_offset = self.offsets.index_vmmemory_definition_base(def_index);
|
||||||
|
let current_length_offset = self
|
||||||
|
.offsets
|
||||||
|
.index_vmmemory_definition_current_length(def_index);
|
||||||
|
|
||||||
|
// If we have a declared maximum, we can make this a "static" heap, which is
|
||||||
|
// allocated up front and never moved.
|
||||||
|
let (offset_guard_size, heap_style, readonly_base) = match self.module.memory_plans[index] {
|
||||||
|
MemoryPlan {
|
||||||
|
memory: _,
|
||||||
|
style: MemoryStyle::Dynamic,
|
||||||
|
offset_guard_size,
|
||||||
|
} => {
|
||||||
|
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: memory,
|
||||||
|
offset: Offset32::new(current_length_offset),
|
||||||
|
global_type: self.offsets.type_of_vmmemory_definition_current_length(),
|
||||||
|
readonly: false,
|
||||||
|
});
|
||||||
|
(
|
||||||
|
Uimm64::new(offset_guard_size),
|
||||||
|
ir::HeapStyle::Dynamic {
|
||||||
|
bound_gv: heap_bound,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MemoryPlan {
|
||||||
|
memory: _,
|
||||||
|
style: MemoryStyle::Static { bound },
|
||||||
|
offset_guard_size,
|
||||||
|
} => (
|
||||||
|
Uimm64::new(offset_guard_size),
|
||||||
|
ir::HeapStyle::Static {
|
||||||
|
bound: Uimm64::new(u64::from(bound) * u64::from(WASM_PAGE_SIZE)),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: memory,
|
||||||
|
offset: Offset32::new(base_offset),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: readonly_base,
|
||||||
|
});
|
||||||
|
func.create_heap(ir::HeapData {
|
||||||
|
base: heap_base,
|
||||||
|
min_size: 0.into(),
|
||||||
|
offset_guard_size,
|
||||||
|
style: heap_style,
|
||||||
|
index_type: I32,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
|
let (global, def_index) = if let Some(def_index) = self.module.defined_global_index(index) {
|
||||||
|
let global = self.get_globals_base(func);
|
||||||
|
(global, def_index)
|
||||||
|
} else {
|
||||||
|
let imported_globals_base = self.get_imported_globals_base(func);
|
||||||
|
let from_offset = self.offsets.index_vmglobal_import_from(index);
|
||||||
|
let global = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: imported_globals_base,
|
||||||
|
offset: Offset32::new(from_offset),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
(global, DefinedGlobalIndex::new(0))
|
||||||
|
};
|
||||||
|
let offset = self.offsets.index_vmglobal_definition(def_index);
|
||||||
|
|
||||||
|
GlobalVariable::Memory {
|
||||||
|
gv: global,
|
||||||
|
offset: offset.into(),
|
||||||
|
ty: self.module.globals[index].ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
|
||||||
|
func.import_signature(self.module.signatures[index].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef {
|
||||||
|
let sigidx = self.module.functions[index];
|
||||||
|
let signature = func.import_signature(self.module.signatures[sigidx].clone());
|
||||||
|
let name = get_func_name(index);
|
||||||
|
func.import_function(ir::ExtFuncData {
|
||||||
|
name,
|
||||||
|
signature,
|
||||||
|
// We currently allocate all code segments independently, so nothing
|
||||||
|
// is colocated.
|
||||||
|
colocated: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_call_indirect(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
table_index: TableIndex,
|
||||||
|
table: ir::Table,
|
||||||
|
sig_index: SignatureIndex,
|
||||||
|
sig_ref: ir::SigRef,
|
||||||
|
callee: ir::Value,
|
||||||
|
call_args: &[ir::Value],
|
||||||
|
) -> WasmResult<ir::Inst> {
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
|
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
|
||||||
|
|
||||||
|
// Dereference table_entry_addr to get the function address.
|
||||||
|
let mem_flags = ir::MemFlags::trusted();
|
||||||
|
let func_addr = pos.ins().load(
|
||||||
|
pointer_type,
|
||||||
|
mem_flags,
|
||||||
|
table_entry_addr,
|
||||||
|
i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// If necessary, check the signature.
|
||||||
|
match self.module.table_plans[table_index].style {
|
||||||
|
TableStyle::CallerChecksSignature => {
|
||||||
|
let sig_id_size = self.offsets.size_of_vmshared_signature_index();
|
||||||
|
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
|
||||||
|
|
||||||
|
let vmctx = self.vmctx(pos.func);
|
||||||
|
let signature_ids_base = self.signature_ids_base.unwrap_or_else(|| {
|
||||||
|
let new_base = pos.func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: vmctx,
|
||||||
|
offset: Offset32::new(i32::from(self.offsets.vmctx_signature_ids())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
self.signature_ids_base = Some(new_base);
|
||||||
|
new_base
|
||||||
|
});
|
||||||
|
let sig_ids = pos.ins().global_value(pointer_type, signature_ids_base);
|
||||||
|
|
||||||
|
// Load the caller ID.
|
||||||
|
let mem_flags = ir::MemFlags::trusted();
|
||||||
|
let caller_sig_id = pos.ins().load(
|
||||||
|
sig_id_type,
|
||||||
|
mem_flags,
|
||||||
|
sig_ids,
|
||||||
|
cast::i32(
|
||||||
|
sig_index
|
||||||
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(sig_id_size))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load the callee ID.
|
||||||
|
let mem_flags = ir::MemFlags::trusted();
|
||||||
|
let callee_sig_id = pos.ins().load(
|
||||||
|
sig_id_type,
|
||||||
|
mem_flags,
|
||||||
|
table_entry_addr,
|
||||||
|
i32::from(self.offsets.vmcaller_checked_anyfunc_type_index()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check that they match.
|
||||||
|
let cmp = pos.ins().icmp(IntCC::Equal, callee_sig_id, caller_sig_id);
|
||||||
|
pos.ins().trapz(cmp, ir::TrapCode::BadSignature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args);
|
||||||
|
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_call(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
callee_index: FuncIndex,
|
||||||
|
callee: ir::FuncRef,
|
||||||
|
call_args: &[ir::Value],
|
||||||
|
) -> WasmResult<ir::Inst> {
|
||||||
|
let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args);
|
||||||
|
|
||||||
|
// Handle direct calls to locally-defined functions.
|
||||||
|
if !self.module.is_imported_function(callee_index) {
|
||||||
|
return Ok(pos.ins().call(callee, &real_call_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle direct calls to imported functions. We use an indirect call
|
||||||
|
// so that we don't have to patch the code at runtime.
|
||||||
|
let pointer_type = self.pointer_type();
|
||||||
|
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
|
||||||
|
let imported_functions_base = self.get_imported_functions_base(&mut pos.func);
|
||||||
|
let base = pos
|
||||||
|
.ins()
|
||||||
|
.global_value(pointer_type, imported_functions_base);
|
||||||
|
let offset = self.offsets.index_vmfunction_body_import(callee_index);
|
||||||
|
let mem_flags = ir::MemFlags::trusted();
|
||||||
|
let func_addr = pos.ins().load(pointer_type, mem_flags, base, offset);
|
||||||
|
Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_memory_grow(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
index: MemoryIndex,
|
||||||
|
_heap: ir::Heap,
|
||||||
|
val: ir::Value,
|
||||||
|
) -> WasmResult<ir::Value> {
|
||||||
|
let (memory_grow_func, index_arg) = self.get_memory_grow_func(&mut pos.func, index);
|
||||||
|
let memory_index = pos.ins().iconst(I32, index_arg as i64);
|
||||||
|
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
|
||||||
|
let call_inst = pos
|
||||||
|
.ins()
|
||||||
|
.call(memory_grow_func, &[val, memory_index, vmctx]);
|
||||||
|
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_memory_size(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
index: MemoryIndex,
|
||||||
|
_heap: ir::Heap,
|
||||||
|
) -> WasmResult<ir::Value> {
|
||||||
|
let (memory_size_func, index_arg) = self.get_memory_size_func(&mut pos.func, index);
|
||||||
|
let memory_index = pos.ins().iconst(I32, index_arg as i64);
|
||||||
|
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
|
||||||
|
let call_inst = pos.ins().call(memory_size_func, &[memory_index, vmctx]);
|
||||||
|
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,18 +39,19 @@ extern crate failure;
|
|||||||
extern crate failure_derive;
|
extern crate failure_derive;
|
||||||
|
|
||||||
mod compilation;
|
mod compilation;
|
||||||
mod environ;
|
mod func_environ;
|
||||||
mod module;
|
mod module;
|
||||||
|
mod module_environ;
|
||||||
mod tunables;
|
mod tunables;
|
||||||
mod vmoffsets;
|
mod vmoffsets;
|
||||||
|
|
||||||
pub use compilation::{
|
pub use compilation::{
|
||||||
compile_module, Compilation, CompileError, RelocSink, Relocation, RelocationTarget, Relocations,
|
compile_module, Compilation, CompileError, RelocSink, Relocation, RelocationTarget, Relocations,
|
||||||
};
|
};
|
||||||
pub use environ::{translate_signature, ModuleEnvironment, ModuleTranslation};
|
|
||||||
pub use module::{
|
pub use module::{
|
||||||
DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
|
DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
|
||||||
};
|
};
|
||||||
|
pub use module_environ::{translate_signature, ModuleEnvironment, ModuleTranslation};
|
||||||
pub use tunables::Tunables;
|
pub use tunables::Tunables;
|
||||||
pub use vmoffsets::VMOffsets;
|
pub use vmoffsets::VMOffsets;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
use cranelift_wasm::{
|
use cranelift_wasm::{
|
||||||
DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table,
|
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global,
|
||||||
TableIndex,
|
GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex,
|
||||||
};
|
};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -142,12 +142,12 @@ pub struct Module {
|
|||||||
/// Names of imported tables.
|
/// Names of imported tables.
|
||||||
pub imported_tables: PrimaryMap<TableIndex, (String, String)>,
|
pub imported_tables: PrimaryMap<TableIndex, (String, String)>,
|
||||||
|
|
||||||
/// Names of imported globals.
|
|
||||||
pub imported_globals: PrimaryMap<GlobalIndex, (String, String)>,
|
|
||||||
|
|
||||||
/// Names of imported memories.
|
/// Names of imported memories.
|
||||||
pub imported_memories: PrimaryMap<MemoryIndex, (String, String)>,
|
pub imported_memories: PrimaryMap<MemoryIndex, (String, String)>,
|
||||||
|
|
||||||
|
/// Names of imported globals.
|
||||||
|
pub imported_globals: PrimaryMap<GlobalIndex, (String, String)>,
|
||||||
|
|
||||||
/// Types of functions, imported and local.
|
/// Types of functions, imported and local.
|
||||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||||
|
|
||||||
@@ -176,9 +176,9 @@ impl Module {
|
|||||||
Self {
|
Self {
|
||||||
signatures: PrimaryMap::new(),
|
signatures: PrimaryMap::new(),
|
||||||
imported_funcs: PrimaryMap::new(),
|
imported_funcs: PrimaryMap::new(),
|
||||||
|
imported_tables: PrimaryMap::new(),
|
||||||
imported_memories: PrimaryMap::new(),
|
imported_memories: PrimaryMap::new(),
|
||||||
imported_globals: PrimaryMap::new(),
|
imported_globals: PrimaryMap::new(),
|
||||||
imported_tables: PrimaryMap::new(),
|
|
||||||
functions: PrimaryMap::new(),
|
functions: PrimaryMap::new(),
|
||||||
table_plans: PrimaryMap::new(),
|
table_plans: PrimaryMap::new(),
|
||||||
memory_plans: PrimaryMap::new(),
|
memory_plans: PrimaryMap::new(),
|
||||||
@@ -211,20 +211,71 @@ impl Module {
|
|||||||
index.index() < self.imported_funcs.len()
|
index.index() < self.imported_funcs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a `DefinedTableIndex` into a `TableIndex`.
|
||||||
|
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
|
||||||
|
TableIndex::new(self.imported_tables.len() + defined_table.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
|
||||||
|
/// index is an imported table.
|
||||||
|
pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
|
||||||
|
if table.index() < self.imported_tables.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(DefinedTableIndex::new(
|
||||||
|
table.index() - self.imported_tables.len(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Test whether the given table index is for an imported table.
|
/// Test whether the given table index is for an imported table.
|
||||||
pub fn is_imported_table(&self, index: TableIndex) -> bool {
|
pub fn is_imported_table(&self, index: TableIndex) -> bool {
|
||||||
index.index() < self.imported_tables.len()
|
index.index() < self.imported_tables.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given global index is for an imported global.
|
/// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
|
||||||
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
|
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
|
||||||
index.index() < self.imported_globals.len()
|
MemoryIndex::new(self.imported_memories.len() + defined_memory.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
|
||||||
|
/// index is an imported memory.
|
||||||
|
pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
|
||||||
|
if memory.index() < self.imported_memories.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(DefinedMemoryIndex::new(
|
||||||
|
memory.index() - self.imported_memories.len(),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given memory index is for an imported memory.
|
/// Test whether the given memory index is for an imported memory.
|
||||||
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
|
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
|
||||||
index.index() < self.imported_memories.len()
|
index.index() < self.imported_memories.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
|
||||||
|
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
|
||||||
|
GlobalIndex::new(self.imported_globals.len() + defined_global.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
|
||||||
|
/// index is an imported global.
|
||||||
|
pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
|
||||||
|
if global.index() < self.imported_globals.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(DefinedGlobalIndex::new(
|
||||||
|
global.index() - self.imported_globals.len(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test whether the given global index is for an imported global.
|
||||||
|
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
|
||||||
|
index.index() < self.imported_globals.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A data initializer for linear memory.
|
/// A data initializer for linear memory.
|
||||||
|
|||||||
261
lib/environ/src/module_environ.rs
Normal file
261
lib/environ/src/module_environ.rs
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_codegen::ir::{AbiParam, ArgumentPurpose};
|
||||||
|
use cranelift_codegen::isa;
|
||||||
|
use cranelift_wasm::{
|
||||||
|
self, translate_module, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||||
|
Table, TableIndex, WasmResult,
|
||||||
|
};
|
||||||
|
use func_environ::FuncEnvironment;
|
||||||
|
use module::{DataInitializer, Export, LazyContents, MemoryPlan, Module, TableElements, TablePlan};
|
||||||
|
use std::clone::Clone;
|
||||||
|
use std::string::String;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use tunables::Tunables;
|
||||||
|
|
||||||
|
/// Object containing the standalone environment information. To be passed after creation as
|
||||||
|
/// argument to `compile_module`.
|
||||||
|
pub struct ModuleEnvironment<'data, 'module> {
|
||||||
|
/// Compilation setting flags.
|
||||||
|
isa: &'module isa::TargetIsa,
|
||||||
|
|
||||||
|
/// Module information.
|
||||||
|
module: &'module mut Module,
|
||||||
|
|
||||||
|
/// References to information to be decoded later.
|
||||||
|
lazy: LazyContents<'data>,
|
||||||
|
|
||||||
|
/// Tunable parameters.
|
||||||
|
tunables: Tunables,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'data, 'module> ModuleEnvironment<'data, 'module> {
|
||||||
|
/// Allocates the enironment data structures with the given isa.
|
||||||
|
pub fn new(
|
||||||
|
isa: &'module isa::TargetIsa,
|
||||||
|
module: &'module mut Module,
|
||||||
|
tunables: Tunables,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
isa,
|
||||||
|
module,
|
||||||
|
lazy: LazyContents::new(),
|
||||||
|
tunables,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointer_type(&self) -> ir::Type {
|
||||||
|
self.isa.frontend_config().pointer_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translate the given wasm module data using this environment. This consumes the
|
||||||
|
/// `ModuleEnvironment` with its mutable reference to the `Module` and produces a
|
||||||
|
/// `ModuleTranslation` with an immutable reference to the `Module` (which has
|
||||||
|
/// become fully populated).
|
||||||
|
pub fn translate(mut self, data: &'data [u8]) -> WasmResult<ModuleTranslation<'data, 'module>> {
|
||||||
|
translate_module(data, &mut self)?;
|
||||||
|
|
||||||
|
Ok(ModuleTranslation {
|
||||||
|
isa: self.isa,
|
||||||
|
module: self.module,
|
||||||
|
lazy: self.lazy,
|
||||||
|
tunables: self.tunables,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This trait is useful for `translate_module` because it tells how to translate
|
||||||
|
/// enironment-dependent wasm instructions. These functions should not be called by the user.
|
||||||
|
impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||||
|
for ModuleEnvironment<'data, 'module>
|
||||||
|
{
|
||||||
|
fn target_config(&self) -> isa::TargetFrontendConfig {
|
||||||
|
self.isa.frontend_config()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||||
|
let sig = translate_signature(sig.clone(), self.pointer_type());
|
||||||
|
// TODO: Deduplicate signatures.
|
||||||
|
self.module.signatures.push(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature {
|
||||||
|
&self.module.signatures[sig_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) {
|
||||||
|
debug_assert_eq!(
|
||||||
|
self.module.functions.len(),
|
||||||
|
self.module.imported_funcs.len(),
|
||||||
|
"Imported functions must be declared first"
|
||||||
|
);
|
||||||
|
self.module.functions.push(sig_index);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_funcs
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_num_func_imports(&self) -> usize {
|
||||||
|
self.module.imported_funcs.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
|
||||||
|
self.module.functions.push(sig_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
|
||||||
|
self.module.functions[func_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_global_import(&mut self, global: Global, module: &str, field: &str) {
|
||||||
|
debug_assert_eq!(
|
||||||
|
self.module.globals.len(),
|
||||||
|
self.module.imported_globals.len(),
|
||||||
|
"Imported globals must be declared first"
|
||||||
|
);
|
||||||
|
self.module.globals.push(global);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_globals
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_global(&mut self, global: Global) {
|
||||||
|
self.module.globals.push(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_global(&self, global_index: GlobalIndex) -> &Global {
|
||||||
|
&self.module.globals[global_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) {
|
||||||
|
debug_assert_eq!(
|
||||||
|
self.module.table_plans.len(),
|
||||||
|
self.module.imported_tables.len(),
|
||||||
|
"Imported tables must be declared first"
|
||||||
|
);
|
||||||
|
let plan = TablePlan::for_table(table, &self.tunables);
|
||||||
|
self.module.table_plans.push(plan);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_tables
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_table(&mut self, table: Table) {
|
||||||
|
let plan = TablePlan::for_table(table, &self.tunables);
|
||||||
|
self.module.table_plans.push(plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_table_elements(
|
||||||
|
&mut self,
|
||||||
|
table_index: TableIndex,
|
||||||
|
base: Option<GlobalIndex>,
|
||||||
|
offset: usize,
|
||||||
|
elements: Vec<FuncIndex>,
|
||||||
|
) {
|
||||||
|
self.module.table_elements.push(TableElements {
|
||||||
|
table_index,
|
||||||
|
base,
|
||||||
|
offset,
|
||||||
|
elements,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_memory_import(&mut self, memory: Memory, module: &str, field: &str) {
|
||||||
|
debug_assert_eq!(
|
||||||
|
self.module.memory_plans.len(),
|
||||||
|
self.module.imported_memories.len(),
|
||||||
|
"Imported memories must be declared first"
|
||||||
|
);
|
||||||
|
let plan = MemoryPlan::for_memory(memory, &self.tunables);
|
||||||
|
self.module.memory_plans.push(plan);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_memories
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_memory(&mut self, memory: Memory) {
|
||||||
|
let plan = MemoryPlan::for_memory(memory, &self.tunables);
|
||||||
|
self.module.memory_plans.push(plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_data_initialization(
|
||||||
|
&mut self,
|
||||||
|
memory_index: MemoryIndex,
|
||||||
|
base: Option<GlobalIndex>,
|
||||||
|
offset: usize,
|
||||||
|
data: &'data [u8],
|
||||||
|
) {
|
||||||
|
self.lazy.data_initializers.push(DataInitializer {
|
||||||
|
memory_index,
|
||||||
|
base,
|
||||||
|
offset,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) {
|
||||||
|
self.module
|
||||||
|
.exports
|
||||||
|
.insert(String::from(name), Export::Function(func_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_table_export(&mut self, table_index: TableIndex, name: &str) {
|
||||||
|
self.module
|
||||||
|
.exports
|
||||||
|
.insert(String::from(name), Export::Table(table_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) {
|
||||||
|
self.module
|
||||||
|
.exports
|
||||||
|
.insert(String::from(name), Export::Memory(memory_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) {
|
||||||
|
self.module
|
||||||
|
.exports
|
||||||
|
.insert(String::from(name), Export::Global(global_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_start_func(&mut self, func_index: FuncIndex) {
|
||||||
|
debug_assert!(self.module.start_func.is_none());
|
||||||
|
self.module.start_func = Some(func_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> {
|
||||||
|
self.lazy.function_body_inputs.push(body_bytes);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of translating via `ModuleEnvironment`.
|
||||||
|
pub struct ModuleTranslation<'data, 'module> {
|
||||||
|
/// Compilation setting flags.
|
||||||
|
pub isa: &'module isa::TargetIsa,
|
||||||
|
|
||||||
|
/// Module information.
|
||||||
|
pub module: &'module Module,
|
||||||
|
|
||||||
|
/// Pointers into the raw data buffer.
|
||||||
|
pub lazy: LazyContents<'data>,
|
||||||
|
|
||||||
|
/// Tunable parameters.
|
||||||
|
pub tunables: Tunables,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'data, 'module> ModuleTranslation<'data, 'module> {
|
||||||
|
/// Return a new `FuncEnvironment` for translating a function.
|
||||||
|
pub fn func_env(&self) -> FuncEnvironment {
|
||||||
|
FuncEnvironment::new(self.isa, &self.module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add environment-specific function parameters.
|
||||||
|
pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir::Signature {
|
||||||
|
sig.params
|
||||||
|
.push(AbiParam::special(pointer_type, ArgumentPurpose::VMContext));
|
||||||
|
sig
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
//! Offsets and sizes of various structs in wasmtime-execute's vmcontext
|
//! Offsets and sizes of various structs in wasmtime-execute's vmcontext
|
||||||
//! module.
|
//! module.
|
||||||
|
|
||||||
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_wasm::{
|
||||||
|
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, GlobalIndex, MemoryIndex,
|
||||||
|
TableIndex,
|
||||||
|
};
|
||||||
|
|
||||||
/// This class computes offsets to fields within `VMContext` and other
|
/// This class computes offsets to fields within `VMContext` and other
|
||||||
/// related structs that JIT code accesses directly.
|
/// related structs that JIT code accesses directly.
|
||||||
pub struct VMOffsets {
|
pub struct VMOffsets {
|
||||||
@@ -14,7 +20,86 @@ impl VMOffsets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMMemoryDefinition`.
|
/// Offsets for `*const VMFunctionBody`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The size of the `current_elements` field.
|
||||||
|
#[allow(clippy::identity_op)]
|
||||||
|
pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `VMTableImport`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `from` field.
|
||||||
|
#[allow(clippy::erasing_op)]
|
||||||
|
pub fn vmtable_import_from(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `vmctx` field.
|
||||||
|
#[allow(clippy::identity_op)]
|
||||||
|
pub fn vmtable_import_vmctx(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMTableImport`.
|
||||||
|
pub fn size_of_vmtable_import(&self) -> u8 {
|
||||||
|
2 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `VMTableDefinition`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `base` field.
|
||||||
|
#[allow(clippy::erasing_op)]
|
||||||
|
pub fn vmtable_definition_base(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `current_elements` field.
|
||||||
|
#[allow(clippy::identity_op)]
|
||||||
|
pub fn vmtable_definition_current_elements(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The size of the `current_elements` field.
|
||||||
|
pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMTableDefinition`.
|
||||||
|
pub fn size_of_vmtable_definition(&self) -> u8 {
|
||||||
|
2 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of the `current_elements` field.
|
||||||
|
pub fn type_of_vmtable_definition_current_elements(&self) -> ir::Type {
|
||||||
|
ir::Type::int(u16::from(self.size_of_vmtable_definition_current_elements()) * 8).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `VMMemoryImport`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `from` field.
|
||||||
|
#[allow(clippy::erasing_op)]
|
||||||
|
pub fn vmmemory_import_from(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `vmctx` field.
|
||||||
|
#[allow(clippy::identity_op)]
|
||||||
|
pub fn vmmemory_import_vmctx(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMMemoryImport`.
|
||||||
|
pub fn size_of_vmmemory_import(&self) -> u8 {
|
||||||
|
2 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `VMMemoryDefinition`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `base` field.
|
/// The offset of the `base` field.
|
||||||
#[allow(clippy::erasing_op)]
|
#[allow(clippy::erasing_op)]
|
||||||
@@ -28,44 +113,23 @@ impl VMOffsets {
|
|||||||
1 * self.pointer_size
|
1 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The size of the `current_length` field.
|
||||||
|
pub fn size_of_vmmemory_definition_current_length(&self) -> u8 {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the size of `VMMemoryDefinition`.
|
/// Return the size of `VMMemoryDefinition`.
|
||||||
pub fn size_of_vmmemory_definition(&self) -> u8 {
|
pub fn size_of_vmmemory_definition(&self) -> u8 {
|
||||||
2 * self.pointer_size
|
2 * self.pointer_size
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMMemoryImport`.
|
/// The type of the `current_length` field.
|
||||||
impl VMOffsets {
|
pub fn type_of_vmmemory_definition_current_length(&self) -> ir::Type {
|
||||||
/// The offset of the `from` field.
|
ir::Type::int(u16::from(self.size_of_vmmemory_definition_current_length()) * 8).unwrap()
|
||||||
#[allow(clippy::erasing_op)]
|
|
||||||
pub fn vmmemory_import_from(&self) -> u8 {
|
|
||||||
0 * self.pointer_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the size of `VMMemoryImport`.
|
|
||||||
#[allow(clippy::identity_op)]
|
|
||||||
pub fn size_of_vmmemory_import(&self) -> u8 {
|
|
||||||
1 * self.pointer_size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMMemory`.
|
/// Offsets for `VMGlobalImport`.
|
||||||
impl VMOffsets {
|
|
||||||
/// Return the size of `VMMemory`.
|
|
||||||
pub fn size_of_vmmemory(&self) -> u8 {
|
|
||||||
2 * self.pointer_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMGlobalDefinition`.
|
|
||||||
impl VMOffsets {
|
|
||||||
/// Return the size of `VMGlobalDefinition`.
|
|
||||||
pub fn size_of_vmglobal_definition(&self) -> u8 {
|
|
||||||
8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMGlobalImport`.
|
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `from` field.
|
/// The offset of the `from` field.
|
||||||
#[allow(clippy::erasing_op)]
|
#[allow(clippy::erasing_op)]
|
||||||
@@ -80,67 +144,23 @@ impl VMOffsets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMGlobal`.
|
/// Offsets for `VMGlobalDefinition`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// Return the size of `VMGlobal`.
|
/// Return the size of `VMGlobalDefinition`.
|
||||||
pub fn size_of_vmglobal(&self) -> u8 {
|
pub fn size_of_vmglobal_definition(&self) -> u8 {
|
||||||
assert!(self.size_of_vmglobal_import() <= self.size_of_vmglobal_definition());
|
8
|
||||||
self.size_of_vmglobal_definition()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMTableDefinition`.
|
/// Offsets for `VMSharedSignatureIndex`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `base` field.
|
/// Return the size of `VMSharedSignatureIndex`.
|
||||||
#[allow(clippy::erasing_op)]
|
pub fn size_of_vmshared_signature_index(&self) -> u8 {
|
||||||
pub fn vmtable_definition_base(&self) -> u8 {
|
|
||||||
0 * self.pointer_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The offset of the `current_elements` field.
|
|
||||||
#[allow(clippy::identity_op)]
|
|
||||||
pub fn vmtable_definition_current_elements(&self) -> u8 {
|
|
||||||
1 * self.pointer_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the size of `VMTableDefinition`.
|
|
||||||
pub fn size_of_vmtable_definition(&self) -> u8 {
|
|
||||||
2 * self.pointer_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMTableImport`.
|
|
||||||
impl VMOffsets {
|
|
||||||
/// The offset of the `from` field.
|
|
||||||
#[allow(clippy::erasing_op)]
|
|
||||||
pub fn vmtable_import_from(&self) -> u8 {
|
|
||||||
0 * self.pointer_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the size of `VMTableImport`.
|
|
||||||
#[allow(clippy::identity_op)]
|
|
||||||
pub fn size_of_vmtable_import(&self) -> u8 {
|
|
||||||
1 * self.pointer_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMTable`.
|
|
||||||
impl VMOffsets {
|
|
||||||
/// Return the size of `VMTable`.
|
|
||||||
pub fn size_of_vmtable(&self) -> u8 {
|
|
||||||
2 * self.pointer_size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMSignatureId`.
|
|
||||||
impl VMOffsets {
|
|
||||||
/// Return the size of `VMSignatureId`.
|
|
||||||
pub fn size_of_vmsignature_id(&self) -> u8 {
|
|
||||||
4
|
4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMCallerCheckedAnyfunc`.
|
/// Offsets for `VMCallerCheckedAnyfunc`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `func_ptr` field.
|
/// The offset of the `func_ptr` field.
|
||||||
#[allow(clippy::erasing_op)]
|
#[allow(clippy::erasing_op)]
|
||||||
@@ -148,131 +168,208 @@ impl VMOffsets {
|
|||||||
0 * self.pointer_size
|
0 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The offset of the `type_id` field.
|
/// The offset of the `type_index` field.
|
||||||
#[allow(clippy::identity_op)]
|
#[allow(clippy::identity_op)]
|
||||||
pub fn vmcaller_checked_anyfunc_type_id(&self) -> u8 {
|
pub fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
|
||||||
1 * self.pointer_size
|
1 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the size of `VMTable`.
|
/// Return the size of `VMCallerCheckedAnyfunc`.
|
||||||
pub fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
|
pub fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
|
||||||
2 * self.pointer_size
|
2 * self.pointer_size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMContext`.
|
/// Offsets for `VMContext`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `memories` field.
|
/// The offset of the `tables` field.
|
||||||
#[allow(clippy::erasing_op)]
|
#[allow(clippy::erasing_op)]
|
||||||
pub fn vmctx_memories(&self) -> u8 {
|
pub fn vmctx_imported_functions(&self) -> u8 {
|
||||||
0 * self.pointer_size
|
0 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The offset of the `globals` field.
|
/// The offset of the `tables` field.
|
||||||
#[allow(clippy::identity_op)]
|
#[allow(clippy::identity_op)]
|
||||||
pub fn vmctx_globals(&self) -> u8 {
|
pub fn vmctx_imported_tables(&self) -> u8 {
|
||||||
1 * self.pointer_size
|
1 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The offset of the `memories` field.
|
||||||
|
pub fn vmctx_imported_memories(&self) -> u8 {
|
||||||
|
2 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `globals` field.
|
||||||
|
pub fn vmctx_imported_globals(&self) -> u8 {
|
||||||
|
3 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
/// The offset of the `tables` field.
|
/// The offset of the `tables` field.
|
||||||
pub fn vmctx_tables(&self) -> u8 {
|
pub fn vmctx_tables(&self) -> u8 {
|
||||||
2 * self.pointer_size
|
4 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `memories` field.
|
||||||
|
pub fn vmctx_memories(&self) -> u8 {
|
||||||
|
5 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `globals` field.
|
||||||
|
pub fn vmctx_globals(&self) -> u8 {
|
||||||
|
6 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The offset of the `signature_ids` field.
|
/// The offset of the `signature_ids` field.
|
||||||
pub fn vmctx_signature_ids(&self) -> u8 {
|
pub fn vmctx_signature_ids(&self) -> u8 {
|
||||||
3 * self.pointer_size
|
7 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the size of `VMContext`.
|
/// Return the size of `VMContext`.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn size_of_vmctx(&self) -> u8 {
|
pub fn size_of_vmctx(&self) -> u8 {
|
||||||
4 * self.pointer_size
|
8 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `memories` pointer to `VMMemory` index `index`.
|
/// Return the offset from the `imported_tables` pointer to `VMTableImport` index `index`.
|
||||||
pub fn index_vmmemory(&self, index: u32) -> i32 {
|
fn index_vmtable_import(&self, index: TableIndex) -> i32 {
|
||||||
cast::i32(
|
cast::i32(
|
||||||
index
|
index
|
||||||
.checked_mul(u32::from(self.size_of_vmmemory()))
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmtable_import()))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `globals` pointer to `VMGlobal` index `index`.
|
/// Return the offset from the `tables` pointer to `VMTableDefinition` index `index`.
|
||||||
pub fn index_vmglobal(&self, index: u32) -> i32 {
|
fn index_vmtable_definition(&self, index: DefinedTableIndex) -> i32 {
|
||||||
cast::i32(
|
cast::i32(
|
||||||
index
|
index
|
||||||
.checked_mul(u32::from(self.size_of_vmglobal()))
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmtable_definition()))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `tables` pointer to `VMTable` index `index`.
|
/// Return the offset from the `imported_memories` pointer to `VMMemoryImport` index `index`.
|
||||||
pub fn index_vmtable(&self, index: u32) -> i32 {
|
fn index_vmmemory_import(&self, index: MemoryIndex) -> i32 {
|
||||||
cast::i32(
|
cast::i32(
|
||||||
index
|
index
|
||||||
.checked_mul(u32::from(self.size_of_vmtable()))
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmmemory_import()))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `memories` pointer to `VMMemoryDefinition` index `index`.
|
||||||
|
fn index_vmmemory_definition(&self, index: DefinedMemoryIndex) -> i32 {
|
||||||
|
cast::i32(
|
||||||
|
index
|
||||||
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmmemory_definition()))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `imported_globals` pointer to `VMGlobalImport` index `index`.
|
||||||
|
fn index_vmglobal_import(&self, index: GlobalIndex) -> i32 {
|
||||||
|
cast::i32(
|
||||||
|
index
|
||||||
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmglobal_import()))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `imported_functions` pointer to the
|
||||||
|
/// `*const VMFunctionBody` index `index`.
|
||||||
|
pub fn index_vmfunction_body_import(&self, index: FuncIndex) -> i32 {
|
||||||
|
cast::i32(
|
||||||
|
index
|
||||||
|
.as_u32()
|
||||||
|
.checked_mul(u32::from(self.size_of_vmfunction_body_ptr()))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `tables` pointer to the `from` field in
|
||||||
|
/// `VMTableImport` index `index`.
|
||||||
|
pub fn index_vmtable_import_from(&self, index: TableIndex) -> i32 {
|
||||||
|
self.index_vmtable_import(index)
|
||||||
|
.checked_add(i32::from(self.vmtable_import_from()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `tables` pointer to the `base` field in
|
||||||
|
/// `VMTableDefinition` index `index`.
|
||||||
|
pub fn index_vmtable_definition_base(&self, index: DefinedTableIndex) -> i32 {
|
||||||
|
self.index_vmtable_definition(index)
|
||||||
|
.checked_add(i32::from(self.vmtable_definition_base()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `tables` pointer to the `current_elements` field in
|
||||||
|
/// `VMTableDefinition` index `index`.
|
||||||
|
pub fn index_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> i32 {
|
||||||
|
self.index_vmtable_definition(index)
|
||||||
|
.checked_add(i32::from(self.vmtable_definition_current_elements()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `memories` pointer to the `from` field in
|
||||||
|
/// `VMMemoryImport` index `index`.
|
||||||
|
pub fn index_vmmemory_import_from(&self, index: MemoryIndex) -> i32 {
|
||||||
|
self.index_vmmemory_import(index)
|
||||||
|
.checked_add(i32::from(self.vmmemory_import_from()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `memories` pointer to the `vmctx` field in
|
||||||
|
/// `VMMemoryImport` index `index`.
|
||||||
|
pub fn index_vmmemory_import_vmctx(&self, index: MemoryIndex) -> i32 {
|
||||||
|
self.index_vmmemory_import(index)
|
||||||
|
.checked_add(i32::from(self.vmmemory_import_vmctx()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the offset from the `memories` pointer to the `base` field in
|
/// Return the offset from the `memories` pointer to the `base` field in
|
||||||
/// `VMMemory` index `index`.
|
/// `VMMemoryDefinition` index `index`.
|
||||||
pub fn index_vmmemory_definition_base(&self, index: u32) -> i32 {
|
pub fn index_vmmemory_definition_base(&self, index: DefinedMemoryIndex) -> i32 {
|
||||||
self.index_vmmemory(index)
|
self.index_vmmemory_definition(index)
|
||||||
.checked_add(i32::from(self.vmmemory_definition_base()))
|
.checked_add(i32::from(self.vmmemory_definition_base()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `memories` pointer to the `current_length` field in
|
/// Return the offset from the `memories` pointer to the `current_length` field in
|
||||||
/// `VMMemoryDefinition` index `index`.
|
/// `VMMemoryDefinition` index `index`.
|
||||||
pub fn index_vmmemory_definition_current_length(&self, index: u32) -> i32 {
|
pub fn index_vmmemory_definition_current_length(&self, index: DefinedMemoryIndex) -> i32 {
|
||||||
self.index_vmmemory(index)
|
self.index_vmmemory_definition(index)
|
||||||
.checked_add(i32::from(self.vmmemory_definition_current_length()))
|
.checked_add(i32::from(self.vmmemory_definition_current_length()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `memories` pointer to the `from` field in
|
/// Return the offset from the `imported_globals` pointer to the `from` field in
|
||||||
/// `VMMemoryImport` index `index`.
|
/// `VMGlobalImport` index `index`.
|
||||||
pub fn index_vmmemory_import_from(&self, index: u32) -> i32 {
|
pub fn index_vmglobal_import_from(&self, index: GlobalIndex) -> i32 {
|
||||||
self.index_vmmemory(index)
|
self.index_vmglobal_import(index)
|
||||||
.checked_add(i32::from(self.vmmemory_import_from()))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the offset from the `globals` pointer to the `from` field in
|
|
||||||
/// `VMGlobal` index `index`.
|
|
||||||
pub fn index_vmglobal_import_from(&self, index: u32) -> i32 {
|
|
||||||
self.index_vmglobal(index)
|
|
||||||
.checked_add(i32::from(self.vmglobal_import_from()))
|
.checked_add(i32::from(self.vmglobal_import_from()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `tables` pointer to the `base` field in
|
/// Return the offset from the `globals` pointer to the `VMGlobalDefinition`
|
||||||
/// `VMTable` index `index`.
|
/// index `index`.
|
||||||
pub fn index_vmtable_definition_base(&self, index: u32) -> i32 {
|
pub fn index_vmglobal_definition(&self, index: DefinedGlobalIndex) -> i32 {
|
||||||
self.index_vmtable(index)
|
cast::i32(
|
||||||
.checked_add(i32::from(self.vmtable_definition_base()))
|
index
|
||||||
.unwrap()
|
.as_u32()
|
||||||
}
|
.checked_mul(u32::from(self.size_of_vmglobal_definition()))
|
||||||
|
.unwrap(),
|
||||||
/// Return the offset from the `tables` pointer to the `current_elements` field in
|
)
|
||||||
/// `VMTable` index `index`.
|
|
||||||
pub fn index_vmtable_definition_current_elements(&self, index: u32) -> i32 {
|
|
||||||
self.index_vmtable(index)
|
|
||||||
.checked_add(i32::from(self.vmtable_definition_current_elements()))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the offset from the `tables` pointer to the `from` field in
|
|
||||||
/// `VMTableImport` index `index`.
|
|
||||||
pub fn index_vmtable_import_from(&self, index: u32) -> i32 {
|
|
||||||
self.index_vmtable(index)
|
|
||||||
.checked_add(i32::from(self.vmtable_import_from()))
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,10 @@ cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branc
|
|||||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-frontend = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-frontend = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
wasmtime-environ = { path = "../environ" }
|
wasmtime-environ = { path = "../environ" }
|
||||||
|
wasmtime-runtime = { path = "../runtime" }
|
||||||
region = "1.0.0"
|
region = "1.0.0"
|
||||||
lazy_static = "1.2.0"
|
failure = { version = "0.1.3", default-features = false }
|
||||||
libc = { version = "0.2.44", default-features = false }
|
failure_derive = { version = "0.1.3", default-features = false }
|
||||||
errno = "0.2.4"
|
|
||||||
memoffset = "0.2.1"
|
|
||||||
cast = { version = "0.2.2", default-features = false }
|
|
||||||
failure = "0.1.3"
|
|
||||||
failure_derive = "0.1.3"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cmake = "0.1.35"
|
cmake = "0.1.35"
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
//! Memory management for executable code.
|
//! Memory management for executable code.
|
||||||
|
|
||||||
use mmap::Mmap;
|
|
||||||
use region;
|
use region;
|
||||||
use std::cmp;
|
|
||||||
use std::mem;
|
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use vmcontext::VMFunctionBody;
|
use std::{cmp, mem};
|
||||||
|
use wasmtime_runtime::{Mmap, VMFunctionBody};
|
||||||
|
|
||||||
/// Memory manager for executable code.
|
/// Memory manager for executable code.
|
||||||
pub struct Code {
|
pub struct Code {
|
||||||
@@ -47,7 +45,7 @@ impl Code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert mut a slice from u8 to VMFunctionBody.
|
/// Convert mut a slice from u8 to VMFunctionBody.
|
||||||
fn as_mut_vmfunc_slice(slice: &mut [u8]) -> &mut [VMFunctionBody] {
|
fn view_as_mut_vmfunc_slice(slice: &mut [u8]) -> &mut [VMFunctionBody] {
|
||||||
let byte_ptr: *mut [u8] = slice;
|
let byte_ptr: *mut [u8] = slice;
|
||||||
let body_ptr = byte_ptr as *mut [VMFunctionBody];
|
let body_ptr = byte_ptr as *mut [VMFunctionBody];
|
||||||
unsafe { &mut *body_ptr }
|
unsafe { &mut *body_ptr }
|
||||||
@@ -62,7 +60,7 @@ impl Code {
|
|||||||
) -> Result<&mut [VMFunctionBody], String> {
|
) -> Result<&mut [VMFunctionBody], String> {
|
||||||
let new = self.allocate(slice.len())?;
|
let new = self.allocate(slice.len())?;
|
||||||
new.copy_from_slice(slice);
|
new.copy_from_slice(slice);
|
||||||
Ok(Self::as_mut_vmfunc_slice(new))
|
Ok(Self::view_as_mut_vmfunc_slice(new))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make all allocated memory executable.
|
/// Make all allocated memory executable.
|
||||||
|
|||||||
@@ -1,22 +1,29 @@
|
|||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_wasm::Global;
|
use cranelift_wasm::Global;
|
||||||
use vmcontext::{VMFunctionBody, VMGlobal, VMMemory, VMTable};
|
|
||||||
use wasmtime_environ::{MemoryPlan, TablePlan};
|
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||||
|
use wasmtime_runtime::{
|
||||||
|
VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An exported function.
|
||||||
|
pub struct FunctionExport {
|
||||||
|
/// The address of the native-code function.
|
||||||
|
pub address: *const VMFunctionBody,
|
||||||
|
/// The function signature declaration, used for compatibilty checking.
|
||||||
|
pub signature: ir::Signature,
|
||||||
|
}
|
||||||
|
|
||||||
/// The value of an export passed from one instance to another.
|
/// The value of an export passed from one instance to another.
|
||||||
pub enum ExportValue {
|
pub enum Export {
|
||||||
/// A function export value.
|
/// A function export value.
|
||||||
Function {
|
Function(FunctionExport),
|
||||||
/// The address of the native-code function.
|
|
||||||
address: *const VMFunctionBody,
|
|
||||||
/// The function signature declaration, used for compatibilty checking.
|
|
||||||
signature: ir::Signature,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A table export value.
|
/// A table export value.
|
||||||
Table {
|
Table {
|
||||||
/// The address of the table descriptor.
|
/// The address of the table descriptor.
|
||||||
address: *mut VMTable,
|
address: *mut VMTableDefinition,
|
||||||
|
/// Pointer to the containing VMContext.
|
||||||
|
vmctx: *mut VMContext,
|
||||||
/// The table declaration, used for compatibilty checking.
|
/// The table declaration, used for compatibilty checking.
|
||||||
table: TablePlan,
|
table: TablePlan,
|
||||||
},
|
},
|
||||||
@@ -24,7 +31,9 @@ pub enum ExportValue {
|
|||||||
/// A memory export value.
|
/// A memory export value.
|
||||||
Memory {
|
Memory {
|
||||||
/// The address of the memory descriptor.
|
/// The address of the memory descriptor.
|
||||||
address: *mut VMMemory,
|
address: *mut VMMemoryDefinition,
|
||||||
|
/// Pointer to the containing VMContext.
|
||||||
|
vmctx: *mut VMContext,
|
||||||
/// The memory declaration, used for compatibilty checking.
|
/// The memory declaration, used for compatibilty checking.
|
||||||
memory: MemoryPlan,
|
memory: MemoryPlan,
|
||||||
},
|
},
|
||||||
@@ -32,45 +41,57 @@ pub enum ExportValue {
|
|||||||
/// A global export value.
|
/// A global export value.
|
||||||
Global {
|
Global {
|
||||||
/// The address of the global storage.
|
/// The address of the global storage.
|
||||||
address: *mut VMGlobal,
|
address: *mut VMGlobalDefinition,
|
||||||
/// The global declaration, used for compatibilty checking.
|
/// The global declaration, used for compatibilty checking.
|
||||||
global: Global,
|
global: Global,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExportValue {
|
impl Export {
|
||||||
/// Construct a function export value.
|
/// Construct a function export value.
|
||||||
pub fn function(address: *const VMFunctionBody, signature: ir::Signature) -> Self {
|
pub fn function(address: *const VMFunctionBody, signature: ir::Signature) -> Self {
|
||||||
ExportValue::Function { address, signature }
|
Export::Function(FunctionExport { address, signature })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a table export value.
|
/// Construct a table export value.
|
||||||
pub fn table(address: *mut VMTable, table: TablePlan) -> Self {
|
pub fn table(address: *mut VMTableDefinition, vmctx: *mut VMContext, table: TablePlan) -> Self {
|
||||||
ExportValue::Table { address, table }
|
Export::Table {
|
||||||
|
address,
|
||||||
|
vmctx,
|
||||||
|
table,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a memory export value.
|
/// Construct a memory export value.
|
||||||
pub fn memory(address: *mut VMMemory, memory: MemoryPlan) -> Self {
|
pub fn memory(
|
||||||
ExportValue::Memory { address, memory }
|
address: *mut VMMemoryDefinition,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
memory: MemoryPlan,
|
||||||
|
) -> Self {
|
||||||
|
Export::Memory {
|
||||||
|
address,
|
||||||
|
vmctx,
|
||||||
|
memory,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a global export value.
|
/// Construct a global export value.
|
||||||
pub fn global(address: *mut VMGlobal, global: Global) -> Self {
|
pub fn global(address: *mut VMGlobalDefinition, global: Global) -> Self {
|
||||||
ExportValue::Global { address, global }
|
Export::Global { address, global }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import resolver connects imports with available exported values.
|
/// Import resolver connects imports with available exported values.
|
||||||
pub trait Resolver {
|
pub trait Resolver {
|
||||||
/// Resolve the given module/field combo.
|
/// Resolve the given module/field combo.
|
||||||
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue>;
|
fn resolve(&mut self, module: &str, field: &str) -> Option<Export>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Resolver` implementation that always resolves to `None`.
|
/// `Resolver` implementation that always resolves to `None`.
|
||||||
pub struct NullResolver {}
|
pub struct NullResolver {}
|
||||||
|
|
||||||
impl Resolver for NullResolver {
|
impl Resolver for NullResolver {
|
||||||
fn resolve(&mut self, _module: &str, _field: &str) -> Option<ExportValue> {
|
fn resolve(&mut self, _module: &str, _field: &str) -> Option<Export> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
//! Support for reading the value of a wasm global from outside the module.
|
|
||||||
|
|
||||||
use action::{ActionError, RuntimeValue};
|
|
||||||
use cranelift_codegen::ir;
|
|
||||||
use cranelift_entity::EntityRef;
|
|
||||||
use cranelift_wasm::GlobalIndex;
|
|
||||||
use instance::Instance;
|
|
||||||
use wasmtime_environ::{Export, Module};
|
|
||||||
|
|
||||||
/// Reads the value of the named global variable in `module`.
|
|
||||||
pub fn get(
|
|
||||||
module: &Module,
|
|
||||||
instance: &mut Instance,
|
|
||||||
global_name: &str,
|
|
||||||
) -> Result<RuntimeValue, ActionError> {
|
|
||||||
let global_index = match module.exports.get(global_name) {
|
|
||||||
Some(Export::Global(index)) => *index,
|
|
||||||
Some(_) => {
|
|
||||||
return Err(ActionError::Kind(format!(
|
|
||||||
"exported item \"{}\" is not a global",
|
|
||||||
global_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(ActionError::Field(format!(
|
|
||||||
"no export named \"{}\"",
|
|
||||||
global_name
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
get_by_index(module, instance, global_index)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the value of the indexed global variable in `module`.
|
|
||||||
pub fn get_by_index(
|
|
||||||
module: &Module,
|
|
||||||
instance: &mut Instance,
|
|
||||||
global_index: GlobalIndex,
|
|
||||||
) -> Result<RuntimeValue, ActionError> {
|
|
||||||
unsafe {
|
|
||||||
let vmctx = &mut *instance.vmctx();
|
|
||||||
let vmglobal = vmctx.global(global_index);
|
|
||||||
let definition = vmglobal.get_definition(module.is_imported_global(global_index));
|
|
||||||
Ok(
|
|
||||||
match module
|
|
||||||
.globals
|
|
||||||
.get(global_index)
|
|
||||||
.ok_or_else(|| ActionError::Index(global_index.index() as u64))?
|
|
||||||
.ty
|
|
||||||
{
|
|
||||||
ir::types::I32 => RuntimeValue::I32(*definition.as_i32()),
|
|
||||||
ir::types::I64 => RuntimeValue::I64(*definition.as_i64()),
|
|
||||||
ir::types::F32 => RuntimeValue::F32(*definition.as_f32_bits()),
|
|
||||||
ir::types::F64 => RuntimeValue::F64(*definition.as_f64_bits()),
|
|
||||||
other => {
|
|
||||||
return Err(ActionError::Type(format!(
|
|
||||||
"global with type {} not supported",
|
|
||||||
other
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
use cranelift_entity::PrimaryMap;
|
|
||||||
use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
|
|
||||||
use vmcontext::{VMFunctionBody, VMGlobal, VMMemory, VMTable};
|
|
||||||
|
|
||||||
/// Resolved import pointers.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Imports {
|
|
||||||
/// Resolved addresses for imported functions.
|
|
||||||
pub functions: PrimaryMap<FuncIndex, *const VMFunctionBody>,
|
|
||||||
|
|
||||||
/// Resolved addresses for imported tables.
|
|
||||||
pub tables: PrimaryMap<TableIndex, *mut VMTable>,
|
|
||||||
|
|
||||||
/// Resolved addresses for imported globals.
|
|
||||||
pub globals: PrimaryMap<GlobalIndex, *mut VMGlobal>,
|
|
||||||
|
|
||||||
/// Resolved addresses for imported memories.
|
|
||||||
pub memories: PrimaryMap<MemoryIndex, *mut VMMemory>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Imports {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
functions: PrimaryMap::new(),
|
|
||||||
tables: PrimaryMap::new(),
|
|
||||||
globals: PrimaryMap::new(),
|
|
||||||
memories: PrimaryMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
//! An `Instance` contains all the runtime state used by execution of a wasm
|
|
||||||
//! module.
|
|
||||||
|
|
||||||
use cranelift_entity::EntityRef;
|
|
||||||
use cranelift_entity::PrimaryMap;
|
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
|
|
||||||
use imports::Imports;
|
|
||||||
use memory::LinearMemory;
|
|
||||||
use sig_registry::SignatureRegistry;
|
|
||||||
use std::ptr;
|
|
||||||
use std::slice;
|
|
||||||
use std::string::String;
|
|
||||||
use table::Table;
|
|
||||||
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMGlobal, VMMemory, VMTable};
|
|
||||||
use wasmtime_environ::{DataInitializer, Module};
|
|
||||||
|
|
||||||
/// An Instance of a WebAssemby module.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Instance {
|
|
||||||
/// WebAssembly linear memory data.
|
|
||||||
memories: PrimaryMap<MemoryIndex, LinearMemory>,
|
|
||||||
|
|
||||||
/// WebAssembly table data.
|
|
||||||
tables: PrimaryMap<TableIndex, Table>,
|
|
||||||
|
|
||||||
/// Function Signature IDs.
|
|
||||||
/// FIXME: This should be shared across instances rather than per-Instance.
|
|
||||||
sig_registry: SignatureRegistry,
|
|
||||||
|
|
||||||
/// Memory base address vector pointed to by vmctx.
|
|
||||||
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>,
|
|
||||||
|
|
||||||
/// Pointer values for resolved imports.
|
|
||||||
imports: Imports,
|
|
||||||
|
|
||||||
/// Pointers to functions in executable memory.
|
|
||||||
allocated_functions: PrimaryMap<DefinedFuncIndex, (*mut VMFunctionBody, usize)>,
|
|
||||||
|
|
||||||
/// Context pointer used by JIT code.
|
|
||||||
vmctx: VMContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instance {
|
|
||||||
/// Create a new `Instance`. In order to complete instantiation, call
|
|
||||||
/// `invoke_start_function`. `allocated_functions` holds the function bodies
|
|
||||||
/// which have been placed in executable memory.
|
|
||||||
pub fn new(
|
|
||||||
module: &Module,
|
|
||||||
allocated_functions: PrimaryMap<DefinedFuncIndex, (*mut VMFunctionBody, usize)>,
|
|
||||||
data_initializers: &[DataInitializer],
|
|
||||||
imports: Imports,
|
|
||||||
) -> Result<Self, String> {
|
|
||||||
let mut sig_registry = instantiate_signatures(module);
|
|
||||||
let mut memories = instantiate_memories(module, data_initializers)?;
|
|
||||||
let mut tables = instantiate_tables(module, &allocated_functions, &mut sig_registry);
|
|
||||||
|
|
||||||
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();
|
|
||||||
let signature_ids_ptr = sig_registry.vmsignature_ids();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
memories,
|
|
||||||
tables,
|
|
||||||
sig_registry,
|
|
||||||
vmctx_memories,
|
|
||||||
vmctx_globals,
|
|
||||||
vmctx_tables,
|
|
||||||
imports,
|
|
||||||
allocated_functions,
|
|
||||||
vmctx: VMContext::new(
|
|
||||||
vmctx_memories_ptr,
|
|
||||||
vmctx_globals_ptr,
|
|
||||||
vmctx_tables_ptr,
|
|
||||||
signature_ids_ptr,
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the vmctx pointer to be passed into JIT code.
|
|
||||||
pub fn vmctx(&mut self) -> &mut VMContext {
|
|
||||||
&mut self.vmctx
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the offset from the vmctx pointer to its containing Instance.
|
|
||||||
pub(crate) fn vmctx_offset() -> isize {
|
|
||||||
offset_of!(Self, vmctx) as isize
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the pointer to executable memory for the given function index.
|
|
||||||
pub(crate) fn get_allocated_function(
|
|
||||||
&self,
|
|
||||||
index: DefinedFuncIndex,
|
|
||||||
) -> Option<&[VMFunctionBody]> {
|
|
||||||
self.allocated_functions
|
|
||||||
.get(index)
|
|
||||||
.map(|(ptr, len)| unsafe { slice::from_raw_parts(*ptr, *len) })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the pointer to executable memory for the given function index.
|
|
||||||
pub(crate) fn get_imported_function(&self, index: FuncIndex) -> Option<*const VMFunctionBody> {
|
|
||||||
self.imports.functions.get(index).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
pub fn inspect_memory(&self, memory_index: MemoryIndex, address: usize, len: usize) -> &[u8] {
|
|
||||||
&self
|
|
||||||
.memories
|
|
||||||
.get(memory_index)
|
|
||||||
.unwrap_or_else(|| panic!("no memory for index {}", memory_index.index()))
|
|
||||||
.as_ref()[address..address + len]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shows the value of a global variable.
|
|
||||||
pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal {
|
|
||||||
&self.vmctx_globals[global_index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
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,
|
|
||||||
allocated_functions: &PrimaryMap<DefinedFuncIndex, (*mut VMFunctionBody, usize)>,
|
|
||||||
sig_registry: &mut SignatureRegistry,
|
|
||||||
) -> PrimaryMap<TableIndex, Table> {
|
|
||||||
let mut tables = PrimaryMap::with_capacity(module.table_plans.len());
|
|
||||||
for table in module.table_plans.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 = 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 callee_sig = module.functions[*func_idx];
|
|
||||||
let func_ptr = allocated_functions[module
|
|
||||||
.defined_func_index(*func_idx)
|
|
||||||
.expect("table element initializer with imported function not supported yet")]
|
|
||||||
.0;
|
|
||||||
let type_id = sig_registry.lookup(callee_sig);
|
|
||||||
subslice[i] = VMCallerCheckedAnyfunc { func_ptr, type_id };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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
|
|
||||||
}
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
//! Support for invoking wasm functions from outside a wasm module.
|
|
||||||
|
|
||||||
use action::{ActionError, ActionOutcome, RuntimeValue};
|
|
||||||
use code::Code;
|
|
||||||
use cranelift_codegen::ir::InstBuilder;
|
|
||||||
use cranelift_codegen::{binemit, ir, isa, Context};
|
|
||||||
use cranelift_entity::EntityRef;
|
|
||||||
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
|
||||||
use cranelift_wasm::FuncIndex;
|
|
||||||
use instance::Instance;
|
|
||||||
use signalhandlers::{ensure_eager_signal_handlers, ensure_full_signal_handlers, TrapContext};
|
|
||||||
use std::mem;
|
|
||||||
use std::ptr;
|
|
||||||
use std::vec::Vec;
|
|
||||||
use traphandlers::call_wasm;
|
|
||||||
use vmcontext::{VMContext, VMFunctionBody};
|
|
||||||
use wasmtime_environ::{CompileError, Export, Module, RelocSink};
|
|
||||||
|
|
||||||
/// Calls the given named function, passing its return values and returning
|
|
||||||
/// its results.
|
|
||||||
pub fn invoke(
|
|
||||||
code: &mut Code,
|
|
||||||
isa: &isa::TargetIsa,
|
|
||||||
module: &Module,
|
|
||||||
instance: &mut Instance,
|
|
||||||
function: &str,
|
|
||||||
args: &[RuntimeValue],
|
|
||||||
) -> Result<ActionOutcome, ActionError> {
|
|
||||||
let fn_index = match module.exports.get(function) {
|
|
||||||
Some(Export::Function(index)) => *index,
|
|
||||||
Some(_) => {
|
|
||||||
return Err(ActionError::Kind(format!(
|
|
||||||
"exported item \"{}\" is not a function",
|
|
||||||
function
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
return Err(ActionError::Field(format!(
|
|
||||||
"no export named \"{}\"",
|
|
||||||
function
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
invoke_by_index(code, isa, module, instance, fn_index, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invoke the WebAssembly start function of the instance, if one is present.
|
|
||||||
pub fn invoke_start_function(
|
|
||||||
code: &mut Code,
|
|
||||||
isa: &isa::TargetIsa,
|
|
||||||
module: &Module,
|
|
||||||
instance: &mut Instance,
|
|
||||||
) -> Result<ActionOutcome, ActionError> {
|
|
||||||
if let Some(start_index) = module.start_func {
|
|
||||||
invoke_by_index(code, isa, module, instance, start_index, &[])
|
|
||||||
} else {
|
|
||||||
// No start function, just return nothing.
|
|
||||||
Ok(ActionOutcome::Returned { values: vec![] })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls the given indexed function, passing its return values and returning
|
|
||||||
/// its results.
|
|
||||||
pub fn invoke_by_index(
|
|
||||||
code: &mut Code,
|
|
||||||
isa: &isa::TargetIsa,
|
|
||||||
module: &Module,
|
|
||||||
instance: &mut Instance,
|
|
||||||
fn_index: FuncIndex,
|
|
||||||
args: &[RuntimeValue],
|
|
||||||
) -> Result<ActionOutcome, ActionError> {
|
|
||||||
let exec_code_buf = match module.defined_func_index(fn_index) {
|
|
||||||
Some(def_fn_index) => instance
|
|
||||||
.get_allocated_function(def_fn_index)
|
|
||||||
.ok_or_else(|| ActionError::Index(def_fn_index.index() as u64))?
|
|
||||||
.as_ptr(),
|
|
||||||
None => instance
|
|
||||||
.get_imported_function(fn_index)
|
|
||||||
.ok_or_else(|| ActionError::Index(fn_index.index() as u64))?,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 {
|
|
||||||
triedToInstallSignalHandlers: false,
|
|
||||||
haveSignalHandlers: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Rather than writing inline assembly to jump to the code region, we use the fact that
|
|
||||||
// the Rust ABI for calling a function with no arguments and no return values matches the one
|
|
||||||
// of the generated code. Thanks to this, we can transmute the code region into a first-class
|
|
||||||
// Rust function and call it.
|
|
||||||
// Ensure that our signal handlers are ready for action.
|
|
||||||
ensure_eager_signal_handlers();
|
|
||||||
ensure_full_signal_handlers(&mut traps);
|
|
||||||
if !traps.haveSignalHandlers {
|
|
||||||
return Err(ActionError::Resource(
|
|
||||||
"failed to install signal handlers".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
call_through_wrapper(code, isa, exec_code_buf, instance, args, &sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_through_wrapper(
|
|
||||||
code: &mut Code,
|
|
||||||
isa: &isa::TargetIsa,
|
|
||||||
callee: *const VMFunctionBody,
|
|
||||||
instance: &mut Instance,
|
|
||||||
args: &[RuntimeValue],
|
|
||||||
sig: &ir::Signature,
|
|
||||||
) -> Result<ActionOutcome, ActionError> {
|
|
||||||
let vmctx = instance.vmctx() as *mut VMContext;
|
|
||||||
|
|
||||||
for (index, value) in args.iter().enumerate() {
|
|
||||||
assert_eq!(value.value_type(), sig.params[index].value_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
let wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
|
|
||||||
let mut context = Context::new();
|
|
||||||
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
|
|
||||||
|
|
||||||
let value_size = 8;
|
|
||||||
let mut results_vec = Vec::new();
|
|
||||||
results_vec.resize(sig.returns.len(), 0i64);
|
|
||||||
|
|
||||||
let mut fn_builder_ctx = FunctionBuilderContext::new();
|
|
||||||
{
|
|
||||||
let mut builder = FunctionBuilder::new(&mut context.func, &mut fn_builder_ctx);
|
|
||||||
let block0 = builder.create_ebb();
|
|
||||||
|
|
||||||
builder.append_ebb_params_for_function_params(block0);
|
|
||||||
|
|
||||||
builder.switch_to_block(block0);
|
|
||||||
builder.seal_block(block0);
|
|
||||||
|
|
||||||
let mut callee_args = Vec::new();
|
|
||||||
let pointer_type = isa.pointer_type();
|
|
||||||
|
|
||||||
let callee_value = builder.ins().iconst(pointer_type, callee as i64);
|
|
||||||
|
|
||||||
for value in args {
|
|
||||||
match value {
|
|
||||||
RuntimeValue::I32(i) => {
|
|
||||||
callee_args.push(builder.ins().iconst(ir::types::I32, i64::from(*i)))
|
|
||||||
}
|
|
||||||
RuntimeValue::I64(i) => callee_args.push(builder.ins().iconst(ir::types::I64, *i)),
|
|
||||||
RuntimeValue::F32(i) => callee_args.push(
|
|
||||||
builder
|
|
||||||
.ins()
|
|
||||||
.f32const(ir::immediates::Ieee32::with_bits(*i)),
|
|
||||||
),
|
|
||||||
RuntimeValue::F64(i) => callee_args.push(
|
|
||||||
builder
|
|
||||||
.ins()
|
|
||||||
.f64const(ir::immediates::Ieee64::with_bits(*i)),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let vmctx_value = builder.ins().iconst(pointer_type, vmctx as i64);
|
|
||||||
callee_args.push(vmctx_value);
|
|
||||||
|
|
||||||
let new_sig = builder.import_signature(sig.clone());
|
|
||||||
|
|
||||||
// TODO: It's possible to make this a direct call. We just need Cranelift
|
|
||||||
// to support functions declared with an immediate integer address.
|
|
||||||
let call = builder
|
|
||||||
.ins()
|
|
||||||
.call_indirect(new_sig, callee_value, &callee_args);
|
|
||||||
|
|
||||||
let results = builder.func.dfg.inst_results(call).to_vec();
|
|
||||||
|
|
||||||
let results_vec_value = builder
|
|
||||||
.ins()
|
|
||||||
.iconst(pointer_type, results_vec.as_ptr() as i64);
|
|
||||||
|
|
||||||
let mut mflags = ir::MemFlags::new();
|
|
||||||
mflags.set_notrap();
|
|
||||||
mflags.set_aligned();
|
|
||||||
for (i, r) in results.iter().enumerate() {
|
|
||||||
builder
|
|
||||||
.ins()
|
|
||||||
.store(mflags, *r, results_vec_value, (i * value_size) as i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.ins().return_(&[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut code_buf: Vec<u8> = Vec::new();
|
|
||||||
let mut reloc_sink = RelocSink::new();
|
|
||||||
let mut trap_sink = binemit::NullTrapSink {};
|
|
||||||
context
|
|
||||||
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
|
|
||||||
.map_err(|error| ActionError::Compile(CompileError::Codegen(error)))?;
|
|
||||||
assert!(reloc_sink.func_relocs.is_empty());
|
|
||||||
|
|
||||||
let exec_code_buf = code
|
|
||||||
.allocate_copy_of_byte_slice(&code_buf)
|
|
||||||
.map_err(ActionError::Resource)?
|
|
||||||
.as_ptr();
|
|
||||||
code.publish();
|
|
||||||
|
|
||||||
let func: fn() = unsafe { mem::transmute(exec_code_buf) };
|
|
||||||
|
|
||||||
Ok(match call_wasm(func) {
|
|
||||||
Ok(()) => {
|
|
||||||
let mut values = Vec::with_capacity(sig.returns.len());
|
|
||||||
|
|
||||||
for (index, abi_param) in sig.returns.iter().enumerate() {
|
|
||||||
let v = unsafe {
|
|
||||||
let ptr = results_vec.as_ptr().add(index * value_size);
|
|
||||||
|
|
||||||
match abi_param.value_type {
|
|
||||||
ir::types::I32 => RuntimeValue::I32(ptr::read(ptr as *const i32)),
|
|
||||||
ir::types::I64 => RuntimeValue::I64(ptr::read(ptr as *const i64)),
|
|
||||||
ir::types::F32 => RuntimeValue::F32(ptr::read(ptr as *const u32)),
|
|
||||||
ir::types::F64 => RuntimeValue::F64(ptr::read(ptr as *const u64)),
|
|
||||||
other => panic!("unsupported value type {:?}", other),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
values.push(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
ActionOutcome::Returned { values }
|
|
||||||
}
|
|
||||||
Err(message) => ActionOutcome::Trapped { message },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -28,18 +28,12 @@ extern crate cranelift_codegen;
|
|||||||
extern crate cranelift_entity;
|
extern crate cranelift_entity;
|
||||||
extern crate cranelift_frontend;
|
extern crate cranelift_frontend;
|
||||||
extern crate cranelift_wasm;
|
extern crate cranelift_wasm;
|
||||||
extern crate errno;
|
|
||||||
extern crate region;
|
extern crate region;
|
||||||
extern crate wasmtime_environ;
|
extern crate wasmtime_environ;
|
||||||
|
extern crate wasmtime_runtime;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
extern crate libc;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate memoffset;
|
|
||||||
extern crate cast;
|
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure_derive;
|
extern crate failure_derive;
|
||||||
@@ -47,30 +41,13 @@ extern crate failure_derive;
|
|||||||
mod action;
|
mod action;
|
||||||
mod code;
|
mod code;
|
||||||
mod export;
|
mod export;
|
||||||
mod get;
|
|
||||||
mod imports;
|
|
||||||
mod instance;
|
|
||||||
mod invoke;
|
|
||||||
mod libcalls;
|
|
||||||
mod link;
|
mod link;
|
||||||
mod memory;
|
|
||||||
mod mmap;
|
|
||||||
mod sig_registry;
|
|
||||||
mod signalhandlers;
|
|
||||||
mod table;
|
|
||||||
mod traphandlers;
|
|
||||||
mod vmcontext;
|
|
||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
pub use action::{ActionError, ActionOutcome, RuntimeValue};
|
pub use action::{ActionError, ActionOutcome, RuntimeValue};
|
||||||
pub use code::Code;
|
pub use code::Code;
|
||||||
pub use export::{ExportValue, NullResolver, Resolver};
|
pub use export::{Export, NullResolver, Resolver};
|
||||||
pub use get::{get, get_by_index};
|
|
||||||
pub use instance::Instance;
|
|
||||||
pub use invoke::{invoke, invoke_by_index, invoke_start_function};
|
|
||||||
pub use link::link_module;
|
pub use link::link_module;
|
||||||
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
|
||||||
pub use vmcontext::{VMContext, VMFunctionBody, VMGlobal, VMMemory, VMTable};
|
|
||||||
pub use world::InstanceWorld;
|
pub use world::InstanceWorld;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
//! Runtime library calls. Note that the JIT may sometimes perform these inline
|
|
||||||
//! rather than calling them, particularly when CPUs have special instructions
|
|
||||||
//! which compute them directly.
|
|
||||||
|
|
||||||
pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 {
|
|
||||||
x.ceil()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn wasmtime_f32_floor(x: f32) -> f32 {
|
|
||||||
x.floor()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn wasmtime_f32_trunc(x: f32) -> f32 {
|
|
||||||
x.trunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::float_arithmetic, clippy::float_cmp)]
|
|
||||||
pub extern "C" fn wasmtime_f32_nearest(x: f32) -> f32 {
|
|
||||||
// Rust doesn't have a nearest function, so do it manually.
|
|
||||||
if x == 0.0 {
|
|
||||||
// Preserve the sign of zero.
|
|
||||||
x
|
|
||||||
} else {
|
|
||||||
// Nearest is either ceil or floor depending on which is nearest or even.
|
|
||||||
let u = x.ceil();
|
|
||||||
let d = x.floor();
|
|
||||||
let um = (x - u).abs();
|
|
||||||
let dm = (x - d).abs();
|
|
||||||
if um < dm
|
|
||||||
|| (um == dm && {
|
|
||||||
let h = u / 2.;
|
|
||||||
h.floor() == h
|
|
||||||
})
|
|
||||||
{
|
|
||||||
u
|
|
||||||
} else {
|
|
||||||
d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn wasmtime_f64_ceil(x: f64) -> f64 {
|
|
||||||
x.ceil()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn wasmtime_f64_floor(x: f64) -> f64 {
|
|
||||||
x.floor()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "C" fn wasmtime_f64_trunc(x: f64) -> f64 {
|
|
||||||
x.trunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::float_arithmetic, clippy::float_cmp)]
|
|
||||||
pub extern "C" fn wasmtime_f64_nearest(x: f64) -> f64 {
|
|
||||||
// Rust doesn't have a nearest function, so do it manually.
|
|
||||||
if x == 0.0 {
|
|
||||||
// Preserve the sign of zero.
|
|
||||||
x
|
|
||||||
} else {
|
|
||||||
// Nearest is either ceil or floor depending on which is nearest or even.
|
|
||||||
let u = x.ceil();
|
|
||||||
let d = x.floor();
|
|
||||||
let um = (x - u).abs();
|
|
||||||
let dm = (x - d).abs();
|
|
||||||
if um < dm
|
|
||||||
|| (um == dm && {
|
|
||||||
let h = u / 2.;
|
|
||||||
h.floor() == h
|
|
||||||
})
|
|
||||||
{
|
|
||||||
u
|
|
||||||
} else {
|
|
||||||
d
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
use cranelift_codegen::binemit::Reloc;
|
use cranelift_codegen::binemit::Reloc;
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::{
|
use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType};
|
||||||
DefinedFuncIndex, Global, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
use export::{Export, FunctionExport, Resolver};
|
||||||
};
|
|
||||||
use export::{ExportValue, Resolver};
|
|
||||||
use imports::Imports;
|
|
||||||
use std::ptr::write_unaligned;
|
use std::ptr::write_unaligned;
|
||||||
|
use std::string::String;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use vmcontext::VMContext;
|
|
||||||
use vmcontext::{VMFunctionBody, VMGlobal, VMMemory, VMTable};
|
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan,
|
MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan,
|
||||||
TableStyle,
|
TableStyle,
|
||||||
};
|
};
|
||||||
|
use wasmtime_runtime::libcalls;
|
||||||
|
use wasmtime_runtime::{Imports, VMFunctionBody, VMGlobalImport, VMMemoryImport, VMTableImport};
|
||||||
|
|
||||||
/// A link error, such as incompatible or unmatched imports/exports.
|
/// A link error, such as incompatible or unmatched imports/exports.
|
||||||
#[derive(Fail, Debug)]
|
#[derive(Fail, Debug)]
|
||||||
@@ -22,29 +20,28 @@ pub struct LinkError(String);
|
|||||||
/// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`.
|
/// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`.
|
||||||
pub fn link_module(
|
pub fn link_module(
|
||||||
module: &Module,
|
module: &Module,
|
||||||
allocated_functions: &PrimaryMap<DefinedFuncIndex, (*mut VMFunctionBody, usize)>,
|
allocated_functions: &PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||||
relocations: Relocations,
|
relocations: Relocations,
|
||||||
resolver: &mut Resolver,
|
resolver: &mut Resolver,
|
||||||
) -> Result<Imports, LinkError> {
|
) -> Result<Imports, LinkError> {
|
||||||
let mut imports = Imports::new();
|
let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len());
|
||||||
|
|
||||||
for (index, (ref module_name, ref field)) in module.imported_funcs.iter() {
|
for (index, (ref module_name, ref field)) in module.imported_funcs.iter() {
|
||||||
match resolver.resolve(module_name, field) {
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Function { address, signature } => {
|
Export::Function(FunctionExport { address, signature }) => {
|
||||||
let import_signature = &module.signatures[module.functions[index]];
|
let import_signature = &module.signatures[module.functions[index]];
|
||||||
if signature != *import_signature {
|
if signature != *import_signature {
|
||||||
|
// TODO: If the difference is in the calling convention,
|
||||||
|
// we could emit a wrapper function to fix it up.
|
||||||
return Err(LinkError(
|
return Err(LinkError(
|
||||||
format!("{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
format!("{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
||||||
module_name, field,
|
module_name, field,
|
||||||
signature, import_signature)
|
signature, import_signature)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
imports.functions.push(address);
|
function_imports.push(address);
|
||||||
}
|
}
|
||||||
ExportValue::Table { .. }
|
Export::Table { .. } | Export::Memory { .. } | Export::Global { .. } => {
|
||||||
| ExportValue::Memory { .. }
|
|
||||||
| ExportValue::Global { .. } => {
|
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: export not compatible with function import",
|
"{}/{}: export not compatible with function import",
|
||||||
module_name, field
|
module_name, field
|
||||||
@@ -60,41 +57,15 @@ pub fn link_module(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index, (ref module_name, ref field)) in module.imported_globals.iter() {
|
let mut table_imports = PrimaryMap::with_capacity(module.imported_tables.len());
|
||||||
match resolver.resolve(module_name, field) {
|
|
||||||
Some(export_value) => match export_value {
|
|
||||||
ExportValue::Global { address, global } => {
|
|
||||||
let imported_global = module.globals[index];
|
|
||||||
if !is_global_compatible(&global, &imported_global) {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: exported global incompatible with global import",
|
|
||||||
module_name, field
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
imports.globals.push(address as *mut VMGlobal);
|
|
||||||
}
|
|
||||||
ExportValue::Table { .. }
|
|
||||||
| ExportValue::Memory { .. }
|
|
||||||
| ExportValue::Function { .. } => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"{}/{}: exported global incompatible with global import",
|
|
||||||
module_name, field
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
return Err(LinkError(format!(
|
|
||||||
"no provided import global for {}/{}",
|
|
||||||
module_name, field
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (index, (ref module_name, ref field)) in module.imported_tables.iter() {
|
for (index, (ref module_name, ref field)) in module.imported_tables.iter() {
|
||||||
match resolver.resolve(module_name, field) {
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Table { address, table } => {
|
Export::Table {
|
||||||
|
address,
|
||||||
|
vmctx,
|
||||||
|
table,
|
||||||
|
} => {
|
||||||
let import_table = &module.table_plans[index];
|
let import_table = &module.table_plans[index];
|
||||||
if !is_table_compatible(&table, import_table) {
|
if !is_table_compatible(&table, import_table) {
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
@@ -102,11 +73,12 @@ pub fn link_module(
|
|||||||
module_name, field,
|
module_name, field,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
imports.tables.push(address as *mut VMTable);
|
table_imports.push(VMTableImport {
|
||||||
|
from: address,
|
||||||
|
vmctx,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ExportValue::Global { .. }
|
Export::Global { .. } | Export::Memory { .. } | Export::Function { .. } => {
|
||||||
| ExportValue::Memory { .. }
|
|
||||||
| ExportValue::Function { .. } => {
|
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: export not compatible with table import",
|
"{}/{}: export not compatible with table import",
|
||||||
module_name, field
|
module_name, field
|
||||||
@@ -122,10 +94,15 @@ pub fn link_module(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut memory_imports = PrimaryMap::with_capacity(module.imported_memories.len());
|
||||||
for (index, (ref module_name, ref field)) in module.imported_memories.iter() {
|
for (index, (ref module_name, ref field)) in module.imported_memories.iter() {
|
||||||
match resolver.resolve(module_name, field) {
|
match resolver.resolve(module_name, field) {
|
||||||
Some(export_value) => match export_value {
|
Some(export_value) => match export_value {
|
||||||
ExportValue::Memory { address, memory } => {
|
Export::Memory {
|
||||||
|
address,
|
||||||
|
vmctx,
|
||||||
|
memory,
|
||||||
|
} => {
|
||||||
let import_memory = &module.memory_plans[index];
|
let import_memory = &module.memory_plans[index];
|
||||||
if is_memory_compatible(&memory, import_memory) {
|
if is_memory_compatible(&memory, import_memory) {
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
@@ -133,11 +110,12 @@ pub fn link_module(
|
|||||||
module_name, field
|
module_name, field
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
imports.memories.push(address as *mut VMMemory);
|
memory_imports.push(VMMemoryImport {
|
||||||
|
from: address,
|
||||||
|
vmctx,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ExportValue::Table { .. }
|
Export::Table { .. } | Export::Global { .. } | Export::Function { .. } => {
|
||||||
| ExportValue::Global { .. }
|
|
||||||
| ExportValue::Function { .. } => {
|
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: export not compatible with memory import",
|
"{}/{}: export not compatible with memory import",
|
||||||
module_name, field
|
module_name, field
|
||||||
@@ -153,6 +131,43 @@ pub fn link_module(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut global_imports = PrimaryMap::with_capacity(module.imported_globals.len());
|
||||||
|
for (index, (ref module_name, ref field)) in module.imported_globals.iter() {
|
||||||
|
match resolver.resolve(module_name, field) {
|
||||||
|
Some(export_value) => match export_value {
|
||||||
|
Export::Global { address, global } => {
|
||||||
|
let imported_global = module.globals[index];
|
||||||
|
if !is_global_compatible(&global, &imported_global) {
|
||||||
|
return Err(LinkError(format!(
|
||||||
|
"{}/{}: exported global incompatible with global import",
|
||||||
|
module_name, field
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
global_imports.push(VMGlobalImport { from: address });
|
||||||
|
}
|
||||||
|
Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => {
|
||||||
|
return Err(LinkError(format!(
|
||||||
|
"{}/{}: exported global incompatible with global import",
|
||||||
|
module_name, field
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(LinkError(format!(
|
||||||
|
"no provided import global for {}/{}",
|
||||||
|
module_name, field
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let imports = Imports::new(
|
||||||
|
function_imports,
|
||||||
|
table_imports,
|
||||||
|
memory_imports,
|
||||||
|
global_imports,
|
||||||
|
);
|
||||||
|
|
||||||
// Apply relocations, now that we have virtual addresses for everything.
|
// Apply relocations, now that we have virtual addresses for everything.
|
||||||
relocate(&imports, allocated_functions, relocations, &module);
|
relocate(&imports, allocated_functions, relocations, &module);
|
||||||
|
|
||||||
@@ -277,22 +292,27 @@ fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
|
|||||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
||||||
fn relocate(
|
fn relocate(
|
||||||
imports: &Imports,
|
imports: &Imports,
|
||||||
allocated_functions: &PrimaryMap<DefinedFuncIndex, (*mut VMFunctionBody, usize)>,
|
allocated_functions: &PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||||
relocations: PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
relocations: PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
) {
|
) {
|
||||||
for (i, function_relocs) in relocations.into_iter() {
|
for (i, function_relocs) in relocations.into_iter() {
|
||||||
for r in function_relocs {
|
for r in function_relocs {
|
||||||
|
use self::libcalls::*;
|
||||||
let target_func_address: usize = match r.reloc_target {
|
let target_func_address: usize = match r.reloc_target {
|
||||||
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
||||||
Some(f) => allocated_functions[f].0 as usize,
|
Some(f) => {
|
||||||
|
let fatptr: *const [VMFunctionBody] = allocated_functions[f];
|
||||||
|
fatptr as *const VMFunctionBody as usize
|
||||||
|
}
|
||||||
None => imports.functions[index] as usize,
|
None => imports.functions[index] as usize,
|
||||||
},
|
},
|
||||||
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
RelocationTarget::Memory32Grow => wasmtime_memory32_grow as usize,
|
||||||
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
RelocationTarget::Memory32Size => wasmtime_memory32_size as usize,
|
||||||
|
RelocationTarget::ImportedMemory32Grow => wasmtime_imported_memory32_grow as usize,
|
||||||
|
RelocationTarget::ImportedMemory32Size => wasmtime_imported_memory32_size as usize,
|
||||||
RelocationTarget::LibCall(libcall) => {
|
RelocationTarget::LibCall(libcall) => {
|
||||||
use cranelift_codegen::ir::LibCall::*;
|
use cranelift_codegen::ir::LibCall::*;
|
||||||
use libcalls::*;
|
|
||||||
match libcall {
|
match libcall {
|
||||||
CeilF32 => wasmtime_f32_ceil as usize,
|
CeilF32 => wasmtime_f32_ceil as usize,
|
||||||
FloorF32 => wasmtime_f32_floor as usize,
|
FloorF32 => wasmtime_f32_floor as usize,
|
||||||
@@ -308,7 +328,8 @@ fn relocate(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = allocated_functions[i].0;
|
let fatptr: *const [VMFunctionBody] = allocated_functions[i];
|
||||||
|
let body = fatptr as *const VMFunctionBody;
|
||||||
match r.reloc {
|
match r.reloc {
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
Reloc::Abs8 => unsafe {
|
Reloc::Abs8 => unsafe {
|
||||||
@@ -340,21 +361,3 @@ fn relocate(
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn __rust_probestack();
|
pub fn __rust_probestack();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The implementation of memory.grow.
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The implementation of memory.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);
|
|
||||||
|
|
||||||
instance.memory_size(memory_index)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,17 +1,30 @@
|
|||||||
use action::{ActionError, ActionOutcome, RuntimeValue};
|
use action::{ActionError, ActionOutcome, RuntimeValue};
|
||||||
use code::Code;
|
use code::Code;
|
||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::ir::InstBuilder;
|
||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_codegen::Context;
|
||||||
use cranelift_wasm::{DefinedFuncIndex, GlobalIndex, MemoryIndex};
|
use cranelift_codegen::{binemit, ir, isa};
|
||||||
|
use cranelift_entity::{BoxedSlice, EntityRef, PrimaryMap};
|
||||||
|
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||||
|
use cranelift_wasm::{
|
||||||
|
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
|
||||||
|
GlobalIndex, MemoryIndex, TableIndex,
|
||||||
|
};
|
||||||
use export::Resolver;
|
use export::Resolver;
|
||||||
use get::get;
|
|
||||||
use instance::Instance;
|
|
||||||
use invoke::{invoke, invoke_start_function};
|
|
||||||
use link::link_module;
|
use link::link_module;
|
||||||
use std::str;
|
use std::cmp::max;
|
||||||
use vmcontext::{VMFunctionBody, VMGlobal};
|
use std::collections::HashMap;
|
||||||
|
use std::slice;
|
||||||
|
use std::string::String;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use std::{mem, ptr};
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
compile_module, Compilation, CompileError, Module, ModuleEnvironment, Tunables,
|
compile_module, Compilation, CompileError, Export, Module, ModuleEnvironment, RelocSink,
|
||||||
|
Tunables,
|
||||||
|
};
|
||||||
|
use wasmtime_runtime::{
|
||||||
|
wasmtime_call_trampoline, wasmtime_init_eager, wasmtime_init_finish, Instance, VMContext,
|
||||||
|
VMFunctionBody, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport,
|
||||||
|
VMTableDefinition, VMTableImport,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A module, an instance of that module, and accompanying compilation artifacts.
|
/// A module, an instance of that module, and accompanying compilation artifacts.
|
||||||
@@ -20,10 +33,19 @@ use wasmtime_environ::{
|
|||||||
pub struct InstanceWorld {
|
pub struct InstanceWorld {
|
||||||
module: Module,
|
module: Module,
|
||||||
instance: Instance,
|
instance: Instance,
|
||||||
|
|
||||||
|
/// Pointers to functions in executable memory.
|
||||||
|
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
|
||||||
|
|
||||||
|
/// Trampolines for calling into JIT code.
|
||||||
|
trampolines: TrampolinePark,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstanceWorld {
|
impl InstanceWorld {
|
||||||
/// Create a new `InstanceWorld` by compiling the wasm module in `data` and instatiating it.
|
/// Create a new `InstanceWorld` by compiling the wasm module in `data` and instatiating it.
|
||||||
|
///
|
||||||
|
/// `finished_functions` holds the function bodies
|
||||||
|
/// which have been placed in executable memory and linked.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
isa: &isa::TargetIsa,
|
isa: &isa::TargetIsa,
|
||||||
@@ -33,9 +55,6 @@ impl InstanceWorld {
|
|||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
// TODO: Allow the tunables to be overridden.
|
// TODO: Allow the tunables to be overridden.
|
||||||
let tunables = Tunables::default();
|
let tunables = Tunables::default();
|
||||||
let instance = {
|
|
||||||
// TODO: Untie this.
|
|
||||||
let ((mut compilation, relocations), lazy_data_initializers) = {
|
|
||||||
let (lazy_function_body_inputs, lazy_data_initializers) = {
|
let (lazy_function_body_inputs, lazy_data_initializers) = {
|
||||||
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
|
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
|
||||||
|
|
||||||
@@ -49,30 +68,48 @@ impl InstanceWorld {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
let (compilation, relocations) = compile_module(&module, &lazy_function_body_inputs, isa)
|
||||||
compile_module(&module, &lazy_function_body_inputs, isa)
|
.map_err(ActionError::Compile)?;
|
||||||
.map_err(ActionError::Compile)?,
|
|
||||||
lazy_data_initializers,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let allocated_functions =
|
let allocated_functions =
|
||||||
allocate_functions(code, compilation).map_err(ActionError::Resource)?;
|
allocate_functions(code, compilation).map_err(ActionError::Resource)?;
|
||||||
|
|
||||||
let resolved = link_module(&module, &allocated_functions, relocations, resolver)
|
let imports = link_module(&module, &allocated_functions, relocations, resolver)
|
||||||
.map_err(ActionError::Link)?;
|
.map_err(ActionError::Link)?;
|
||||||
|
|
||||||
let mut instance = Instance::new(
|
let finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody> =
|
||||||
|
allocated_functions
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_index, allocated)| {
|
||||||
|
let fatptr: *const [VMFunctionBody] = *allocated;
|
||||||
|
fatptr as *const VMFunctionBody
|
||||||
|
})
|
||||||
|
.collect::<PrimaryMap<_, _>>()
|
||||||
|
.into_boxed_slice();
|
||||||
|
|
||||||
|
let instance = Instance::new(
|
||||||
&module,
|
&module,
|
||||||
allocated_functions,
|
&finished_functions,
|
||||||
|
imports,
|
||||||
&lazy_data_initializers,
|
&lazy_data_initializers,
|
||||||
resolved,
|
|
||||||
)
|
)
|
||||||
.map_err(ActionError::Resource)?;
|
.map_err(ActionError::Resource)?;
|
||||||
|
|
||||||
|
let fn_builder_ctx = FunctionBuilderContext::new();
|
||||||
|
|
||||||
|
let mut result = Self {
|
||||||
|
module,
|
||||||
|
instance,
|
||||||
|
finished_functions,
|
||||||
|
trampolines: TrampolinePark {
|
||||||
|
memo: HashMap::new(),
|
||||||
|
fn_builder_ctx,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// The WebAssembly spec specifies that the start function is
|
// The WebAssembly spec specifies that the start function is
|
||||||
// invoked automatically at instantiation time.
|
// invoked automatically at instantiation time.
|
||||||
match invoke_start_function(code, isa, &module, &mut instance)? {
|
match result.invoke_start_function(code, isa)? {
|
||||||
ActionOutcome::Returned { .. } => {}
|
ActionOutcome::Returned { .. } => {}
|
||||||
ActionOutcome::Trapped { message } => {
|
ActionOutcome::Trapped { message } => {
|
||||||
// Instantiation fails if the start function traps.
|
// Instantiation fails if the start function traps.
|
||||||
@@ -80,10 +117,71 @@ impl InstanceWorld {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instance
|
Ok(result)
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(Self { module, instance })
|
fn get_imported_function(&self, index: FuncIndex) -> Option<*const VMFunctionBody> {
|
||||||
|
if index.index() < self.module.imported_funcs.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().imported_function(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add an accessor for table elements.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn get_imported_table(&self, index: TableIndex) -> Option<&VMTableImport> {
|
||||||
|
if index.index() < self.module.imported_tables.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().imported_table(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_imported_memory(&self, index: MemoryIndex) -> Option<&VMMemoryImport> {
|
||||||
|
if index.index() < self.module.imported_memories.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().imported_memory(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_imported_global(&self, index: GlobalIndex) -> Option<&VMGlobalImport> {
|
||||||
|
if index.index() < self.module.imported_globals.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().imported_global(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_finished_function(&self, index: DefinedFuncIndex) -> Option<*const VMFunctionBody> {
|
||||||
|
self.finished_functions.get(index).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add an accessor for table elements.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn get_defined_table(&self, index: DefinedTableIndex) -> Option<&VMTableDefinition> {
|
||||||
|
if self.module.table_index(index).index() < self.module.table_plans.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().table(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_defined_memory(&self, index: DefinedMemoryIndex) -> Option<&VMMemoryDefinition> {
|
||||||
|
if self.module.memory_index(index).index() < self.module.memory_plans.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().memory(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_defined_global(&self, index: DefinedGlobalIndex) -> Option<&VMGlobalDefinition> {
|
||||||
|
if self.module.global_index(index).index() < self.module.globals.len() {
|
||||||
|
Some(unsafe { self.instance.vmctx().global(index) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke a function in this `InstanceWorld` by name.
|
/// Invoke a function in this `InstanceWorld` by name.
|
||||||
@@ -94,40 +192,362 @@ impl InstanceWorld {
|
|||||||
function_name: &str,
|
function_name: &str,
|
||||||
args: &[RuntimeValue],
|
args: &[RuntimeValue],
|
||||||
) -> Result<ActionOutcome, ActionError> {
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
invoke(
|
let fn_index = match self.module.exports.get(function_name) {
|
||||||
code,
|
Some(Export::Function(index)) => *index,
|
||||||
isa,
|
Some(_) => {
|
||||||
&self.module,
|
return Err(ActionError::Kind(format!(
|
||||||
&mut self.instance,
|
"exported item \"{}\" is not a function",
|
||||||
&function_name,
|
function_name
|
||||||
args,
|
)))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(ActionError::Field(format!(
|
||||||
|
"no export named \"{}\"",
|
||||||
|
function_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.invoke_by_index(code, isa, fn_index, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoke the WebAssembly start function of the instance, if one is present.
|
||||||
|
fn invoke_start_function(
|
||||||
|
&mut self,
|
||||||
|
code: &mut Code,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
|
if let Some(start_index) = self.module.start_func {
|
||||||
|
self.invoke_by_index(code, isa, start_index, &[])
|
||||||
|
} else {
|
||||||
|
// No start function, just return nothing.
|
||||||
|
Ok(ActionOutcome::Returned { values: vec![] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the given indexed function, passing its return values and returning
|
||||||
|
/// its results.
|
||||||
|
fn invoke_by_index(
|
||||||
|
&mut self,
|
||||||
|
code: &mut Code,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
fn_index: FuncIndex,
|
||||||
|
args: &[RuntimeValue],
|
||||||
|
) -> Result<ActionOutcome, ActionError> {
|
||||||
|
let callee_address = match self.module.defined_func_index(fn_index) {
|
||||||
|
Some(def_fn_index) => self
|
||||||
|
.get_finished_function(def_fn_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(def_fn_index.index() as u64))?,
|
||||||
|
None => self
|
||||||
|
.get_imported_function(fn_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(fn_index.index() as u64))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rather than writing inline assembly to jump to the code region, we use the fact that
|
||||||
|
// the Rust ABI for calling a function with no arguments and no return values matches the one
|
||||||
|
// of the generated code. Thanks to this, we can transmute the code region into a first-class
|
||||||
|
// Rust function and call it.
|
||||||
|
// Ensure that our signal handlers are ready for action.
|
||||||
|
wasmtime_init_eager();
|
||||||
|
wasmtime_init_finish(self.instance.vmctx_mut());
|
||||||
|
|
||||||
|
let signature = &self.module.signatures[self.module.functions[fn_index]];
|
||||||
|
let vmctx: *mut VMContext = self.instance.vmctx_mut();
|
||||||
|
|
||||||
|
for (index, value) in args.iter().enumerate() {
|
||||||
|
assert_eq!(value.value_type(), signature.params[index].value_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Support values larger than u64.
|
||||||
|
let mut values_vec: Vec<u64> = Vec::new();
|
||||||
|
let value_size = mem::size_of::<u64>();
|
||||||
|
values_vec.resize(max(signature.params.len(), signature.returns.len()), 0u64);
|
||||||
|
|
||||||
|
// Store the argument values into `values_vec`.
|
||||||
|
for (index, arg) in args.iter().enumerate() {
|
||||||
|
unsafe {
|
||||||
|
let ptr = values_vec.as_mut_ptr().add(index);
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
RuntimeValue::I32(x) => ptr::write(ptr as *mut i32, *x),
|
||||||
|
RuntimeValue::I64(x) => ptr::write(ptr as *mut i64, *x),
|
||||||
|
RuntimeValue::F32(x) => ptr::write(ptr as *mut u32, *x),
|
||||||
|
RuntimeValue::F64(x) => ptr::write(ptr as *mut u64, *x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the vmctx value into `values_vec`.
|
||||||
|
unsafe {
|
||||||
|
let ptr = values_vec.as_mut_ptr().add(args.len());
|
||||||
|
ptr::write(ptr as *mut usize, vmctx as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the trampoline to call for this function.
|
||||||
|
let exec_code_buf =
|
||||||
|
self.trampolines
|
||||||
|
.get(code, isa, callee_address, &signature, value_size)?;
|
||||||
|
|
||||||
|
// Make all JIT code produced thus far executable.
|
||||||
|
code.publish();
|
||||||
|
|
||||||
|
// Call the trampoline.
|
||||||
|
if let Err(message) = unsafe {
|
||||||
|
wasmtime_call_trampoline(
|
||||||
|
exec_code_buf,
|
||||||
|
values_vec.as_mut_ptr() as *mut u8,
|
||||||
|
self.instance.vmctx_mut(),
|
||||||
)
|
)
|
||||||
|
} {
|
||||||
|
return Ok(ActionOutcome::Trapped { message });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the return values out of `values_vec`.
|
||||||
|
let values = signature
|
||||||
|
.returns
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, abi_param)| unsafe {
|
||||||
|
let ptr = values_vec.as_ptr().add(index);
|
||||||
|
|
||||||
|
match abi_param.value_type {
|
||||||
|
ir::types::I32 => RuntimeValue::I32(ptr::read(ptr as *const i32)),
|
||||||
|
ir::types::I64 => RuntimeValue::I64(ptr::read(ptr as *const i64)),
|
||||||
|
ir::types::F32 => RuntimeValue::F32(ptr::read(ptr as *const u32)),
|
||||||
|
ir::types::F64 => RuntimeValue::F64(ptr::read(ptr as *const u64)),
|
||||||
|
other => panic!("unsupported value type {:?}", other),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(ActionOutcome::Returned { values })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a global in this `InstanceWorld` by name.
|
/// Read a global in this `InstanceWorld` by name.
|
||||||
pub fn get(&mut self, global_name: &str) -> Result<RuntimeValue, ActionError> {
|
pub fn get(&self, global_name: &str) -> Result<RuntimeValue, ActionError> {
|
||||||
get(&self.module, &mut self.instance, global_name)
|
let global_index = match self.module.exports.get(global_name) {
|
||||||
|
Some(Export::Global(index)) => *index,
|
||||||
|
Some(_) => {
|
||||||
|
return Err(ActionError::Kind(format!(
|
||||||
|
"exported item \"{}\" is not a global",
|
||||||
|
global_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(ActionError::Field(format!(
|
||||||
|
"no export named \"{}\"",
|
||||||
|
global_name
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.get_by_index(global_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the value of the indexed global variable in `module`.
|
||||||
|
pub fn get_by_index(&self, global_index: GlobalIndex) -> Result<RuntimeValue, ActionError> {
|
||||||
|
let global_address = match self.module.defined_global_index(global_index) {
|
||||||
|
Some(def_global_index) => self
|
||||||
|
.get_defined_global(def_global_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(def_global_index.index() as u64))?,
|
||||||
|
None => {
|
||||||
|
let from: *const VMGlobalDefinition = self
|
||||||
|
.get_imported_global(global_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(global_index.index() as u64))?
|
||||||
|
.from;
|
||||||
|
from
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let global_def = unsafe { &*global_address };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
Ok(
|
||||||
|
match self
|
||||||
|
.module
|
||||||
|
.globals
|
||||||
|
.get(global_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(global_index.index() as u64))?
|
||||||
|
.ty
|
||||||
|
{
|
||||||
|
ir::types::I32 => RuntimeValue::I32(*global_def.as_i32()),
|
||||||
|
ir::types::I64 => RuntimeValue::I64(*global_def.as_i64()),
|
||||||
|
ir::types::F32 => RuntimeValue::F32(*global_def.as_f32_bits()),
|
||||||
|
ir::types::F64 => RuntimeValue::F64(*global_def.as_f64_bits()),
|
||||||
|
other => {
|
||||||
|
return Err(ActionError::Type(format!(
|
||||||
|
"global with type {} not supported",
|
||||||
|
other
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a slice of the contents of allocated linear memory.
|
/// Returns a slice of the contents of allocated linear memory.
|
||||||
pub fn inspect_memory(&self, memory_index: MemoryIndex, address: usize, len: usize) -> &[u8] {
|
pub fn inspect_memory(
|
||||||
self.instance.inspect_memory(memory_index, address, len)
|
&self,
|
||||||
|
memory_index: MemoryIndex,
|
||||||
|
address: usize,
|
||||||
|
len: usize,
|
||||||
|
) -> Result<&[u8], ActionError> {
|
||||||
|
let memory_address = match self.module.defined_memory_index(memory_index) {
|
||||||
|
Some(def_memory_index) => self
|
||||||
|
.get_defined_memory(def_memory_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(def_memory_index.index() as u64))?,
|
||||||
|
None => {
|
||||||
|
let from: *const VMMemoryDefinition = self
|
||||||
|
.get_imported_memory(memory_index)
|
||||||
|
.ok_or_else(|| ActionError::Index(memory_index.index() as u64))?
|
||||||
|
.from;
|
||||||
|
from
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
let memory_def = unsafe { &*memory_address };
|
||||||
|
|
||||||
/// Shows the value of a global variable.
|
Ok(unsafe {
|
||||||
pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal {
|
&slice::from_raw_parts(memory_def.base, memory_def.current_length)
|
||||||
self.instance.inspect_global(global_index)
|
[address..address + len]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_functions(
|
fn allocate_functions(
|
||||||
code: &mut Code,
|
code: &mut Code,
|
||||||
compilation: Compilation,
|
compilation: Compilation,
|
||||||
) -> Result<PrimaryMap<DefinedFuncIndex, (*mut VMFunctionBody, usize)>, String> {
|
) -> Result<PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>, String> {
|
||||||
let mut result = PrimaryMap::with_capacity(compilation.functions.len());
|
let mut result = PrimaryMap::with_capacity(compilation.functions.len());
|
||||||
for (_, body) in compilation.functions.into_iter() {
|
for (_, body) in compilation.functions.into_iter() {
|
||||||
let slice = code.allocate_copy_of_byte_slice(body)?;
|
let fatptr: *mut [VMFunctionBody] = code.allocate_copy_of_byte_slice(body)?;
|
||||||
result.push((slice.as_mut_ptr(), slice.len()));
|
result.push(fatptr);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TrampolinePark {
|
||||||
|
/// Memorized per-function trampolines.
|
||||||
|
memo: HashMap<*const VMFunctionBody, *const VMFunctionBody>,
|
||||||
|
|
||||||
|
/// The `FunctionBuilderContext`, shared between function compilations.
|
||||||
|
fn_builder_ctx: FunctionBuilderContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrampolinePark {
|
||||||
|
fn get(
|
||||||
|
&mut self,
|
||||||
|
code: &mut Code,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
callee_address: *const VMFunctionBody,
|
||||||
|
signature: &ir::Signature,
|
||||||
|
value_size: usize,
|
||||||
|
) -> Result<*const VMFunctionBody, ActionError> {
|
||||||
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
Ok(match self.memo.entry(callee_address) {
|
||||||
|
Occupied(entry) => *entry.get(),
|
||||||
|
Vacant(entry) => {
|
||||||
|
let body = make_trampoline(
|
||||||
|
&mut self.fn_builder_ctx,
|
||||||
|
code,
|
||||||
|
isa,
|
||||||
|
callee_address,
|
||||||
|
signature,
|
||||||
|
value_size,
|
||||||
|
)?;
|
||||||
|
entry.insert(body);
|
||||||
|
body
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_trampoline(
|
||||||
|
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||||
|
code: &mut Code,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
callee_address: *const VMFunctionBody,
|
||||||
|
signature: &ir::Signature,
|
||||||
|
value_size: usize,
|
||||||
|
) -> Result<*const VMFunctionBody, ActionError> {
|
||||||
|
let pointer_type = isa.pointer_type();
|
||||||
|
let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
|
||||||
|
|
||||||
|
// Add the `values_vec` parameter.
|
||||||
|
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||||
|
// Add the `vmctx` parameter.
|
||||||
|
wrapper_sig.params.push(ir::AbiParam::special(
|
||||||
|
pointer_type,
|
||||||
|
ir::ArgumentPurpose::VMContext,
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut context = Context::new();
|
||||||
|
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
|
||||||
|
let block0 = builder.create_ebb();
|
||||||
|
|
||||||
|
builder.append_ebb_params_for_function_params(block0);
|
||||||
|
builder.switch_to_block(block0);
|
||||||
|
builder.seal_block(block0);
|
||||||
|
|
||||||
|
let mut callee_args = Vec::new();
|
||||||
|
let pointer_type = isa.pointer_type();
|
||||||
|
|
||||||
|
let (values_vec_ptr_val, vmctx_ptr_val) = {
|
||||||
|
let params = builder.func.dfg.ebb_params(block0);
|
||||||
|
(params[0], params[1])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the argument values out of `values_vec`.
|
||||||
|
let mflags = ir::MemFlags::trusted();
|
||||||
|
for (i, r) in signature.params.iter().enumerate() {
|
||||||
|
let value = match r.purpose {
|
||||||
|
ir::ArgumentPurpose::Normal => builder.ins().load(
|
||||||
|
r.value_type,
|
||||||
|
mflags,
|
||||||
|
values_vec_ptr_val,
|
||||||
|
(i * value_size) as i32,
|
||||||
|
),
|
||||||
|
ir::ArgumentPurpose::VMContext => vmctx_ptr_val,
|
||||||
|
other => panic!("unsupported argument purpose {}", other),
|
||||||
|
};
|
||||||
|
callee_args.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_sig = builder.import_signature(signature.clone());
|
||||||
|
|
||||||
|
// TODO: It's possible to make this a direct call. We just need Cranelift
|
||||||
|
// to support functions declared with an immediate integer address.
|
||||||
|
// ExternalName::Absolute(u64). Let's do it.
|
||||||
|
let callee_value = builder.ins().iconst(pointer_type, callee_address as i64);
|
||||||
|
let call = builder
|
||||||
|
.ins()
|
||||||
|
.call_indirect(new_sig, callee_value, &callee_args);
|
||||||
|
|
||||||
|
let results = builder.func.dfg.inst_results(call).to_vec();
|
||||||
|
|
||||||
|
// Store the return values into `values_vec`.
|
||||||
|
let mflags = ir::MemFlags::trusted();
|
||||||
|
for (i, r) in results.iter().enumerate() {
|
||||||
|
builder
|
||||||
|
.ins()
|
||||||
|
.store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.ins().return_(&[]);
|
||||||
|
builder.finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut code_buf: Vec<u8> = Vec::new();
|
||||||
|
let mut reloc_sink = RelocSink::new();
|
||||||
|
let mut trap_sink = binemit::NullTrapSink {};
|
||||||
|
context
|
||||||
|
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
|
||||||
|
.map_err(|error| ActionError::Compile(CompileError::Codegen(error)))?;
|
||||||
|
assert!(reloc_sink.func_relocs.is_empty());
|
||||||
|
|
||||||
|
Ok(code
|
||||||
|
.allocate_copy_of_byte_slice(&code_buf)
|
||||||
|
.map_err(ActionError::Resource)?
|
||||||
|
.as_ptr())
|
||||||
|
}
|
||||||
|
|||||||
38
lib/runtime/Cargo.toml
Normal file
38
lib/runtime/Cargo.toml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasmtime-runtime"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["The Cranelift Project Developers"]
|
||||||
|
publish = false
|
||||||
|
description = "Runtime library support for Wasmtime"
|
||||||
|
categories = ["wasm"]
|
||||||
|
repository = "https://github.com/CraneStation/wasmtime"
|
||||||
|
license = "Apache-2.0 WITH LLVM-exception"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
|
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
|
wasmtime-environ = { path = "../environ" }
|
||||||
|
region = "1.0.0"
|
||||||
|
lazy_static = "1.2.0"
|
||||||
|
libc = { version = "0.2.44", default-features = false }
|
||||||
|
errno = "0.2.4"
|
||||||
|
memoffset = "0.2.1"
|
||||||
|
cast = { version = "0.2.2", default-features = false }
|
||||||
|
failure = { version = "0.1.3", default-features = false }
|
||||||
|
failure_derive = { version = "0.1.3", default-features = false }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cmake = "0.1.35"
|
||||||
|
bindgen = "0.44.0"
|
||||||
|
regex = "1.0.6"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = ["cranelift-codegen/std", "cranelift-wasm/std"]
|
||||||
|
core = ["cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core"]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "experimental" }
|
||||||
|
travis-ci = { repository = "CraneStation/wasmtime" }
|
||||||
220
lib/runtime/LICENSE
Normal file
220
lib/runtime/LICENSE
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
--- LLVM Exceptions to the Apache 2.0 License ----
|
||||||
|
|
||||||
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
|
of this Software are embedded into an Object form of such source code, you
|
||||||
|
may redistribute such embedded portions in such Object form without complying
|
||||||
|
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||||
|
|
||||||
|
In addition, if you combine or link compiled forms of this Software with
|
||||||
|
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||||
|
court of competent jurisdiction determines that the patent provision (Section
|
||||||
|
3), the indemnity provision (Section 9) or other Section of the License
|
||||||
|
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||||
|
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||||
|
the License, but only in their entirety and only with respect to the Combined
|
||||||
|
Software.
|
||||||
|
|
||||||
7
lib/runtime/README.md
Normal file
7
lib/runtime/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
This is the `wasmtime-runtime` crate, which contains wasm runtime library
|
||||||
|
support, supporting the wasm ABI used by [`wasmtime-environ`],
|
||||||
|
[`wasmtime-execute`], and [`wasmtime-obj`].
|
||||||
|
|
||||||
|
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ
|
||||||
|
[`wasmtime-execute`]: https://crates.io/crates/wasmtime-execute
|
||||||
|
[`wasmtime-obj`]: https://crates.io/crates/wasmtime-obj
|
||||||
@@ -702,7 +702,7 @@ EnsureEagerSignalHandlers()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(USE_APPLE_MACH_PORTS)
|
#elif defined(USE_APPLE_MACH_PORTS)
|
||||||
// All the Mach setup in EnsureLazyProcessSignalHandlers.
|
// All the Mach setup in EnsureDarwinMachPorts.
|
||||||
#else
|
#else
|
||||||
// SA_ONSTACK allows us to handle signals on an alternate stack, so that
|
// SA_ONSTACK allows us to handle signals on an alternate stack, so that
|
||||||
// the handler can run in response to running out of stack space on the
|
// the handler can run in response to running out of stack space on the
|
||||||
46
lib/runtime/src/imports.rs
Normal file
46
lib/runtime/src/imports.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use cranelift_entity::{BoxedSlice, PrimaryMap};
|
||||||
|
use cranelift_wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
|
||||||
|
use vmcontext::{VMFunctionBody, VMGlobalImport, VMMemoryImport, VMTableImport};
|
||||||
|
|
||||||
|
/// Resolved import pointers.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Imports {
|
||||||
|
/// Resolved addresses for imported functions.
|
||||||
|
pub functions: BoxedSlice<FuncIndex, *const VMFunctionBody>,
|
||||||
|
|
||||||
|
/// Resolved addresses for imported tables.
|
||||||
|
pub tables: BoxedSlice<TableIndex, VMTableImport>,
|
||||||
|
|
||||||
|
/// Resolved addresses for imported memories.
|
||||||
|
pub memories: BoxedSlice<MemoryIndex, VMMemoryImport>,
|
||||||
|
|
||||||
|
/// Resolved addresses for imported globals.
|
||||||
|
pub globals: BoxedSlice<GlobalIndex, VMGlobalImport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Imports {
|
||||||
|
/// Construct a new `Imports` instance.
|
||||||
|
pub fn new(
|
||||||
|
function_imports: PrimaryMap<FuncIndex, *const VMFunctionBody>,
|
||||||
|
table_imports: PrimaryMap<TableIndex, VMTableImport>,
|
||||||
|
memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>,
|
||||||
|
global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
functions: function_imports.into_boxed_slice(),
|
||||||
|
tables: table_imports.into_boxed_slice(),
|
||||||
|
memories: memory_imports.into_boxed_slice(),
|
||||||
|
globals: global_imports.into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a new `Imports` instance with no imports.
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
functions: PrimaryMap::new().into_boxed_slice(),
|
||||||
|
tables: PrimaryMap::new().into_boxed_slice(),
|
||||||
|
memories: PrimaryMap::new().into_boxed_slice(),
|
||||||
|
globals: PrimaryMap::new().into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
256
lib/runtime/src/instance.rs
Normal file
256
lib/runtime/src/instance.rs
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
//! An `Instance` contains all the runtime state used by execution of a wasm
|
||||||
|
//! module.
|
||||||
|
|
||||||
|
use cranelift_entity::EntityRef;
|
||||||
|
use cranelift_entity::{BoxedSlice, PrimaryMap};
|
||||||
|
use cranelift_wasm::{
|
||||||
|
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
|
||||||
|
};
|
||||||
|
use imports::Imports;
|
||||||
|
use memory::LinearMemory;
|
||||||
|
use sig_registry::SignatureRegistry;
|
||||||
|
use std::string::String;
|
||||||
|
use table::Table;
|
||||||
|
use vmcontext::{
|
||||||
|
VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition,
|
||||||
|
VMTableDefinition,
|
||||||
|
};
|
||||||
|
use wasmtime_environ::{DataInitializer, Module};
|
||||||
|
|
||||||
|
/// An Instance of a WebAssemby module.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Instance {
|
||||||
|
/// WebAssembly linear memory data.
|
||||||
|
memories: BoxedSlice<DefinedMemoryIndex, LinearMemory>,
|
||||||
|
|
||||||
|
/// WebAssembly table data.
|
||||||
|
tables: BoxedSlice<DefinedTableIndex, Table>,
|
||||||
|
|
||||||
|
/// Function Signature IDs.
|
||||||
|
/// FIXME: This should be shared across instances rather than per-Instance.
|
||||||
|
sig_registry: SignatureRegistry,
|
||||||
|
|
||||||
|
/// Resolved imports.
|
||||||
|
vmctx_imports: Imports,
|
||||||
|
|
||||||
|
/// Table storage base address vector pointed to by vmctx.
|
||||||
|
vmctx_tables: BoxedSlice<DefinedTableIndex, VMTableDefinition>,
|
||||||
|
|
||||||
|
/// Memory base address vector pointed to by vmctx.
|
||||||
|
vmctx_memories: BoxedSlice<DefinedMemoryIndex, VMMemoryDefinition>,
|
||||||
|
|
||||||
|
/// WebAssembly global variable data.
|
||||||
|
vmctx_globals: BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition>,
|
||||||
|
|
||||||
|
/// Context pointer used by JIT code.
|
||||||
|
vmctx: VMContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instance {
|
||||||
|
/// Create a new `Instance`.
|
||||||
|
pub fn new(
|
||||||
|
module: &Module,
|
||||||
|
finished_functions: &BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
|
||||||
|
mut vmctx_imports: Imports,
|
||||||
|
data_initializers: &[DataInitializer],
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
let mut sig_registry = instantiate_signatures(module);
|
||||||
|
let mut memories = instantiate_memories(module, data_initializers)?;
|
||||||
|
let mut tables = instantiate_tables(
|
||||||
|
module,
|
||||||
|
finished_functions,
|
||||||
|
&vmctx_imports.functions,
|
||||||
|
&mut sig_registry,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut vmctx_memories = memories
|
||||||
|
.values_mut()
|
||||||
|
.map(LinearMemory::vmmemory)
|
||||||
|
.collect::<PrimaryMap<DefinedMemoryIndex, _>>()
|
||||||
|
.into_boxed_slice();
|
||||||
|
|
||||||
|
let mut vmctx_globals = instantiate_globals(module);
|
||||||
|
|
||||||
|
let mut vmctx_tables = tables
|
||||||
|
.values_mut()
|
||||||
|
.map(Table::vmtable)
|
||||||
|
.collect::<PrimaryMap<DefinedTableIndex, _>>()
|
||||||
|
.into_boxed_slice();
|
||||||
|
|
||||||
|
let vmctx_imported_functions_ptr = vmctx_imports
|
||||||
|
.functions
|
||||||
|
.values_mut()
|
||||||
|
.into_slice()
|
||||||
|
.as_mut_ptr();
|
||||||
|
let vmctx_imported_tables_ptr = vmctx_imports.tables.values_mut().into_slice().as_mut_ptr();
|
||||||
|
let vmctx_imported_memories_ptr = vmctx_imports
|
||||||
|
.memories
|
||||||
|
.values_mut()
|
||||||
|
.into_slice()
|
||||||
|
.as_mut_ptr();
|
||||||
|
let vmctx_imported_globals_ptr =
|
||||||
|
vmctx_imports.globals.values_mut().into_slice().as_mut_ptr();
|
||||||
|
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();
|
||||||
|
let vmctx_shared_signatures_ptr = sig_registry.vmshared_signatures();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
memories,
|
||||||
|
tables,
|
||||||
|
sig_registry,
|
||||||
|
vmctx_imports,
|
||||||
|
vmctx_memories,
|
||||||
|
vmctx_globals,
|
||||||
|
vmctx_tables,
|
||||||
|
vmctx: VMContext::new(
|
||||||
|
vmctx_imported_functions_ptr,
|
||||||
|
vmctx_imported_tables_ptr,
|
||||||
|
vmctx_imported_memories_ptr,
|
||||||
|
vmctx_imported_globals_ptr,
|
||||||
|
vmctx_tables_ptr,
|
||||||
|
vmctx_memories_ptr,
|
||||||
|
vmctx_globals_ptr,
|
||||||
|
vmctx_shared_signatures_ptr,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the vmctx used by JIT code.
|
||||||
|
pub fn vmctx(&self) -> &VMContext {
|
||||||
|
&self.vmctx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the vmctx used by JIT code.
|
||||||
|
pub fn vmctx_mut(&mut self) -> &mut VMContext {
|
||||||
|
&mut self.vmctx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the vmctx pointer to its containing Instance.
|
||||||
|
pub(crate) fn vmctx_offset() -> isize {
|
||||||
|
offset_of!(Self, vmctx) as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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: DefinedMemoryIndex, 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: DefinedMemoryIndex) -> u32 {
|
||||||
|
self.memories
|
||||||
|
.get(memory_index)
|
||||||
|
.unwrap_or_else(|| panic!("no memory for index {}", memory_index.index()))
|
||||||
|
.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test whether any of the objects inside this instance require signal
|
||||||
|
/// handlers to catch out of bounds accesses.
|
||||||
|
pub(crate) fn needs_signal_handlers(&self) -> bool {
|
||||||
|
self.memories
|
||||||
|
.values()
|
||||||
|
.any(|memory| memory.needs_signal_handlers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of imported memories.
|
||||||
|
pub(crate) fn num_imported_memories(&self) -> usize {
|
||||||
|
self.vmctx_imports.functions.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 tables of the current module.
|
||||||
|
fn instantiate_tables(
|
||||||
|
module: &Module,
|
||||||
|
finished_functions: &BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
|
||||||
|
imported_functions: &BoxedSlice<FuncIndex, *const VMFunctionBody>,
|
||||||
|
sig_registry: &mut SignatureRegistry,
|
||||||
|
) -> BoxedSlice<DefinedTableIndex, Table> {
|
||||||
|
let num_imports = module.imported_memories.len();
|
||||||
|
let mut tables: PrimaryMap<DefinedTableIndex, _> =
|
||||||
|
PrimaryMap::with_capacity(module.table_plans.len() - num_imports);
|
||||||
|
for table in &module.table_plans.values().as_slice()[num_imports..] {
|
||||||
|
tables.push(Table::new(table));
|
||||||
|
}
|
||||||
|
|
||||||
|
for init in &module.table_elements {
|
||||||
|
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
|
||||||
|
let defined_table_index = module
|
||||||
|
.defined_table_index(init.table_index)
|
||||||
|
.expect("Initializers for imported tables not supported yet");
|
||||||
|
let slice = tables[defined_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 callee_sig = module.functions[*func_idx];
|
||||||
|
let func_ptr = if let Some(index) = module.defined_func_index(*func_idx) {
|
||||||
|
finished_functions[index]
|
||||||
|
} else {
|
||||||
|
imported_functions[*func_idx]
|
||||||
|
};
|
||||||
|
let type_index = sig_registry.lookup(callee_sig);
|
||||||
|
subslice[i] = VMCallerCheckedAnyfunc {
|
||||||
|
func_ptr,
|
||||||
|
type_index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tables.into_boxed_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate memory for just the memories of the current module.
|
||||||
|
fn instantiate_memories(
|
||||||
|
module: &Module,
|
||||||
|
data_initializers: &[DataInitializer],
|
||||||
|
) -> Result<BoxedSlice<DefinedMemoryIndex, LinearMemory>, String> {
|
||||||
|
let num_imports = module.imported_memories.len();
|
||||||
|
let mut memories: PrimaryMap<DefinedMemoryIndex, _> =
|
||||||
|
PrimaryMap::with_capacity(module.memory_plans.len() - num_imports);
|
||||||
|
for plan in &module.memory_plans.values().as_slice()[num_imports..] {
|
||||||
|
memories.push(LinearMemory::new(&plan)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
for init in data_initializers {
|
||||||
|
debug_assert!(init.base.is_none(), "globalvar base not supported yet");
|
||||||
|
let defined_memory_index = module
|
||||||
|
.defined_memory_index(init.memory_index)
|
||||||
|
.expect("Initializers for imported memories not supported yet");
|
||||||
|
let mem_mut = memories[defined_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.into_boxed_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate memory for just the globals of the current module,
|
||||||
|
/// without any initializers applied yet.
|
||||||
|
fn instantiate_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition> {
|
||||||
|
let num_imports = module.imported_globals.len();
|
||||||
|
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
|
||||||
|
|
||||||
|
for global in &module.globals.values().as_slice()[num_imports..] {
|
||||||
|
vmctx_globals.push(VMGlobalDefinition::new(global));
|
||||||
|
}
|
||||||
|
|
||||||
|
vmctx_globals.into_boxed_slice()
|
||||||
|
}
|
||||||
70
lib/runtime/src/lib.rs
Normal file
70
lib/runtime/src/lib.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//! Runtime library support for Wasmtime.
|
||||||
|
|
||||||
|
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||||
|
#![warn(unused_import_braces)]
|
||||||
|
#![cfg_attr(feature = "std", deny(unstable_features))]
|
||||||
|
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||||
|
#![cfg_attr(
|
||||||
|
feature = "cargo-clippy",
|
||||||
|
allow(clippy::new_without_default, clippy::new_without_default_derive)
|
||||||
|
)]
|
||||||
|
#![cfg_attr(
|
||||||
|
feature = "cargo-clippy",
|
||||||
|
warn(
|
||||||
|
clippy::float_arithmetic,
|
||||||
|
clippy::mut_mut,
|
||||||
|
clippy::nonminimal_bool,
|
||||||
|
clippy::option_map_unwrap_or,
|
||||||
|
clippy::option_map_unwrap_or_else,
|
||||||
|
clippy::print_stdout,
|
||||||
|
clippy::unicode_not_nfc,
|
||||||
|
clippy::use_self
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||||
|
|
||||||
|
extern crate cranelift_codegen;
|
||||||
|
extern crate cranelift_entity;
|
||||||
|
extern crate cranelift_wasm;
|
||||||
|
extern crate errno;
|
||||||
|
extern crate region;
|
||||||
|
extern crate wasmtime_environ;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate alloc;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
extern crate libc;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate memoffset;
|
||||||
|
extern crate cast;
|
||||||
|
|
||||||
|
mod imports;
|
||||||
|
mod instance;
|
||||||
|
mod memory;
|
||||||
|
mod mmap;
|
||||||
|
mod sig_registry;
|
||||||
|
mod signalhandlers;
|
||||||
|
mod table;
|
||||||
|
mod traphandlers;
|
||||||
|
mod vmcontext;
|
||||||
|
|
||||||
|
pub mod libcalls;
|
||||||
|
|
||||||
|
pub use imports::Imports;
|
||||||
|
pub use instance::Instance;
|
||||||
|
pub use mmap::Mmap;
|
||||||
|
pub use signalhandlers::{wasmtime_init_eager, wasmtime_init_finish};
|
||||||
|
pub use traphandlers::wasmtime_call_trampoline;
|
||||||
|
pub use vmcontext::{
|
||||||
|
VMContext, VMFunctionBody, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition,
|
||||||
|
VMMemoryImport, VMTableDefinition, VMTableImport,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
mod std {
|
||||||
|
pub use alloc::{string, vec};
|
||||||
|
pub use core::*;
|
||||||
|
pub use core::{i32, str, u32};
|
||||||
|
}
|
||||||
157
lib/runtime/src/libcalls.rs
Normal file
157
lib/runtime/src/libcalls.rs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
//! Runtime library calls. Note that the JIT may sometimes perform these inline
|
||||||
|
//! rather than calling them, particularly when CPUs have special instructions
|
||||||
|
//! which compute them directly.
|
||||||
|
|
||||||
|
use cranelift_wasm::{DefinedMemoryIndex, MemoryIndex};
|
||||||
|
use vmcontext::VMContext;
|
||||||
|
|
||||||
|
/// Implementation of f32.ceil
|
||||||
|
pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 {
|
||||||
|
x.ceil()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f32.floor
|
||||||
|
pub extern "C" fn wasmtime_f32_floor(x: f32) -> f32 {
|
||||||
|
x.floor()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f32.trunc
|
||||||
|
pub extern "C" fn wasmtime_f32_trunc(x: f32) -> f32 {
|
||||||
|
x.trunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f32.nearest
|
||||||
|
#[allow(clippy::float_arithmetic, clippy::float_cmp)]
|
||||||
|
pub extern "C" fn wasmtime_f32_nearest(x: f32) -> f32 {
|
||||||
|
// Rust doesn't have a nearest function, so do it manually.
|
||||||
|
if x == 0.0 {
|
||||||
|
// Preserve the sign of zero.
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
// Nearest is either ceil or floor depending on which is nearest or even.
|
||||||
|
let u = x.ceil();
|
||||||
|
let d = x.floor();
|
||||||
|
let um = (x - u).abs();
|
||||||
|
let dm = (x - d).abs();
|
||||||
|
if um < dm
|
||||||
|
|| (um == dm && {
|
||||||
|
let h = u / 2.;
|
||||||
|
h.floor() == h
|
||||||
|
})
|
||||||
|
{
|
||||||
|
u
|
||||||
|
} else {
|
||||||
|
d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f64.ceil
|
||||||
|
pub extern "C" fn wasmtime_f64_ceil(x: f64) -> f64 {
|
||||||
|
x.ceil()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f64.floor
|
||||||
|
pub extern "C" fn wasmtime_f64_floor(x: f64) -> f64 {
|
||||||
|
x.floor()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f64.trunc
|
||||||
|
pub extern "C" fn wasmtime_f64_trunc(x: f64) -> f64 {
|
||||||
|
x.trunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of f64.nearest
|
||||||
|
#[allow(clippy::float_arithmetic, clippy::float_cmp)]
|
||||||
|
pub extern "C" fn wasmtime_f64_nearest(x: f64) -> f64 {
|
||||||
|
// Rust doesn't have a nearest function, so do it manually.
|
||||||
|
if x == 0.0 {
|
||||||
|
// Preserve the sign of zero.
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
// Nearest is either ceil or floor depending on which is nearest or even.
|
||||||
|
let u = x.ceil();
|
||||||
|
let d = x.floor();
|
||||||
|
let um = (x - u).abs();
|
||||||
|
let dm = (x - d).abs();
|
||||||
|
if um < dm
|
||||||
|
|| (um == dm && {
|
||||||
|
let h = u / 2.;
|
||||||
|
h.floor() == h
|
||||||
|
})
|
||||||
|
{
|
||||||
|
u
|
||||||
|
} else {
|
||||||
|
d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of memory.grow for locally-defined 32-bit memories.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasmtime_memory32_grow(
|
||||||
|
delta: u32,
|
||||||
|
memory_index: u32,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
) -> u32 {
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||||
|
|
||||||
|
instance
|
||||||
|
.memory_grow(memory_index, delta)
|
||||||
|
.unwrap_or(u32::max_value())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of memory.grow for imported 32-bit memories.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasmtime_imported_memory32_grow(
|
||||||
|
delta: u32,
|
||||||
|
memory_index: u32,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
) -> u32 {
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
assert!(
|
||||||
|
(memory_index as usize) < instance.num_imported_memories(),
|
||||||
|
"imported memory index for memory.grow out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||||
|
let import = instance.vmctx_mut().imported_memory_mut(memory_index);
|
||||||
|
let foreign_instance = (&mut *import.vmctx).instance();
|
||||||
|
let foreign_memory = &mut *import.from;
|
||||||
|
let foreign_index = foreign_instance.vmctx().memory_index(foreign_memory);
|
||||||
|
|
||||||
|
foreign_instance
|
||||||
|
.memory_grow(foreign_index, delta)
|
||||||
|
.unwrap_or(u32::max_value())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of memory.size for locally-defined 32-bit memories.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasmtime_memory32_size(memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||||
|
|
||||||
|
instance.memory_size(memory_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of memory.size for imported 32-bit memories.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn wasmtime_imported_memory32_size(
|
||||||
|
memory_index: u32,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
) -> u32 {
|
||||||
|
let instance = (&mut *vmctx).instance();
|
||||||
|
assert!(
|
||||||
|
(memory_index as usize) < instance.num_imported_memories(),
|
||||||
|
"imported memory index for memory.grow out of bounds"
|
||||||
|
);
|
||||||
|
|
||||||
|
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||||
|
let import = instance.vmctx_mut().imported_memory_mut(memory_index);
|
||||||
|
let foreign_instance = (&mut *import.vmctx).instance();
|
||||||
|
let foreign_memory = &mut *import.from;
|
||||||
|
let foreign_index = foreign_instance.vmctx().memory_index(foreign_memory);
|
||||||
|
|
||||||
|
foreign_instance.memory_size(foreign_index)
|
||||||
|
}
|
||||||
@@ -5,20 +5,32 @@
|
|||||||
use mmap::Mmap;
|
use mmap::Mmap;
|
||||||
use region;
|
use region;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use vmcontext::VMMemory;
|
use vmcontext::VMMemoryDefinition;
|
||||||
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)]
|
#[derive(Debug)]
|
||||||
pub struct LinearMemory {
|
pub struct LinearMemory {
|
||||||
|
// The underlying allocation.
|
||||||
mmap: Mmap,
|
mmap: Mmap,
|
||||||
|
|
||||||
|
// The current logical size in wasm pages of this linear memory.
|
||||||
current: u32,
|
current: u32,
|
||||||
|
|
||||||
|
// The optional maximum size in wasm pages of this linear memory.
|
||||||
maximum: Option<u32>,
|
maximum: Option<u32>,
|
||||||
|
|
||||||
|
// Size in bytes of extra guard pages after the end to optimize loads and stores with
|
||||||
|
// constant offsets.
|
||||||
offset_guard_size: usize,
|
offset_guard_size: usize,
|
||||||
|
|
||||||
|
// Records whether we're using a bounds-checking strategy which requires
|
||||||
|
// handlers to catch trapping accesses.
|
||||||
|
pub(crate) needs_signal_handlers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinearMemory {
|
impl LinearMemory {
|
||||||
/// Create a new linear memory instance with specified minimum and maximum number of pages.
|
/// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
|
||||||
pub fn new(plan: &MemoryPlan) -> Result<Self, String> {
|
pub fn new(plan: &MemoryPlan) -> Result<Self, String> {
|
||||||
// `maximum` cannot be set to more than `65536` pages.
|
// `maximum` cannot be set to more than `65536` pages.
|
||||||
assert!(plan.memory.minimum <= WASM_MAX_PAGES);
|
assert!(plan.memory.minimum <= WASM_MAX_PAGES);
|
||||||
@@ -26,6 +38,15 @@ impl LinearMemory {
|
|||||||
|
|
||||||
let offset_guard_bytes = plan.offset_guard_size as usize;
|
let offset_guard_bytes = plan.offset_guard_size as usize;
|
||||||
|
|
||||||
|
// If we have an offset guard, or if we're doing the static memory
|
||||||
|
// allocation strategy, we need signal handlers to catch out of bounds
|
||||||
|
// acceses.
|
||||||
|
let needs_signal_handlers = offset_guard_bytes > 0
|
||||||
|
|| match plan.style {
|
||||||
|
MemoryStyle::Dynamic => false,
|
||||||
|
MemoryStyle::Static { .. } => true,
|
||||||
|
};
|
||||||
|
|
||||||
let minimum_pages = match plan.style {
|
let minimum_pages = match plan.style {
|
||||||
MemoryStyle::Dynamic => plan.memory.minimum,
|
MemoryStyle::Dynamic => plan.memory.minimum,
|
||||||
MemoryStyle::Static { bound } => {
|
MemoryStyle::Static { bound } => {
|
||||||
@@ -58,6 +79,7 @@ impl LinearMemory {
|
|||||||
current: plan.memory.minimum,
|
current: plan.memory.minimum,
|
||||||
maximum: plan.memory.maximum,
|
maximum: plan.memory.maximum,
|
||||||
offset_guard_size: offset_guard_bytes,
|
offset_guard_size: offset_guard_bytes,
|
||||||
|
needs_signal_handlers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +88,10 @@ impl LinearMemory {
|
|||||||
self.current
|
self.current
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grow memory by the specified amount of pages.
|
/// Grow memory by the specified amount of wasm pages.
|
||||||
///
|
///
|
||||||
/// Returns `None` if memory can't be grown by the specified amount
|
/// Returns `None` if memory can't be grown by the specified amount
|
||||||
/// of pages.
|
/// of wasm pages.
|
||||||
pub fn grow(&mut self, delta: u32) -> Option<u32> {
|
pub fn grow(&mut self, delta: u32) -> Option<u32> {
|
||||||
let new_pages = match self.current.checked_add(delta) {
|
let new_pages = match self.current.checked_add(delta) {
|
||||||
Some(new_pages) => new_pages,
|
Some(new_pages) => new_pages,
|
||||||
@@ -124,9 +146,12 @@ impl LinearMemory {
|
|||||||
Some(prev_pages)
|
Some(prev_pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a `VMMemory` for exposing the memory to JIT code.
|
/// Return a `VMMemoryDefinition` for exposing the memory to JIT code.
|
||||||
pub fn vmmemory(&mut self) -> VMMemory {
|
pub fn vmmemory(&mut self) -> VMMemoryDefinition {
|
||||||
VMMemory::definition(self.mmap.as_mut_ptr(), self.mmap.len())
|
VMMemoryDefinition {
|
||||||
|
base: self.mmap.as_mut_ptr(),
|
||||||
|
current_length: self.mmap.len(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ pub struct Mmap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mmap {
|
impl Mmap {
|
||||||
|
/// Construct a new empty instance of `Mmap`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ptr: ptr::null_mut(),
|
ptr: ptr::null_mut(),
|
||||||
@@ -55,6 +56,8 @@ impl Mmap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Mmap` pointing to at least `size` bytes of memory,
|
||||||
|
/// suitably sized and aligned for memory protection.
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn with_size(size: usize) -> Result<Self, String> {
|
pub fn with_size(size: usize) -> Result<Self, String> {
|
||||||
use winapi::um::memoryapi::VirtualAlloc;
|
use winapi::um::memoryapi::VirtualAlloc;
|
||||||
@@ -81,22 +84,27 @@ impl Mmap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the allocated memory as a slice of u8.
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
unsafe { slice::from_raw_parts(self.ptr, self.len) }
|
unsafe { slice::from_raw_parts(self.ptr, self.len) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the allocated memory as a mutable slice of u8.
|
||||||
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.ptr, self.len) }
|
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the allocated memory as a pointer to u8.
|
||||||
pub fn as_ptr(&self) -> *const u8 {
|
pub fn as_ptr(&self) -> *const u8 {
|
||||||
self.ptr
|
self.ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the allocated memory as a mutable pointer to u8.
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||||
self.ptr
|
self.ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the lengthof the allocated memory.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
@@ -6,47 +6,50 @@ use cranelift_codegen::ir;
|
|||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::SignatureIndex;
|
use cranelift_wasm::SignatureIndex;
|
||||||
use std::collections::{hash_map, HashMap};
|
use std::collections::{hash_map, HashMap};
|
||||||
use vmcontext::VMSignatureId;
|
use vmcontext::VMSharedSignatureIndex;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SignatureRegistry {
|
pub struct SignatureRegistry {
|
||||||
signature_hash: HashMap<ir::Signature, VMSignatureId>,
|
signature_hash: HashMap<ir::Signature, VMSharedSignatureIndex>,
|
||||||
signature_ids: PrimaryMap<SignatureIndex, VMSignatureId>,
|
shared_signatures: PrimaryMap<SignatureIndex, VMSharedSignatureIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureRegistry {
|
impl SignatureRegistry {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
signature_hash: HashMap::new(),
|
signature_hash: HashMap::new(),
|
||||||
signature_ids: PrimaryMap::new(),
|
shared_signatures: PrimaryMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vmsignature_ids(&mut self) -> *mut VMSignatureId {
|
pub fn vmshared_signatures(&mut self) -> *mut VMSharedSignatureIndex {
|
||||||
self.signature_ids.values_mut().into_slice().as_mut_ptr()
|
self.shared_signatures
|
||||||
|
.values_mut()
|
||||||
|
.into_slice()
|
||||||
|
.as_mut_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register the given signature.
|
/// Register the given signature.
|
||||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) {
|
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) {
|
||||||
// TODO: Refactor this interface so that we're not passing in redundant
|
// TODO: Refactor this interface so that we're not passing in redundant
|
||||||
// information.
|
// information.
|
||||||
debug_assert_eq!(sig_index.index(), self.signature_ids.len());
|
debug_assert_eq!(sig_index.index(), self.shared_signatures.len());
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
|
|
||||||
let len = self.signature_hash.len();
|
let len = self.signature_hash.len();
|
||||||
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
||||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||||
hash_map::Entry::Vacant(entry) => {
|
hash_map::Entry::Vacant(entry) => {
|
||||||
let sig_id = cast::u32(len).unwrap();
|
let sig_id = VMSharedSignatureIndex::new(cast::u32(len).unwrap());
|
||||||
entry.insert(sig_id);
|
entry.insert(sig_id);
|
||||||
sig_id
|
sig_id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.signature_ids.push(sig_id);
|
self.shared_signatures.push(sig_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the identifying runtime index for the given signature.
|
/// Return the identifying runtime index for the given signature.
|
||||||
pub fn lookup(&mut self, sig_index: SignatureIndex) -> VMSignatureId {
|
pub fn lookup(&mut self, sig_index: SignatureIndex) -> VMSharedSignatureIndex {
|
||||||
self.signature_ids[sig_index]
|
self.shared_signatures[sig_index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,9 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use std::borrow::{Borrow, BorrowMut};
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
use vmcontext::VMContext;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/signalhandlers.rs"));
|
include!(concat!(env!("OUT_DIR"), "/signalhandlers.rs"));
|
||||||
|
|
||||||
@@ -36,7 +38,8 @@ lazy_static! {
|
|||||||
/// called at the end of the startup process, after other handlers have been
|
/// called at the end of the startup process, after other handlers have been
|
||||||
/// installed. This function can thus be called multiple times, having no effect
|
/// installed. This function can thus be called multiple times, having no effect
|
||||||
/// after the first call.
|
/// after the first call.
|
||||||
pub fn ensure_eager_signal_handlers() {
|
#[no_mangle]
|
||||||
|
pub extern "C" fn wasmtime_init_eager() {
|
||||||
let mut locked = EAGER_INSTALL_STATE.write().unwrap();
|
let mut locked = EAGER_INSTALL_STATE.write().unwrap();
|
||||||
let state = locked.borrow_mut();
|
let state = locked.borrow_mut();
|
||||||
|
|
||||||
@@ -54,6 +57,49 @@ pub fn ensure_eager_signal_handlers() {
|
|||||||
state.success = true;
|
state.success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static TRAP_CONTEXT: RefCell<TrapContext> = RefCell::new(TrapContext { triedToInstallSignalHandlers: false, haveSignalHandlers: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assuming `EnsureEagerProcessSignalHandlers` has already been called,
|
||||||
|
/// this function performs the full installation of signal handlers which must
|
||||||
|
/// be performed per-thread. This operation may incur some overhead and
|
||||||
|
/// so should be done only when needed to use wasm.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn wasmtime_init_finish(vmctx: &mut VMContext) {
|
||||||
|
if !TRAP_CONTEXT.with(|cx| cx.borrow().triedToInstallSignalHandlers) {
|
||||||
|
TRAP_CONTEXT.with(|cx| {
|
||||||
|
cx.borrow_mut().triedToInstallSignalHandlers = true;
|
||||||
|
assert!(!cx.borrow().haveSignalHandlers);
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
let locked = EAGER_INSTALL_STATE.read().unwrap();
|
||||||
|
let state = locked.borrow();
|
||||||
|
assert!(
|
||||||
|
state.tried,
|
||||||
|
"call wasmtime_init_eager before calling wasmtime_init_finish"
|
||||||
|
);
|
||||||
|
if !state.success {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
ensure_darwin_mach_ports();
|
||||||
|
|
||||||
|
TRAP_CONTEXT.with(|cx| {
|
||||||
|
cx.borrow_mut().haveSignalHandlers = true;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let instance = unsafe { vmctx.instance() };
|
||||||
|
let have_signal_handlers = TRAP_CONTEXT.with(|cx| cx.borrow().haveSignalHandlers);
|
||||||
|
if !have_signal_handlers && instance.needs_signal_handlers() {
|
||||||
|
panic!("failed to install signal handlers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
fn ensure_darwin_mach_ports() {
|
fn ensure_darwin_mach_ports() {
|
||||||
let mut locked = LAZY_INSTALL_STATE.write().unwrap();
|
let mut locked = LAZY_INSTALL_STATE.write().unwrap();
|
||||||
@@ -72,30 +118,3 @@ fn ensure_darwin_mach_ports() {
|
|||||||
|
|
||||||
state.success = true;
|
state.success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assuming `EnsureEagerProcessSignalHandlers` has already been called,
|
|
||||||
/// this function performs the full installation of signal handlers which must
|
|
||||||
/// be performed per-thread. This operation may incur some overhead and
|
|
||||||
/// so should be done only when needed to use wasm.
|
|
||||||
pub fn ensure_full_signal_handlers(cx: &mut TrapContext) {
|
|
||||||
if cx.triedToInstallSignalHandlers {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.triedToInstallSignalHandlers = true;
|
|
||||||
assert!(!cx.haveSignalHandlers);
|
|
||||||
|
|
||||||
{
|
|
||||||
let locked = EAGER_INSTALL_STATE.read().unwrap();
|
|
||||||
let state = locked.borrow();
|
|
||||||
assert!(state.tried);
|
|
||||||
if !state.success {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
||||||
ensure_darwin_mach_ports();
|
|
||||||
|
|
||||||
cx.haveSignalHandlers = true;
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
|
//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
|
||||||
|
|
||||||
use cranelift_wasm::TableElementType;
|
use cranelift_wasm::TableElementType;
|
||||||
use vmcontext::{VMCallerCheckedAnyfunc, VMTable};
|
use vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
|
||||||
use wasmtime_environ::{TablePlan, TableStyle};
|
use wasmtime_environ::{TablePlan, TableStyle};
|
||||||
|
|
||||||
/// A table instance.
|
/// A table instance.
|
||||||
@@ -39,9 +39,12 @@ impl Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a `VMTable` for exposing the table to JIT code.
|
/// Return a `VMTableDefinition` for exposing the table to JIT code.
|
||||||
pub fn vmtable(&mut self) -> VMTable {
|
pub fn vmtable(&mut self) -> VMTableDefinition {
|
||||||
VMTable::definition(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
VMTableDefinition {
|
||||||
|
base: self.vec.as_mut_ptr() as *mut u8,
|
||||||
|
current_elements: self.vec.len(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ use std::cell::{Cell, RefCell};
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
use vmcontext::{VMContext, VMFunctionBody};
|
||||||
|
|
||||||
// Currently we uset setjmp/longjmp to unwind out of a signal handler
|
// Currently we uset setjmp/longjmp to unwind out of a signal handler
|
||||||
// and back to the point where WebAssembly was called (via `call_wasm`).
|
// and back to the point where WebAssembly was called (via `call_wasm`).
|
||||||
@@ -81,22 +82,28 @@ impl Drop for ScopeGuard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call the wasm function poined to by `f`.
|
/// Call the wasm function pointed to by `callee`. `values_vec` points to
|
||||||
pub fn call_wasm<F>(f: F) -> Result<(), String>
|
/// a buffer which holds the incoming arguments, and to which the outgoing
|
||||||
where
|
/// return values will be written.
|
||||||
F: FnOnce(),
|
#[no_mangle]
|
||||||
{
|
pub unsafe extern "C" fn wasmtime_call_trampoline(
|
||||||
|
callee: *const VMFunctionBody,
|
||||||
|
values_vec: *mut u8,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
) -> Result<(), String> {
|
||||||
// In case wasm code calls Rust that panics and unwinds past this point,
|
// In case wasm code calls Rust that panics and unwinds past this point,
|
||||||
// ensure that JMP_BUFS is unwound to its incoming state.
|
// ensure that JMP_BUFS is unwound to its incoming state.
|
||||||
let _guard = ScopeGuard::new();
|
let _guard = ScopeGuard::new();
|
||||||
|
|
||||||
|
let func: fn(*mut u8, *mut VMContext) = mem::transmute(callee);
|
||||||
|
|
||||||
JMP_BUFS.with(|bufs| {
|
JMP_BUFS.with(|bufs| {
|
||||||
let mut buf = unsafe { mem::uninitialized() };
|
let mut buf = mem::uninitialized();
|
||||||
if unsafe { setjmp(&mut buf) } != 0 {
|
if setjmp(&mut buf) != 0 {
|
||||||
return TRAP_DATA.with(|data| Err(format!("wasm trap at {:?}", data.get().pc)));
|
return TRAP_DATA.with(|data| Err(format!("wasm trap at {:?}", data.get().pc)));
|
||||||
}
|
}
|
||||||
bufs.borrow_mut().push(buf);
|
bufs.borrow_mut().push(buf);
|
||||||
f();
|
func(values_vec, vmctx);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,12 @@
|
|||||||
//! fields that JIT code accesses directly.
|
//! fields that JIT code accesses directly.
|
||||||
|
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_wasm::{Global, GlobalIndex, GlobalInit, MemoryIndex, TableIndex};
|
use cranelift_wasm::{
|
||||||
|
DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex, Global, GlobalIndex,
|
||||||
|
GlobalInit, MemoryIndex, TableIndex,
|
||||||
|
};
|
||||||
use instance::Instance;
|
use instance::Instance;
|
||||||
use std::fmt;
|
use std::{mem, ptr, u32};
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
/// A placeholder byte-sized type which is just used to provide some amount of type
|
/// A placeholder byte-sized type which is just used to provide some amount of type
|
||||||
/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's
|
/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's
|
||||||
@@ -25,6 +27,107 @@ mod test_vmfunction_body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub from: *mut VMTableDefinition,
|
||||||
|
|
||||||
|
/// A pointer to the VMContext that owns the table description.
|
||||||
|
pub vmctx: *mut VMContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMTableImport, vmctx),
|
||||||
|
usize::from(offsets.vmtable_import_vmctx())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub from: *mut VMMemoryDefinition,
|
||||||
|
|
||||||
|
/// A pointer to the VMContext that owns the memory description.
|
||||||
|
pub vmctx: *mut VMContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMMemoryImport, vmctx),
|
||||||
|
usize::from(offsets.vmmemory_import_vmctx())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub 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 fields a JIT needs to access to utilize a WebAssembly linear
|
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||||
/// memory defined within the instance, namely the start address and the
|
/// memory defined within the instance, namely the start address and the
|
||||||
/// size in bytes.
|
/// size in bytes.
|
||||||
@@ -32,9 +135,10 @@ mod test_vmfunction_body {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMMemoryDefinition {
|
pub struct VMMemoryDefinition {
|
||||||
/// The start address.
|
/// The start address.
|
||||||
base: *mut u8,
|
pub base: *mut u8,
|
||||||
/// The current size of linear memory in bytes.
|
|
||||||
current_length: usize,
|
/// The current logical size of this linear memory in bytes.
|
||||||
|
pub current_length: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -58,104 +162,51 @@ mod test_vmmemory_definition {
|
|||||||
offset_of!(VMMemoryDefinition, current_length),
|
offset_of!(VMMemoryDefinition, current_length),
|
||||||
usize::from(offsets.vmmemory_definition_current_length())
|
usize::from(offsets.vmmemory_definition_current_length())
|
||||||
);
|
);
|
||||||
|
/* TODO: Assert that the size of `current_length` matches.
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMMemoryDefinition::current_length>(),
|
||||||
|
usize::from(offsets.size_of_vmmemory_definition_current_length())
|
||||||
|
);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
/// The fields a JIT needs to access to utilize a WebAssembly table
|
||||||
/// memory imported from another instance.
|
/// defined within the instance.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMMemoryImport {
|
pub struct VMTableDefinition {
|
||||||
/// A pointer to the imported memory description.
|
/// Pointer to the table data.
|
||||||
from: *mut VMMemoryDefinition,
|
pub base: *mut u8,
|
||||||
|
|
||||||
|
/// The current number of elements in the table.
|
||||||
|
pub current_elements: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_vmmemory_import {
|
mod test_vmtable_definition {
|
||||||
use super::VMMemoryImport;
|
use super::VMTableDefinition;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use wasmtime_environ::VMOffsets;
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_vmmemory_import_offsets() {
|
fn check_vmtable_definition_offsets() {
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMMemoryImport>(),
|
size_of::<VMTableDefinition>(),
|
||||||
usize::from(offsets.size_of_vmmemory_import())
|
usize::from(offsets.size_of_vmtable_definition())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
offset_of!(VMMemoryImport, from),
|
offset_of!(VMTableDefinition, base),
|
||||||
usize::from(offsets.vmmemory_import_from())
|
usize::from(offsets.vmtable_definition_base())
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)]
|
|
||||||
mod test_vmmemory {
|
|
||||||
use super::VMMemory;
|
|
||||||
use std::mem::size_of;
|
|
||||||
use wasmtime_environ::VMOffsets;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_vmmemory_offsets() {
|
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMMemory>(),
|
offset_of!(VMTableDefinition, current_elements),
|
||||||
usize::from(offsets.size_of_vmmemory())
|
usize::from(offsets.vmtable_definition_current_elements())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VMMemory {
|
|
||||||
/// Construct a `VMMemoryDefinition` variant of `VMMemory`.
|
|
||||||
pub fn definition(base: *mut u8, current_length: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
definition: VMMemoryDefinition {
|
|
||||||
base,
|
|
||||||
current_length,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a `VMMemoryImmport` variant of `VMMemory`.
|
|
||||||
pub fn import(from: *mut VMMemoryDefinition) -> Self {
|
|
||||||
Self {
|
|
||||||
import: VMMemoryImport { from },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
/// The storage for a WebAssembly global defined within the instance.
|
||||||
///
|
///
|
||||||
/// TODO: Pack the globals more densely, rather than using the same size
|
/// TODO: Pack the globals more densely, rather than using the same size
|
||||||
@@ -192,277 +243,118 @@ mod test_vmglobal_definition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VMGlobalDefinition {
|
impl VMGlobalDefinition {
|
||||||
|
/// Construct a `VMGlobalDefinition`.
|
||||||
|
pub fn new(global: &Global) -> Self {
|
||||||
|
let mut result = Self { storage: [0; 8] };
|
||||||
|
match global.initializer {
|
||||||
|
GlobalInit::I32Const(x) => *unsafe { result.as_i32_mut() } = x,
|
||||||
|
GlobalInit::I64Const(x) => *unsafe { result.as_i64_mut() } = x,
|
||||||
|
GlobalInit::F32Const(x) => *unsafe { result.as_f32_bits_mut() } = x,
|
||||||
|
GlobalInit::F64Const(x) => *unsafe { result.as_f64_bits_mut() } = x,
|
||||||
|
GlobalInit::GetGlobal(_x) => unimplemented!("globals init with get_global"),
|
||||||
|
GlobalInit::Import => panic!("attempting to initialize imported global"),
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as an i32.
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe fn as_i32(&mut self) -> &mut i32 {
|
pub unsafe fn as_i32(&self) -> &i32 {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const u8 as *const i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as an i32.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_i32_mut(&mut self) -> &mut i32 {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i32)
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as an i64.
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe fn as_i64(&mut self) -> &mut i64 {
|
pub unsafe fn as_i64(&self) -> &i64 {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const u8 as *const i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as an i64.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_i64_mut(&mut self) -> &mut i64 {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i64)
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as an f32.
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe fn as_f32(&mut self) -> &mut f32 {
|
pub unsafe fn as_f32(&self) -> &f32 {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const u8 as *const f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as an f32.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_f32_mut(&mut self) -> &mut f32 {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f32)
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as f32 bits.
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe fn as_f32_bits(&mut self) -> &mut u32 {
|
pub unsafe fn as_f32_bits(&self) -> &u32 {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const u8 as *const u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as f32 bits.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u32)
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as an f64.
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe fn as_f64(&mut self) -> &mut f64 {
|
pub unsafe fn as_f64(&self) -> &f64 {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const u8 as *const f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as an f64.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_f64_mut(&mut self) -> &mut f64 {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f64)
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to the value as f64 bits.
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
pub unsafe fn as_f64_bits(&mut self) -> &mut u64 {
|
pub unsafe fn as_f64_bits(&self) -> &u64 {
|
||||||
|
&*(self.storage.as_ref().as_ptr() as *const u8 as *const u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to the value as f64 bits.
|
||||||
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
|
pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 {
|
||||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *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
|
/// An index into the shared signature registry, usable for checking signatures
|
||||||
/// variable imported from another instance.
|
/// at indirect calls.
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMGlobalImport {
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
/// A pointer to the imported global variable description.
|
pub struct VMSharedSignatureIndex(u32);
|
||||||
from: *mut VMGlobalDefinition,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_vmglobal_import {
|
mod test_vmshared_signature_index {
|
||||||
use super::VMGlobalImport;
|
use super::VMSharedSignatureIndex;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use wasmtime_environ::VMOffsets;
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_import_offsets() {
|
fn check_vmshared_signature_index() {
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMGlobalImport>(),
|
size_of::<VMSharedSignatureIndex>(),
|
||||||
usize::from(offsets.size_of_vmglobal_import())
|
usize::from(offsets.size_of_vmshared_signature_index())
|
||||||
);
|
|
||||||
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
|
impl VMSharedSignatureIndex {
|
||||||
/// variable. It must know whether the global variable is defined within the
|
pub fn new(value: u32) -> Self {
|
||||||
/// instance or imported.
|
VMSharedSignatureIndex(value)
|
||||||
#[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);
|
|
||||||
assert_eq!(
|
|
||||||
size_of::<VMGlobal>(),
|
|
||||||
usize::from(offsets.size_of_vmglobal())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VMGlobal {
|
|
||||||
/// Construct a `VMGlobalDefinition` variant of `VMGlobal`.
|
|
||||||
pub fn definition(global: &Global) -> Self {
|
|
||||||
let mut result = VMGlobalDefinition { storage: [0; 8] };
|
|
||||||
match global.initializer {
|
|
||||||
GlobalInit::I32Const(x) => *unsafe { result.as_i32() } = x,
|
|
||||||
GlobalInit::I64Const(x) => *unsafe { result.as_i64() } = x,
|
|
||||||
GlobalInit::F32Const(x) => *unsafe { result.as_f32_bits() } = x,
|
|
||||||
GlobalInit::F64Const(x) => *unsafe { 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 VMTableDefinition {
|
|
||||||
base: *mut u8,
|
|
||||||
current_elements: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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)]
|
|
||||||
mod test_vmtable {
|
|
||||||
use super::VMTable;
|
|
||||||
use std::mem::size_of;
|
|
||||||
use wasmtime_environ::VMOffsets;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VMTable {
|
|
||||||
/// Construct a `VMTableDefinition` variant of `VMTable`.
|
|
||||||
pub fn definition(base: *mut u8, current_elements: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
definition: VMTableDefinition {
|
|
||||||
base,
|
|
||||||
current_elements,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a `VMTableImmport` variant of `VMTable`.
|
|
||||||
pub fn import(from: *mut VMTableDefinition) -> Self {
|
|
||||||
Self {
|
|
||||||
import: VMTableImport { from },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of the `type_id` field in `VMCallerCheckedAnyfunc`.
|
|
||||||
pub type VMSignatureId = u32;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_vmsignature_id {
|
|
||||||
use super::VMSignatureId;
|
|
||||||
use std::mem::size_of;
|
|
||||||
use wasmtime_environ::VMOffsets;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_vmcaller_checked_anyfunc_offsets() {
|
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
|
||||||
assert_eq!(
|
|
||||||
size_of::<VMSignatureId>(),
|
|
||||||
usize::from(offsets.size_of_vmsignature_id())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +365,7 @@ mod test_vmsignature_id {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMCallerCheckedAnyfunc {
|
pub struct VMCallerCheckedAnyfunc {
|
||||||
pub func_ptr: *const VMFunctionBody,
|
pub func_ptr: *const VMFunctionBody,
|
||||||
pub type_id: VMSignatureId,
|
pub type_index: VMSharedSignatureIndex,
|
||||||
// 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!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,8 +387,8 @@ mod test_vmcaller_checked_anyfunc {
|
|||||||
usize::from(offsets.vmcaller_checked_anyfunc_func_ptr())
|
usize::from(offsets.vmcaller_checked_anyfunc_func_ptr())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
offset_of!(VMCallerCheckedAnyfunc, type_id),
|
offset_of!(VMCallerCheckedAnyfunc, type_index),
|
||||||
usize::from(offsets.vmcaller_checked_anyfunc_type_id())
|
usize::from(offsets.vmcaller_checked_anyfunc_type_index())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,7 +397,7 @@ impl Default for VMCallerCheckedAnyfunc {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
func_ptr: ptr::null_mut(),
|
func_ptr: ptr::null_mut(),
|
||||||
type_id: 0,
|
type_index: VMSharedSignatureIndex::new(u32::MAX),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,16 +412,32 @@ impl Default for VMCallerCheckedAnyfunc {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMContext {
|
pub struct VMContext {
|
||||||
/// A pointer to an array of `VMMemory` instances, indexed by
|
/// A pointer to an array of `*const VMFunctionBody` instances, indexed by `FuncIndex`.
|
||||||
/// WebAssembly memory index.
|
imported_functions: *const *const VMFunctionBody,
|
||||||
memories: *mut VMMemory,
|
|
||||||
/// A pointer to an array of globals.
|
/// A pointer to an array of `VMTableImport` instances, indexed by `TableIndex`.
|
||||||
globals: *mut VMGlobal,
|
imported_tables: *mut VMTableImport,
|
||||||
/// A pointer to an array of `VMTable` instances, indexed by
|
|
||||||
/// WebAssembly table index.
|
/// A pointer to an array of `VMMemoryImport` instances, indexed by `MemoryIndex`.
|
||||||
tables: *mut VMTable,
|
imported_memories: *mut VMMemoryImport,
|
||||||
|
|
||||||
|
/// A pointer to an array of `VMGlobalImport` instances, indexed by `GlobalIndex`.
|
||||||
|
imported_globals: *mut VMGlobalImport,
|
||||||
|
|
||||||
|
/// A pointer to an array of locally-defined `VMTableDefinition` instances,
|
||||||
|
/// indexed by `DefinedTableIndex`.
|
||||||
|
tables: *mut VMTableDefinition,
|
||||||
|
|
||||||
|
/// A pointer to an array of locally-defined `VMMemoryDefinition` instances,
|
||||||
|
/// indexed by `DefinedMemoryIndex`.
|
||||||
|
memories: *mut VMMemoryDefinition,
|
||||||
|
|
||||||
|
/// A pointer to an array of locally-defined `VMGlobalDefinition` instances,
|
||||||
|
/// indexed by `DefinedGlobalIndex`.
|
||||||
|
globals: *mut VMGlobalDefinition,
|
||||||
|
|
||||||
/// Signature identifiers for signature-checking indirect calls.
|
/// Signature identifiers for signature-checking indirect calls.
|
||||||
signature_ids: *mut u32,
|
signature_ids: *mut VMSharedSignatureIndex,
|
||||||
// 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!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,32 +473,90 @@ mod test {
|
|||||||
impl VMContext {
|
impl VMContext {
|
||||||
/// Create a new `VMContext` instance.
|
/// Create a new `VMContext` instance.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
memories: *mut VMMemory,
|
imported_functions: *const *const VMFunctionBody,
|
||||||
globals: *mut VMGlobal,
|
imported_tables: *mut VMTableImport,
|
||||||
tables: *mut VMTable,
|
imported_memories: *mut VMMemoryImport,
|
||||||
signature_ids: *mut u32,
|
imported_globals: *mut VMGlobalImport,
|
||||||
|
tables: *mut VMTableDefinition,
|
||||||
|
memories: *mut VMMemoryDefinition,
|
||||||
|
globals: *mut VMGlobalDefinition,
|
||||||
|
signature_ids: *mut VMSharedSignatureIndex,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
imported_functions,
|
||||||
|
imported_tables,
|
||||||
|
imported_memories,
|
||||||
|
imported_globals,
|
||||||
|
tables,
|
||||||
memories,
|
memories,
|
||||||
globals,
|
globals,
|
||||||
tables,
|
|
||||||
signature_ids,
|
signature_ids,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the base pointer of the globals array.
|
/// Return a reference to imported function `index`.
|
||||||
pub unsafe fn global(&mut self, index: GlobalIndex) -> &mut VMGlobal {
|
pub unsafe fn imported_function(&self, index: FuncIndex) -> *const VMFunctionBody {
|
||||||
&mut *self.globals.add(index.index())
|
*self.imported_functions.add(index.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a mutable reference to linear memory `index`.
|
/// Return a reference to imported table `index`.
|
||||||
pub unsafe fn memory(&mut self, index: MemoryIndex) -> &mut VMMemory {
|
pub unsafe fn imported_table(&self, index: TableIndex) -> &VMTableImport {
|
||||||
|
&*self.imported_tables.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to imported table `index`.
|
||||||
|
pub unsafe fn imported_table_mut(&mut self, index: TableIndex) -> &mut VMTableImport {
|
||||||
|
&mut *self.imported_tables.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to imported memory `index`.
|
||||||
|
pub unsafe fn imported_memory(&self, index: MemoryIndex) -> &VMMemoryImport {
|
||||||
|
&*self.imported_memories.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to imported memory `index`.
|
||||||
|
pub unsafe fn imported_memory_mut(&mut self, index: MemoryIndex) -> &mut VMMemoryImport {
|
||||||
|
&mut *self.imported_memories.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to imported global `index`.
|
||||||
|
pub unsafe fn imported_global(&self, index: GlobalIndex) -> &VMGlobalImport {
|
||||||
|
&*self.imported_globals.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to imported global `index`.
|
||||||
|
pub unsafe fn imported_global_mut(&mut self, index: GlobalIndex) -> &mut VMGlobalImport {
|
||||||
|
&mut *self.imported_globals.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to locally-defined table `index`.
|
||||||
|
pub unsafe fn table(&self, index: DefinedTableIndex) -> &VMTableDefinition {
|
||||||
|
&*self.tables.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to locally-defined table `index`.
|
||||||
|
pub unsafe fn table_mut(&mut self, index: DefinedTableIndex) -> &mut VMTableDefinition {
|
||||||
|
&mut *self.tables.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reference to locally-defined linear memory `index`.
|
||||||
|
pub unsafe fn memory(&self, index: DefinedMemoryIndex) -> &VMMemoryDefinition {
|
||||||
|
&*self.memories.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to locally-defined linear memory `index`.
|
||||||
|
pub unsafe fn memory_mut(&mut self, index: DefinedMemoryIndex) -> &mut VMMemoryDefinition {
|
||||||
&mut *self.memories.add(index.index())
|
&mut *self.memories.add(index.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a mutable reference to table `index`.
|
/// Return a reference to locally-defined global variable `index`.
|
||||||
pub unsafe fn table(&mut self, index: TableIndex) -> &mut VMTable {
|
pub unsafe fn global(&self, index: DefinedGlobalIndex) -> &VMGlobalDefinition {
|
||||||
&mut *self.tables.add(index.index())
|
&*self.globals.add(index.index())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable reference to locally-defined global variable `index`.
|
||||||
|
pub unsafe fn global_mut(&mut self, index: DefinedGlobalIndex) -> &mut VMGlobalDefinition {
|
||||||
|
&mut *self.globals.add(index.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a mutable reference to the associated `Instance`.
|
/// Return a mutable reference to the associated `Instance`.
|
||||||
@@ -598,4 +564,14 @@ impl VMContext {
|
|||||||
pub unsafe fn instance(&mut self) -> &mut Instance {
|
pub unsafe fn instance(&mut self) -> &mut Instance {
|
||||||
&mut *((self as *mut Self as *mut u8).offset(-Instance::vmctx_offset()) as *mut Instance)
|
&mut *((self as *mut Self as *mut u8).offset(-Instance::vmctx_offset()) as *mut Instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the memory index for the given `VMMemoryDefinition`.
|
||||||
|
pub fn memory_index(&self, memory: &mut VMMemoryDefinition) -> DefinedMemoryIndex {
|
||||||
|
// TODO: Use `offset_from` once it stablizes.
|
||||||
|
let begin = self.memories;
|
||||||
|
let end: *mut VMMemoryDefinition = memory;
|
||||||
|
DefinedMemoryIndex::new(
|
||||||
|
(end as usize - begin as usize) / mem::size_of::<VMMemoryDefinition>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,11 +15,12 @@ cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branc
|
|||||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-entity = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
wasmtime-execute = { path = "../execute" }
|
wasmtime-execute = { path = "../execute" }
|
||||||
|
wasmtime-runtime = { path = "../runtime" }
|
||||||
wasmtime-environ = { path = "../environ" }
|
wasmtime-environ = { path = "../environ" }
|
||||||
wabt = "0.7"
|
wabt = "0.7"
|
||||||
target-lexicon = "0.2.0"
|
target-lexicon = "0.2.0"
|
||||||
failure = "0.1.3"
|
failure = { version = "0.1.3", default-features = false }
|
||||||
failure_derive = "0.1.3"
|
failure_derive = { version = "0.1.3", default-features = false }
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ fn test_directory(out: &mut File, testsuite: &str) -> io::Result<()> {
|
|||||||
" fn {}() {{",
|
" fn {}() {{",
|
||||||
avoid_keywords(&stemstr.replace("-", "_"))
|
avoid_keywords(&stemstr.replace("-", "_"))
|
||||||
)?;
|
)?;
|
||||||
writeln!(out, " let mut wast_context = WastContext::new();")?;
|
writeln!(out, " let mut wast_context = WastContext::new().expect(\"error constructing WastContext\");")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
out,
|
out,
|
||||||
" wast_context.run_file(&*native_isa(), Path::new(\"{}\")).expect(\"error running wast file: {}\");",
|
" wast_context.run_file(&*native_isa(), Path::new(\"{}\")).expect(\"error running wast file: {}\");",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ extern crate target_lexicon;
|
|||||||
extern crate wabt;
|
extern crate wabt;
|
||||||
extern crate wasmtime_environ;
|
extern crate wasmtime_environ;
|
||||||
extern crate wasmtime_execute;
|
extern crate wasmtime_execute;
|
||||||
|
extern crate wasmtime_runtime;
|
||||||
|
|
||||||
mod spectest;
|
mod spectest;
|
||||||
mod wast;
|
mod wast;
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
use cranelift_codegen::ir::types;
|
use cranelift_codegen::ir::types;
|
||||||
use cranelift_codegen::{ir, isa};
|
use cranelift_codegen::{ir, isa};
|
||||||
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
use cranelift_wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use target_lexicon::HOST;
|
use target_lexicon::HOST;
|
||||||
use wasmtime_environ::{translate_signature, MemoryPlan, MemoryStyle, TablePlan, TableStyle};
|
use wasmtime_environ::{
|
||||||
use wasmtime_execute::{ExportValue, Resolver, VMFunctionBody, VMGlobal, VMMemory, VMTable};
|
translate_signature, MemoryPlan, MemoryStyle, Module, TablePlan, TableStyle,
|
||||||
|
};
|
||||||
|
use wasmtime_execute::{Export, Resolver};
|
||||||
|
use wasmtime_runtime::{
|
||||||
|
Imports, Instance, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
|
||||||
|
};
|
||||||
|
|
||||||
extern "C" fn spectest_print() {}
|
extern "C" fn spectest_print() {}
|
||||||
|
|
||||||
@@ -41,44 +47,60 @@ extern "C" fn spectest_print_f64_f64(x: f64, y: f64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpecTest {
|
pub struct SpecTest {
|
||||||
spectest_global_i32: VMGlobal,
|
instance: Instance,
|
||||||
spectest_global_f32: VMGlobal,
|
spectest_global_i32: VMGlobalDefinition,
|
||||||
spectest_global_f64: VMGlobal,
|
spectest_global_f32: VMGlobalDefinition,
|
||||||
spectest_table: VMTable,
|
spectest_global_f64: VMGlobalDefinition,
|
||||||
spectest_memory: VMMemory,
|
spectest_table: VMTableDefinition,
|
||||||
|
spectest_memory: VMMemoryDefinition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpecTest {
|
impl SpecTest {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Result<Self, String> {
|
||||||
Self {
|
let finished_functions = PrimaryMap::new();
|
||||||
spectest_global_i32: VMGlobal::definition(&Global {
|
let imports = Imports::none();
|
||||||
|
let data_initializers = Vec::new();
|
||||||
|
Ok(Self {
|
||||||
|
instance: Instance::new(
|
||||||
|
&Module::new(),
|
||||||
|
&finished_functions.into_boxed_slice(),
|
||||||
|
imports,
|
||||||
|
&data_initializers,
|
||||||
|
)?,
|
||||||
|
spectest_global_i32: VMGlobalDefinition::new(&Global {
|
||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
mutability: true,
|
mutability: true,
|
||||||
initializer: GlobalInit::I32Const(0),
|
initializer: GlobalInit::I32Const(0),
|
||||||
}),
|
}),
|
||||||
spectest_global_f32: VMGlobal::definition(&Global {
|
spectest_global_f32: VMGlobalDefinition::new(&Global {
|
||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
mutability: true,
|
mutability: true,
|
||||||
initializer: GlobalInit::F32Const(0),
|
initializer: GlobalInit::F32Const(0),
|
||||||
}),
|
}),
|
||||||
spectest_global_f64: VMGlobal::definition(&Global {
|
spectest_global_f64: VMGlobalDefinition::new(&Global {
|
||||||
ty: types::I32,
|
ty: types::I32,
|
||||||
mutability: true,
|
mutability: true,
|
||||||
initializer: GlobalInit::F64Const(0),
|
initializer: GlobalInit::F64Const(0),
|
||||||
}),
|
}),
|
||||||
spectest_table: VMTable::definition(ptr::null_mut(), 0),
|
spectest_table: VMTableDefinition {
|
||||||
spectest_memory: VMMemory::definition(ptr::null_mut(), 0),
|
base: ptr::null_mut(),
|
||||||
}
|
current_elements: 0,
|
||||||
|
},
|
||||||
|
spectest_memory: VMMemoryDefinition {
|
||||||
|
base: ptr::null_mut(),
|
||||||
|
current_length: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver for SpecTest {
|
impl Resolver for SpecTest {
|
||||||
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue> {
|
fn resolve(&mut self, module: &str, field: &str) -> Option<Export> {
|
||||||
let call_conv = isa::CallConv::triple_default(&HOST);
|
let call_conv = isa::CallConv::triple_default(&HOST);
|
||||||
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||||
match module {
|
match module {
|
||||||
"spectest" => match field {
|
"spectest" => match field {
|
||||||
"print" => Some(ExportValue::function(
|
"print" => Some(Export::function(
|
||||||
spectest_print as *const VMFunctionBody,
|
spectest_print as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -89,7 +111,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_i32" => Some(ExportValue::function(
|
"print_i32" => Some(Export::function(
|
||||||
spectest_print_i32 as *const VMFunctionBody,
|
spectest_print_i32 as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -100,7 +122,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_i64" => Some(ExportValue::function(
|
"print_i64" => Some(Export::function(
|
||||||
spectest_print_i64 as *const VMFunctionBody,
|
spectest_print_i64 as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -111,7 +133,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_f32" => Some(ExportValue::function(
|
"print_f32" => Some(Export::function(
|
||||||
spectest_print_f32 as *const VMFunctionBody,
|
spectest_print_f32 as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -122,7 +144,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_f64" => Some(ExportValue::function(
|
"print_f64" => Some(Export::function(
|
||||||
spectest_print_f64 as *const VMFunctionBody,
|
spectest_print_f64 as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -133,7 +155,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_i32_f32" => Some(ExportValue::function(
|
"print_i32_f32" => Some(Export::function(
|
||||||
spectest_print_i32_f32 as *const VMFunctionBody,
|
spectest_print_i32_f32 as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -147,7 +169,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"print_f64_f64" => Some(ExportValue::function(
|
"print_f64_f64" => Some(Export::function(
|
||||||
spectest_print_f64_f64 as *const VMFunctionBody,
|
spectest_print_f64_f64 as *const VMFunctionBody,
|
||||||
translate_signature(
|
translate_signature(
|
||||||
ir::Signature {
|
ir::Signature {
|
||||||
@@ -161,7 +183,7 @@ impl Resolver for SpecTest {
|
|||||||
pointer_type,
|
pointer_type,
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
"global_i32" => Some(ExportValue::global(
|
"global_i32" => Some(Export::global(
|
||||||
&mut self.spectest_global_i32,
|
&mut self.spectest_global_i32,
|
||||||
Global {
|
Global {
|
||||||
ty: ir::types::I32,
|
ty: ir::types::I32,
|
||||||
@@ -169,7 +191,7 @@ impl Resolver for SpecTest {
|
|||||||
initializer: GlobalInit::I32Const(0),
|
initializer: GlobalInit::I32Const(0),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
"global_f32" => Some(ExportValue::global(
|
"global_f32" => Some(Export::global(
|
||||||
&mut self.spectest_global_f32,
|
&mut self.spectest_global_f32,
|
||||||
Global {
|
Global {
|
||||||
ty: ir::types::F32,
|
ty: ir::types::F32,
|
||||||
@@ -177,7 +199,7 @@ impl Resolver for SpecTest {
|
|||||||
initializer: GlobalInit::F32Const(0),
|
initializer: GlobalInit::F32Const(0),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
"global_f64" => Some(ExportValue::global(
|
"global_f64" => Some(Export::global(
|
||||||
&mut self.spectest_global_f64,
|
&mut self.spectest_global_f64,
|
||||||
Global {
|
Global {
|
||||||
ty: ir::types::F64,
|
ty: ir::types::F64,
|
||||||
@@ -185,8 +207,9 @@ impl Resolver for SpecTest {
|
|||||||
initializer: GlobalInit::F64Const(0),
|
initializer: GlobalInit::F64Const(0),
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
"table" => Some(ExportValue::table(
|
"table" => Some(Export::table(
|
||||||
&mut self.spectest_table,
|
&mut self.spectest_table,
|
||||||
|
self.instance.vmctx_mut(),
|
||||||
TablePlan {
|
TablePlan {
|
||||||
table: Table {
|
table: Table {
|
||||||
ty: TableElementType::Func,
|
ty: TableElementType::Func,
|
||||||
@@ -196,8 +219,9 @@ impl Resolver for SpecTest {
|
|||||||
style: TableStyle::CallerChecksSignature,
|
style: TableStyle::CallerChecksSignature,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
"memory" => Some(ExportValue::memory(
|
"memory" => Some(Export::memory(
|
||||||
&mut self.spectest_memory,
|
&mut self.spectest_memory,
|
||||||
|
self.instance.vmctx_mut(),
|
||||||
MemoryPlan {
|
MemoryPlan {
|
||||||
memory: Memory {
|
memory: Memory {
|
||||||
minimum: 0,
|
minimum: 0,
|
||||||
|
|||||||
@@ -90,14 +90,14 @@ pub struct WastContext {
|
|||||||
|
|
||||||
impl WastContext {
|
impl WastContext {
|
||||||
/// Construct a new instance of `WastContext`.
|
/// Construct a new instance of `WastContext`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Result<Self, String> {
|
||||||
Self {
|
Ok(Self {
|
||||||
worlds: PrimaryMap::new(),
|
worlds: PrimaryMap::new(),
|
||||||
current: None,
|
current: None,
|
||||||
namespace: HashMap::new(),
|
namespace: HashMap::new(),
|
||||||
code: Code::new(),
|
code: Code::new(),
|
||||||
spectest: SpecTest::new(),
|
spectest: SpecTest::new()?,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate(
|
fn instantiate(
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||||
let mut wast_context = WastContext::new();
|
let mut wast_context = WastContext::new().expect("Error creating WastContext");
|
||||||
for filename in &args.arg_file {
|
for filename in &args.arg_file {
|
||||||
wast_context
|
wast_context
|
||||||
.run_file(&*isa, Path::new(&filename))
|
.run_file(&*isa, Path::new(&filename))
|
||||||
|
|||||||
@@ -184,11 +184,13 @@ fn handle_module(args: &Args, path: &Path, isa: &TargetIsa) -> Result<(), String
|
|||||||
if split.len() != 3 {
|
if split.len() != 3 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let memory = world.inspect_memory(
|
let memory = world
|
||||||
|
.inspect_memory(
|
||||||
MemoryIndex::new(str::parse(split[0]).unwrap()),
|
MemoryIndex::new(str::parse(split[0]).unwrap()),
|
||||||
str::parse(split[1]).unwrap(),
|
str::parse(split[1]).unwrap(),
|
||||||
str::parse(split[2]).unwrap(),
|
str::parse(split[2]).unwrap(),
|
||||||
);
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
let mut s = memory.iter().fold(String::from("#"), |mut acc, byte| {
|
let mut s = memory.iter().fold(String::from("#"), |mut acc, byte| {
|
||||||
acc.push_str(format!("{:02x}_", byte).as_str());
|
acc.push_str(format!("{:02x}_", byte).as_str());
|
||||||
acc
|
acc
|
||||||
|
|||||||
Reference in New Issue
Block a user