Support imports.
This commit is contained in:
@@ -7,7 +7,9 @@ use cranelift_codegen::ir::ExternalName;
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_codegen::Context;
|
||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
|
||||
use cranelift_wasm::{
|
||||
DefinedFuncIndex, FuncIndex, FuncTranslator, GlobalIndex, MemoryIndex, TableIndex,
|
||||
};
|
||||
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, ModuleTranslation};
|
||||
use std::string::{String, ToString};
|
||||
use std::vec::Vec;
|
||||
@@ -17,12 +19,30 @@ use std::vec::Vec;
|
||||
pub struct Compilation {
|
||||
/// Compiled machine code for the function bodies.
|
||||
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>,
|
||||
|
||||
/// Resolved function addresses for imported functions.
|
||||
pub resolved_func_imports: PrimaryMap<FuncIndex, usize>,
|
||||
|
||||
/// Resolved function addresses for imported tables.
|
||||
pub resolved_table_imports: PrimaryMap<TableIndex, usize>,
|
||||
|
||||
/// Resolved function addresses for imported globals.
|
||||
pub resolved_global_imports: PrimaryMap<GlobalIndex, usize>,
|
||||
|
||||
/// Resolved function addresses for imported memories.
|
||||
pub resolved_memory_imports: PrimaryMap<MemoryIndex, usize>,
|
||||
}
|
||||
|
||||
impl Compilation {
|
||||
/// Allocates the compilation result with the given function bodies.
|
||||
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self {
|
||||
Self { functions }
|
||||
Self {
|
||||
functions,
|
||||
resolved_func_imports: PrimaryMap::new(),
|
||||
resolved_table_imports: PrimaryMap::new(),
|
||||
resolved_memory_imports: PrimaryMap::new(),
|
||||
resolved_global_imports: PrimaryMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,5 +165,6 @@ pub fn compile_module<'data, 'module>(
|
||||
functions.push(code_buf);
|
||||
relocations.push(reloc_sink.func_relocs);
|
||||
}
|
||||
// TODO: Reorganize where we create the Vec for the resolved imports.
|
||||
Ok((Compilation::new(functions), relocations))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use cast;
|
||||
use cranelift_codegen::cursor::FuncCursor;
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_codegen::ir::condcodes::*;
|
||||
use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64};
|
||||
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
|
||||
use cranelift_codegen::ir::types::*;
|
||||
use cranelift_codegen::ir::{
|
||||
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
|
||||
@@ -169,11 +169,7 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||
}
|
||||
|
||||
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||
let mut sig = sig.clone();
|
||||
sig.params.push(AbiParam::special(
|
||||
self.pointer_type(),
|
||||
ArgumentPurpose::VMContext,
|
||||
));
|
||||
let sig = translate_signature(sig.clone(), self.pointer_type());
|
||||
// TODO: Deduplicate signatures.
|
||||
self.module.signatures.push(sig);
|
||||
}
|
||||
@@ -207,8 +203,17 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||
self.module.functions[func_index]
|
||||
}
|
||||
|
||||
fn declare_global_import(&mut self, _global: Global, _module: &str, _field: &str) {
|
||||
unimplemented!("imported globals");
|
||||
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) {
|
||||
@@ -219,8 +224,18 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||
&self.module.globals[global_index]
|
||||
}
|
||||
|
||||
fn declare_table_import(&mut self, _table: Table, _module: &str, _field: &str) {
|
||||
unimplemented!("imported tables");
|
||||
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) {
|
||||
@@ -235,7 +250,6 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||
offset: usize,
|
||||
elements: Vec<FuncIndex>,
|
||||
) {
|
||||
debug_assert!(base.is_none(), "global-value offsets not supported yet");
|
||||
self.module.table_elements.push(TableElements {
|
||||
table_index,
|
||||
base,
|
||||
@@ -244,8 +258,18 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||
});
|
||||
}
|
||||
|
||||
fn declare_memory_import(&mut self, _memory: Memory, _module: &str, _field: &str) {
|
||||
unimplemented!("imported memories");
|
||||
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) {
|
||||
@@ -260,7 +284,6 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
||||
offset: usize,
|
||||
data: &'data [u8],
|
||||
) {
|
||||
debug_assert!(base.is_none(), "global-value offsets not supported yet");
|
||||
self.lazy.data_initializers.push(DataInitializer {
|
||||
memory_index,
|
||||
base,
|
||||
@@ -313,7 +336,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
let pointer_type = self.pointer_type();
|
||||
|
||||
let vmctx = self.vmctx(func);
|
||||
let globals_base = self.globals_base.unwrap_or_else(|| {
|
||||
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())),
|
||||
@@ -323,13 +346,24 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
self.globals_base = Some(new_base);
|
||||
new_base
|
||||
});
|
||||
let gv = func.create_global_value(ir::GlobalValueData::IAddImm {
|
||||
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: Imm64::new(i64::from(self.offsets.index_vmglobal(index.as_u32()))),
|
||||
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,
|
||||
gv: globals_base,
|
||||
offset: offset.into(),
|
||||
ty: self.module.globals[index].ty,
|
||||
}
|
||||
}
|
||||
@@ -338,7 +372,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
let pointer_type = self.pointer_type();
|
||||
|
||||
let vmctx = self.vmctx(func);
|
||||
let memories_base = self.memories_base.unwrap_or_else(|| {
|
||||
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())),
|
||||
@@ -348,6 +382,25 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
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] {
|
||||
@@ -358,9 +411,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
} => {
|
||||
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
|
||||
base: memories_base,
|
||||
offset: Offset32::new(
|
||||
self.offsets.index_vmmemory_current_length(index.as_u32()),
|
||||
),
|
||||
offset: Offset32::new(current_length_offset),
|
||||
global_type: I32,
|
||||
readonly: false,
|
||||
});
|
||||
@@ -387,7 +438,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
|
||||
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||
base: memories_base,
|
||||
offset: Offset32::new(self.offsets.index_vmmemory_base(index.as_u32())),
|
||||
offset: Offset32::new(base_offset),
|
||||
global_type: pointer_type,
|
||||
readonly: readonly_base,
|
||||
});
|
||||
@@ -404,7 +455,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
let pointer_type = self.pointer_type();
|
||||
|
||||
let vmctx = self.vmctx(func);
|
||||
let tables_base = self.tables_base.unwrap_or_else(|| {
|
||||
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())),
|
||||
@@ -414,15 +465,34 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
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(self.offsets.index_vmtable_base(index.as_u32())),
|
||||
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(self.offsets.index_vmtable_current_elements(index.as_u32())),
|
||||
offset: Offset32::new(current_elements_offset),
|
||||
global_type: I32,
|
||||
readonly: false,
|
||||
});
|
||||
@@ -492,7 +562,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
|
||||
|
||||
let vmctx = self.vmctx(pos.func);
|
||||
let signature_ids_base = self.globals_base.unwrap_or_else(|| {
|
||||
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())),
|
||||
@@ -563,13 +633,13 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
) -> WasmResult<ir::Value> {
|
||||
let memory_grow_func = self.memory_grow_extfunc.unwrap_or_else(|| {
|
||||
let sig_ref = pos.func.import_signature(Signature {
|
||||
call_conv: self.isa.frontend_config().default_call_conv,
|
||||
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.
|
||||
@@ -597,12 +667,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
) -> WasmResult<ir::Value> {
|
||||
let memory_size_func = self.memory_size_extfunc.unwrap_or_else(|| {
|
||||
let sig_ref = pos.func.import_signature(Signature {
|
||||
call_conv: self.isa.frontend_config().default_call_conv,
|
||||
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.
|
||||
@@ -642,3 +712,10 @@ impl<'data, 'module> ModuleTranslation<'data, 'module> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ mod vmoffsets;
|
||||
pub use compilation::{
|
||||
compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations,
|
||||
};
|
||||
pub use environ::{ModuleEnvironment, ModuleTranslation};
|
||||
pub use environ::{translate_signature, ModuleEnvironment, ModuleTranslation};
|
||||
pub use module::{
|
||||
DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
|
||||
};
|
||||
|
||||
@@ -139,6 +139,15 @@ pub struct Module {
|
||||
/// Names of imported functions.
|
||||
pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
|
||||
|
||||
/// Names of imported tables.
|
||||
pub imported_tables: PrimaryMap<TableIndex, (String, String)>,
|
||||
|
||||
/// Names of imported globals.
|
||||
pub imported_globals: PrimaryMap<GlobalIndex, (String, String)>,
|
||||
|
||||
/// Names of imported memories.
|
||||
pub imported_memories: PrimaryMap<MemoryIndex, (String, String)>,
|
||||
|
||||
/// Types of functions, imported and local.
|
||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||
|
||||
@@ -167,6 +176,9 @@ impl Module {
|
||||
Self {
|
||||
signatures: PrimaryMap::new(),
|
||||
imported_funcs: PrimaryMap::new(),
|
||||
imported_memories: PrimaryMap::new(),
|
||||
imported_globals: PrimaryMap::new(),
|
||||
imported_tables: PrimaryMap::new(),
|
||||
functions: PrimaryMap::new(),
|
||||
table_plans: PrimaryMap::new(),
|
||||
memory_plans: PrimaryMap::new(),
|
||||
@@ -193,6 +205,26 @@ impl Module {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether the given function index is for an imported function.
|
||||
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
|
||||
index.index() < self.imported_funcs.len()
|
||||
}
|
||||
|
||||
/// Test whether the given table index is for an imported table.
|
||||
pub fn is_imported_table(&self, index: TableIndex) -> bool {
|
||||
index.index() < self.imported_tables.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()
|
||||
}
|
||||
|
||||
/// Test whether the given memory index is for an imported memory.
|
||||
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
|
||||
index.index() < self.imported_memories.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// A data initializer for linear memory.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! Offsets and sizes of various structs in wasmtime-execute's vmcontext
|
||||
//! module.
|
||||
|
||||
/// This class computes offsets to fields within `VMContext` and other
|
||||
/// related structs that JIT code accesses directly.
|
||||
pub struct VMOffsets {
|
||||
@@ -11,44 +14,108 @@ impl VMOffsets {
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `wasmtime_execute::VMMemory`.
|
||||
/// Offsets for `wasmtime_execute::VMMemoryDefinition`.
|
||||
impl VMOffsets {
|
||||
/// The offset of the `base` field.
|
||||
pub fn vmmemory_base(&self) -> u8 {
|
||||
pub fn vmmemory_definition_base(&self) -> u8 {
|
||||
0 * self.pointer_size
|
||||
}
|
||||
|
||||
/// The offset of the `current_length` field.
|
||||
pub fn vmmemory_current_length(&self) -> u8 {
|
||||
pub fn vmmemory_definition_current_length(&self) -> u8 {
|
||||
1 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the size of `VMMemoryDefinition`.
|
||||
pub fn size_of_vmmemory_definition(&self) -> u8 {
|
||||
2 * self.pointer_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `wasmtime_execute::VMMemoryImport`.
|
||||
impl VMOffsets {
|
||||
/// The offset of the `from` field.
|
||||
pub fn vmmemory_import_from(&self) -> u8 {
|
||||
0 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the size of `VMMemoryImport`.
|
||||
pub fn size_of_vmmemory_import(&self) -> u8 {
|
||||
1 * self.pointer_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `wasmtime_execute::VMMemory`.
|
||||
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 {
|
||||
/// The offset of the `from` field.
|
||||
pub fn vmglobal_import_from(&self) -> u8 {
|
||||
0 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the size of `VMGlobalImport`.
|
||||
pub fn size_of_vmglobal_import(&self) -> u8 {
|
||||
1 * self.pointer_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `wasmtime_execute::VMGlobal`.
|
||||
impl VMOffsets {
|
||||
/// Return the size of `VMGlobal`.
|
||||
pub fn size_of_vmglobal(&self) -> u8 {
|
||||
8
|
||||
assert!(self.size_of_vmglobal_import() <= self.size_of_vmglobal_definition());
|
||||
self.size_of_vmglobal_definition()
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `wasmtime_execute::VMTableDefinition`.
|
||||
impl VMOffsets {
|
||||
/// The offset of the `base` field.
|
||||
pub fn vmtable_definition_base(&self) -> u8 {
|
||||
0 * self.pointer_size
|
||||
}
|
||||
|
||||
/// The offset of the `current_elements` field.
|
||||
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.
|
||||
pub fn vmtable_import_from(&self) -> u8 {
|
||||
0 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the size of `VMTableImport`.
|
||||
pub fn size_of_vmtable_import(&self) -> u8 {
|
||||
1 * self.pointer_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `wasmtime_execute::VMTable`.
|
||||
impl VMOffsets {
|
||||
/// The offset of the `base` field.
|
||||
pub fn vmtable_base(&self) -> u8 {
|
||||
0 * self.pointer_size
|
||||
}
|
||||
|
||||
/// The offset of the `current_elements` field.
|
||||
pub fn vmtable_current_elements(&self) -> u8 {
|
||||
1 * self.pointer_size
|
||||
}
|
||||
|
||||
/// Return the size of `VMTable`.
|
||||
pub fn size_of_vmtable(&self) -> u8 {
|
||||
2 * self.pointer_size
|
||||
@@ -141,33 +208,57 @@ impl VMOffsets {
|
||||
|
||||
/// Return the offset from the `memories` pointer to the `base` field in
|
||||
/// `VMMemory` index `index`.
|
||||
pub fn index_vmmemory_base(&self, index: u32) -> i32 {
|
||||
pub fn index_vmmemory_definition_base(&self, index: u32) -> i32 {
|
||||
self.index_vmmemory(index)
|
||||
.checked_add(i32::from(self.vmmemory_base()))
|
||||
.checked_add(i32::from(self.vmmemory_definition_base()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Return the offset from the `memories` pointer to the `current_length` field in
|
||||
/// `VMMemory` index `index`.
|
||||
pub fn index_vmmemory_current_length(&self, index: u32) -> i32 {
|
||||
/// `VMMemoryDefinition` index `index`.
|
||||
pub fn index_vmmemory_definition_current_length(&self, index: u32) -> i32 {
|
||||
self.index_vmmemory(index)
|
||||
.checked_add(i32::from(self.vmmemory_current_length()))
|
||||
.checked_add(i32::from(self.vmmemory_definition_current_length()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Return the offset from the `memories` pointer to the `from` field in
|
||||
/// `VMMemoryImport` index `index`.
|
||||
pub fn index_vmmemory_import_from(&self, index: u32) -> i32 {
|
||||
self.index_vmmemory(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()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Return the offset from the `tables` pointer to the `base` field in
|
||||
/// `VMTable` index `index`.
|
||||
pub fn index_vmtable_base(&self, index: u32) -> i32 {
|
||||
pub fn index_vmtable_definition_base(&self, index: u32) -> i32 {
|
||||
self.index_vmtable(index)
|
||||
.checked_add(i32::from(self.vmtable_base()))
|
||||
.checked_add(i32::from(self.vmtable_definition_base()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Return the offset from the `tables` pointer to the `current_elements` field in
|
||||
/// `VMTable` index `index`.
|
||||
pub fn index_vmtable_current_elements(&self, index: u32) -> i32 {
|
||||
pub fn index_vmtable_definition_current_elements(&self, index: u32) -> i32 {
|
||||
self.index_vmtable(index)
|
||||
.checked_add(i32::from(self.vmtable_current_elements()))
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,66 +5,282 @@ use code::Code;
|
||||
use cranelift_codegen::binemit::Reloc;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||
use cranelift_wasm::{DefinedFuncIndex, MemoryIndex};
|
||||
use cranelift_wasm::{
|
||||
DefinedFuncIndex, Global, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
||||
};
|
||||
use export::{ExportValue, Resolver};
|
||||
use instance::Instance;
|
||||
use invoke::{invoke_by_index, InvokeOutcome};
|
||||
use region::protect;
|
||||
use region::Protection;
|
||||
use region::{protect, Protection};
|
||||
use std::ptr::write_unaligned;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use vmcontext::VMContext;
|
||||
use wasmtime_environ::{
|
||||
compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget,
|
||||
compile_module, Compilation, MemoryPlan, MemoryStyle, Module, ModuleTranslation, Relocation,
|
||||
RelocationTarget, TablePlan, TableStyle,
|
||||
};
|
||||
|
||||
/// Executes a module that has been translated with the `wasmtime-environ` environment
|
||||
/// implementation.
|
||||
pub fn compile_and_link_module<'data, 'module, F>(
|
||||
pub fn compile_and_link_module<'data, 'module>(
|
||||
isa: &TargetIsa,
|
||||
translation: &ModuleTranslation<'data, 'module>,
|
||||
imports: F,
|
||||
) -> Result<Compilation, String>
|
||||
where
|
||||
F: Fn(&str, &str) -> Option<usize>,
|
||||
{
|
||||
resolver: &mut Resolver,
|
||||
) -> Result<Compilation, String> {
|
||||
let (mut compilation, relocations) = compile_module(&translation, isa)?;
|
||||
|
||||
for (index, (ref module, ref field)) in translation.module.imported_funcs.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Function { address, signature } => {
|
||||
let import_signature =
|
||||
&translation.module.signatures[translation.module.functions[index]];
|
||||
if signature != *import_signature {
|
||||
return Err(format!(
|
||||
"{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
||||
module, field,
|
||||
signature, import_signature,
|
||||
));
|
||||
}
|
||||
compilation.resolved_func_imports.push(address);
|
||||
}
|
||||
ExportValue::Table { .. }
|
||||
| ExportValue::Memory { .. }
|
||||
| ExportValue::Global { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: export not compatible with function import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => return Err(format!("{}/{}: no provided import function", module, field)),
|
||||
}
|
||||
}
|
||||
for (index, (ref module, ref field)) in translation.module.imported_globals.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Global { address, global } => {
|
||||
let imported_global = translation.module.globals[index];
|
||||
if !is_global_compatible(&global, &imported_global) {
|
||||
return Err(format!(
|
||||
"{}/{}: exported global incompatible with global import",
|
||||
module, field,
|
||||
));
|
||||
}
|
||||
compilation.resolved_global_imports.push(address as usize);
|
||||
}
|
||||
ExportValue::Table { .. }
|
||||
| ExportValue::Memory { .. }
|
||||
| ExportValue::Function { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: exported global incompatible with global import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(format!(
|
||||
"no provided import global for {}/{}",
|
||||
module, field
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index, (ref module, ref field)) in translation.module.imported_tables.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Table { address, table } => {
|
||||
let import_table = &translation.module.table_plans[index];
|
||||
if !is_table_compatible(&table, import_table) {
|
||||
return Err(format!(
|
||||
"{}/{}: exported table incompatible with table import",
|
||||
module, field,
|
||||
));
|
||||
}
|
||||
compilation.resolved_table_imports.push(address as usize);
|
||||
}
|
||||
ExportValue::Global { .. }
|
||||
| ExportValue::Memory { .. }
|
||||
| ExportValue::Function { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: export not compatible with table import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => return Err(format!("no provided import table for {}/{}", module, field)),
|
||||
}
|
||||
}
|
||||
for (index, (ref module, ref field)) in translation.module.imported_memories.iter() {
|
||||
match resolver.resolve(module, field) {
|
||||
Some(export_value) => match export_value {
|
||||
ExportValue::Memory { address, memory } => {
|
||||
let import_memory = &translation.module.memory_plans[index];
|
||||
if is_memory_compatible(&memory, import_memory) {
|
||||
return Err(format!(
|
||||
"{}/{}: exported memory incompatible with memory import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
compilation.resolved_memory_imports.push(address as usize);
|
||||
}
|
||||
ExportValue::Table { .. }
|
||||
| ExportValue::Global { .. }
|
||||
| ExportValue::Function { .. } => {
|
||||
return Err(format!(
|
||||
"{}/{}: export not compatible with memory import",
|
||||
module, field
|
||||
));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(format!(
|
||||
"no provided import memory for {}/{}",
|
||||
module, field
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply relocations, now that we have virtual addresses for everything.
|
||||
relocate(&mut compilation, &relocations, &translation.module, imports);
|
||||
relocate(&mut compilation, &relocations, &translation.module)?;
|
||||
|
||||
Ok(compilation)
|
||||
}
|
||||
|
||||
fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
||||
match imported.initializer {
|
||||
GlobalInit::Import => (),
|
||||
_ => panic!("imported Global should have an Imported initializer"),
|
||||
}
|
||||
|
||||
let Global {
|
||||
ty: exported_ty,
|
||||
mutability: exported_mutability,
|
||||
initializer: _exported_initializer,
|
||||
} = exported;
|
||||
let Global {
|
||||
ty: imported_ty,
|
||||
mutability: imported_mutability,
|
||||
initializer: _imported_initializer,
|
||||
} = imported;
|
||||
exported_ty == imported_ty && imported_mutability == exported_mutability
|
||||
}
|
||||
|
||||
fn is_table_style_compatible(exported_style: &TableStyle, imported_style: &TableStyle) -> bool {
|
||||
match exported_style {
|
||||
TableStyle::CallerChecksSignature => match imported_style {
|
||||
TableStyle::CallerChecksSignature => true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_table_element_type_compatible(
|
||||
exported_type: TableElementType,
|
||||
imported_type: TableElementType,
|
||||
) -> bool {
|
||||
match exported_type {
|
||||
TableElementType::Func => match imported_type {
|
||||
TableElementType::Func => true,
|
||||
_ => false,
|
||||
},
|
||||
TableElementType::Val(exported_val_ty) => match imported_type {
|
||||
TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool {
|
||||
let TablePlan {
|
||||
table:
|
||||
Table {
|
||||
ty: exported_ty,
|
||||
minimum: exported_minimum,
|
||||
maximum: exported_maximum,
|
||||
},
|
||||
style: exported_style,
|
||||
} = exported;
|
||||
let TablePlan {
|
||||
table:
|
||||
Table {
|
||||
ty: imported_ty,
|
||||
minimum: imported_minimum,
|
||||
maximum: imported_maximum,
|
||||
},
|
||||
style: imported_style,
|
||||
} = imported;
|
||||
|
||||
is_table_element_type_compatible(*exported_ty, *imported_ty)
|
||||
&& imported_minimum >= exported_minimum
|
||||
&& imported_maximum <= exported_maximum
|
||||
&& is_table_style_compatible(imported_style, exported_style)
|
||||
}
|
||||
|
||||
fn is_memory_style_compatible(exported_style: &MemoryStyle, imported_style: &MemoryStyle) -> bool {
|
||||
match exported_style {
|
||||
MemoryStyle::Dynamic => match imported_style {
|
||||
MemoryStyle::Dynamic => true,
|
||||
_ => false,
|
||||
},
|
||||
MemoryStyle::Static {
|
||||
bound: imported_bound,
|
||||
} => match imported_style {
|
||||
MemoryStyle::Static {
|
||||
bound: exported_bound,
|
||||
} => exported_bound >= imported_bound,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
|
||||
let MemoryPlan {
|
||||
memory:
|
||||
Memory {
|
||||
minimum: exported_minimum,
|
||||
maximum: exported_maximum,
|
||||
shared: exported_shared,
|
||||
},
|
||||
style: exported_style,
|
||||
offset_guard_size: exported_offset_guard_size,
|
||||
} = exported;
|
||||
let MemoryPlan {
|
||||
memory:
|
||||
Memory {
|
||||
minimum: imported_minimum,
|
||||
maximum: imported_maximum,
|
||||
shared: imported_shared,
|
||||
},
|
||||
style: imported_style,
|
||||
offset_guard_size: imported_offset_guard_size,
|
||||
} = imported;
|
||||
|
||||
imported_minimum >= exported_minimum
|
||||
&& imported_maximum <= exported_maximum
|
||||
&& exported_shared == imported_shared
|
||||
&& is_memory_style_compatible(exported_style, imported_style)
|
||||
&& exported_offset_guard_size >= imported_offset_guard_size
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn __rust_probestack();
|
||||
}
|
||||
|
||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata
|
||||
fn relocate<F>(
|
||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
||||
fn relocate(
|
||||
compilation: &mut Compilation,
|
||||
relocations: &PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||
module: &Module,
|
||||
imports: F,
|
||||
) where
|
||||
F: Fn(&str, &str) -> Option<usize>,
|
||||
{
|
||||
// The relocations are relative to the relocation's address plus four bytes
|
||||
// TODO: Support architectures other than x64, and other reloc kinds.
|
||||
) -> Result<(), String> {
|
||||
// The relocations are relative to the relocation's address plus four bytes.
|
||||
for (i, function_relocs) in relocations.iter() {
|
||||
for r in function_relocs {
|
||||
let target_func_address: usize = match r.reloc_target {
|
||||
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
||||
Some(f) => compilation.functions[f].as_ptr() as usize,
|
||||
None => {
|
||||
let func = &module.imported_funcs[index];
|
||||
match imports(&func.0, &func.1) {
|
||||
Some(ptr) => ptr,
|
||||
None => {
|
||||
panic!("no provided import function for {}/{}", &func.0, &func.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => compilation.resolved_func_imports[index],
|
||||
},
|
||||
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
||||
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
||||
@@ -111,6 +327,7 @@ fn relocate<F>(
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
||||
|
||||
76
lib/execute/src/export.rs
Normal file
76
lib/execute/src/export.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_wasm::Global;
|
||||
use vmcontext::{VMGlobal, VMMemory, VMTable};
|
||||
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||
|
||||
/// The value of an export passed from one instance to another.
|
||||
pub enum ExportValue {
|
||||
/// A function export value.
|
||||
Function {
|
||||
/// The address of the native-code function.
|
||||
address: usize,
|
||||
/// The function signature declaration, used for compatibilty checking.
|
||||
signature: ir::Signature,
|
||||
},
|
||||
|
||||
/// A table export value.
|
||||
Table {
|
||||
/// The address of the table descriptor.
|
||||
address: *mut VMTable,
|
||||
/// The table declaration, used for compatibilty checking.
|
||||
table: TablePlan,
|
||||
},
|
||||
|
||||
/// A memory export value.
|
||||
Memory {
|
||||
/// The address of the memory descriptor.
|
||||
address: *mut VMMemory,
|
||||
/// The memory declaration, used for compatibilty checking.
|
||||
memory: MemoryPlan,
|
||||
},
|
||||
|
||||
/// A global export value.
|
||||
Global {
|
||||
/// The address of the global storage.
|
||||
address: *mut VMGlobal,
|
||||
/// The global declaration, used for compatibilty checking.
|
||||
global: Global,
|
||||
},
|
||||
}
|
||||
|
||||
impl ExportValue {
|
||||
/// Construct a function export value.
|
||||
pub fn function(address: usize, signature: ir::Signature) -> Self {
|
||||
ExportValue::Function { address, signature }
|
||||
}
|
||||
|
||||
/// Construct a table export value.
|
||||
pub fn table(address: *mut VMTable, table: TablePlan) -> Self {
|
||||
ExportValue::Table { address, table }
|
||||
}
|
||||
|
||||
/// Construct a memory export value.
|
||||
pub fn memory(address: *mut VMMemory, memory: MemoryPlan) -> Self {
|
||||
ExportValue::Memory { address, memory }
|
||||
}
|
||||
|
||||
/// Construct a global export value.
|
||||
pub fn global(address: *mut VMGlobal, global: Global) -> Self {
|
||||
ExportValue::Global { address, global }
|
||||
}
|
||||
}
|
||||
|
||||
/// Import resolver connects imports with available exported values.
|
||||
pub trait Resolver {
|
||||
/// Resolve the given module/field combo.
|
||||
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue>;
|
||||
}
|
||||
|
||||
/// `Resolver` implementation that always resolves to `None`.
|
||||
pub struct NullResolver {}
|
||||
|
||||
impl Resolver for NullResolver {
|
||||
fn resolve(&mut self, _module: &str, _field: &str) -> Option<ExportValue> {
|
||||
None
|
||||
}
|
||||
}
|
||||
39
lib/execute/src/get.rs
Normal file
39
lib/execute/src/get.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
//! Support for reading the value of a wasm global from outside the module.
|
||||
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_wasm::GlobalIndex;
|
||||
use invoke::Value;
|
||||
use std::string::String;
|
||||
use vmcontext::VMContext;
|
||||
use wasmtime_environ::{Export, Module};
|
||||
|
||||
/// Jumps to the code region of memory and invoke the exported function
|
||||
pub fn get(module: &Module, vmctx: *mut VMContext, global_name: &str) -> Result<Value, String> {
|
||||
let global_index = match module.exports.get(global_name) {
|
||||
Some(Export::Global(index)) => *index,
|
||||
Some(_) => return Err(format!("exported item \"{}\" is not a global", global_name)),
|
||||
None => return Err(format!("no export named \"{}\"", global_name)),
|
||||
};
|
||||
|
||||
get_by_index(module, vmctx, global_index)
|
||||
}
|
||||
|
||||
pub fn get_by_index(
|
||||
module: &Module,
|
||||
vmctx: *mut VMContext,
|
||||
global_index: GlobalIndex,
|
||||
) -> Result<Value, String> {
|
||||
// TODO: Return Err if the index is out of bounds.
|
||||
unsafe {
|
||||
let vmctx = &mut *vmctx;
|
||||
let vmglobal = vmctx.global(global_index);
|
||||
let definition = vmglobal.get_definition(module.is_imported_global(global_index));
|
||||
Ok(match module.globals[global_index].ty {
|
||||
ir::types::I32 => Value::I32(*definition.as_i32()),
|
||||
ir::types::I64 => Value::I64(*definition.as_i64()),
|
||||
ir::types::F32 => Value::F32(*definition.as_f32_bits()),
|
||||
ir::types::F64 => Value::F64(*definition.as_f64_bits()),
|
||||
other => return Err(format!("global with type {} not supported", other)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||
use memory::LinearMemory;
|
||||
use sig_registry::SignatureRegistry;
|
||||
use std::ptr;
|
||||
use std::string::String;
|
||||
use table::Table;
|
||||
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
||||
@@ -44,7 +45,7 @@ impl Instance {
|
||||
compilation: &Compilation,
|
||||
data_initializers: &[DataInitializer],
|
||||
) -> Result<Self, String> {
|
||||
let mut sig_registry = SignatureRegistry::new();
|
||||
let mut sig_registry = instantiate_signatures(module);
|
||||
let mut memories = instantiate_memories(module, data_initializers)?;
|
||||
let mut tables = instantiate_tables(module, compilation, &mut sig_registry);
|
||||
|
||||
@@ -131,6 +132,14 @@ impl Instance {
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate_signatures(module: &Module) -> SignatureRegistry {
|
||||
let mut sig_registry = SignatureRegistry::new();
|
||||
for (sig_index, sig) in module.signatures.iter() {
|
||||
sig_registry.register(sig_index, sig);
|
||||
}
|
||||
sig_registry
|
||||
}
|
||||
|
||||
/// Allocate memory for just the memories of the current module.
|
||||
fn instantiate_memories(
|
||||
module: &Module,
|
||||
@@ -171,7 +180,7 @@ fn instantiate_tables(
|
||||
let code_buf = &compilation.functions[module
|
||||
.defined_func_index(*func_idx)
|
||||
.expect("table element initializer with imported function not supported yet")];
|
||||
let type_id = sig_registry.register(callee_sig, &module.signatures[callee_sig]);
|
||||
let type_id = sig_registry.lookup(callee_sig);
|
||||
subslice[i] = VMCallerCheckedAnyfunc {
|
||||
func_ptr: code_buf.as_ptr(),
|
||||
type_id,
|
||||
@@ -187,8 +196,13 @@ fn instantiate_tables(
|
||||
fn instantiate_globals(module: &Module) -> PrimaryMap<GlobalIndex, VMGlobal> {
|
||||
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len());
|
||||
|
||||
for _ in 0..module.globals.len() {
|
||||
vmctx_globals.push(VMGlobal::default());
|
||||
for (index, global) in module.globals.iter() {
|
||||
if module.is_imported_global(index) {
|
||||
// FIXME: get the actual import
|
||||
vmctx_globals.push(VMGlobal::import(ptr::null_mut()));
|
||||
} else {
|
||||
vmctx_globals.push(VMGlobal::definition(global));
|
||||
}
|
||||
}
|
||||
|
||||
vmctx_globals
|
||||
|
||||
@@ -114,12 +114,16 @@ pub fn invoke_by_index(
|
||||
fn_index: FuncIndex,
|
||||
args: &[Value],
|
||||
) -> Result<InvokeOutcome, String> {
|
||||
let code_buf = &compilation.functions[module
|
||||
.defined_func_index(fn_index)
|
||||
.expect("imported start functions not supported yet")];
|
||||
let sig = &module.signatures[module.functions[fn_index]];
|
||||
// TODO: Return Err if fn_index is out of bounds.
|
||||
let exec_code_buf = match module.defined_func_index(fn_index) {
|
||||
Some(def_fn_index) => {
|
||||
let code_buf = &compilation.functions[def_fn_index];
|
||||
code.allocate_copy_of_slice(&code_buf)?.as_ptr() as usize
|
||||
}
|
||||
None => compilation.resolved_func_imports[fn_index],
|
||||
};
|
||||
|
||||
let exec_code_buf = code.allocate_copy_of_slice(&code_buf)?.as_ptr();
|
||||
let sig = &module.signatures[module.functions[fn_index]];
|
||||
|
||||
// TODO: Move this out to be done once per thread rather than per call.
|
||||
let mut traps = TrapContext {
|
||||
@@ -138,7 +142,7 @@ pub fn invoke_by_index(
|
||||
return Err("failed to install signal handlers".to_string());
|
||||
}
|
||||
|
||||
call_through_wrapper(code, isa, exec_code_buf as usize, vmctx, args, &sig)
|
||||
call_through_wrapper(code, isa, exec_code_buf, vmctx, args, &sig)
|
||||
}
|
||||
|
||||
fn call_through_wrapper(
|
||||
|
||||
@@ -43,6 +43,8 @@ extern crate cast;
|
||||
|
||||
mod code;
|
||||
mod execute;
|
||||
mod export;
|
||||
mod get;
|
||||
mod instance;
|
||||
mod invoke;
|
||||
mod libcalls;
|
||||
@@ -57,10 +59,12 @@ mod world;
|
||||
|
||||
pub use code::Code;
|
||||
pub use execute::{compile_and_link_module, finish_instantiation};
|
||||
pub use export::{ExportValue, NullResolver, Resolver};
|
||||
pub use get::get;
|
||||
pub use instance::Instance;
|
||||
pub use invoke::{invoke, InvokeOutcome, Value};
|
||||
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
||||
pub use vmcontext::VMContext;
|
||||
pub use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
|
||||
pub use world::InstanceWorld;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
|
||||
@@ -124,8 +124,9 @@ impl LinearMemory {
|
||||
Some(prev_pages)
|
||||
}
|
||||
|
||||
/// Return a `VMMemory` for exposing the memory to JIT code.
|
||||
pub fn vmmemory(&mut self) -> VMMemory {
|
||||
VMMemory::new(self.mmap.as_mut_ptr(), self.mmap.len())
|
||||
VMMemory::definition(self.mmap.as_mut_ptr(), self.mmap.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use cast;
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_entity::SecondaryMap;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::SignatureIndex;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use vmcontext::VMSignatureId;
|
||||
@@ -11,14 +11,14 @@ use vmcontext::VMSignatureId;
|
||||
#[derive(Debug)]
|
||||
pub struct SignatureRegistry {
|
||||
signature_hash: HashMap<ir::Signature, VMSignatureId>,
|
||||
signature_ids: SecondaryMap<SignatureIndex, VMSignatureId>,
|
||||
signature_ids: PrimaryMap<SignatureIndex, VMSignatureId>,
|
||||
}
|
||||
|
||||
impl SignatureRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
signature_hash: HashMap::new(),
|
||||
signature_ids: SecondaryMap::new(),
|
||||
signature_ids: PrimaryMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,12 @@ impl SignatureRegistry {
|
||||
}
|
||||
|
||||
/// Register the given signature.
|
||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) -> VMSignatureId {
|
||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) {
|
||||
// TODO: Refactor this interface so that we're not passing in redundant
|
||||
// information.
|
||||
debug_assert_eq!(sig_index.index(), self.signature_ids.len());
|
||||
use cranelift_entity::EntityRef;
|
||||
|
||||
let len = self.signature_hash.len();
|
||||
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||
@@ -37,7 +42,11 @@ impl SignatureRegistry {
|
||||
sig_id
|
||||
}
|
||||
};
|
||||
self.signature_ids[sig_index] = sig_id;
|
||||
sig_id
|
||||
self.signature_ids.push(sig_id);
|
||||
}
|
||||
|
||||
/// Return the identifying runtime index for the given signature.
|
||||
pub fn lookup(&mut self, sig_index: SignatureIndex) -> VMSignatureId {
|
||||
self.signature_ids[sig_index]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,9 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `VMTable` for exposing the table to JIT code.
|
||||
pub fn vmtable(&mut self) -> VMTable {
|
||||
VMTable::new(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
||||
VMTable::definition(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,20 +2,86 @@
|
||||
//! fields that JIT code accesses directly.
|
||||
|
||||
use cranelift_entity::EntityRef;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||
use cranelift_wasm::{Global, GlobalIndex, GlobalInit, MemoryIndex, TableIndex};
|
||||
use instance::Instance;
|
||||
use std::mem::size_of;
|
||||
use std::fmt;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly linear,
|
||||
/// memory, namely the start address and the size in bytes.
|
||||
#[derive(Debug)]
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||
/// memory defined within the instance, namely the start address and the
|
||||
/// size in bytes.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMMemory {
|
||||
pub struct VMMemoryDefinition {
|
||||
/// The start address.
|
||||
base: *mut u8,
|
||||
/// The current size of linear memory in bytes.
|
||||
current_length: usize,
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmmemory_definition {
|
||||
use super::VMMemoryDefinition;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmmemory_definition_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMMemoryDefinition>(),
|
||||
usize::from(offsets.size_of_vmmemory_definition())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemoryDefinition, base),
|
||||
usize::from(offsets.vmmemory_definition_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemoryDefinition, current_length),
|
||||
usize::from(offsets.vmmemory_definition_current_length())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||
/// memory imported from another instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMMemoryImport {
|
||||
/// A pointer to the imported memory description.
|
||||
from: *mut VMMemoryDefinition,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmmemory_import {
|
||||
use super::VMMemoryImport;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmmemory_import_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMMemoryImport>(),
|
||||
usize::from(offsets.size_of_vmmemory_import())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemoryImport, from),
|
||||
usize::from(offsets.vmmemory_import_from())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly linear
|
||||
/// memory. It must know whether the memory is defined within the instance
|
||||
/// or imported.
|
||||
#[repr(C)]
|
||||
pub union VMMemory {
|
||||
/// A linear memory defined within the instance.
|
||||
definition: VMMemoryDefinition,
|
||||
|
||||
/// An imported linear memory.
|
||||
import: VMMemoryImport,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -31,71 +97,155 @@ mod test_vmmemory {
|
||||
size_of::<VMMemory>(),
|
||||
usize::from(offsets.size_of_vmmemory())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemory, base),
|
||||
usize::from(offsets.vmmemory_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMMemory, current_length),
|
||||
usize::from(offsets.vmmemory_current_length())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMMemory {
|
||||
pub fn new(base: *mut u8, current_length: usize) -> Self {
|
||||
/// Construct a `VMMemoryDefinition` variant of `VMMemory`.
|
||||
pub fn definition(base: *mut u8, current_length: usize) -> Self {
|
||||
Self {
|
||||
definition: VMMemoryDefinition {
|
||||
base,
|
||||
current_length,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.base, self.current_length) }
|
||||
}
|
||||
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.base, self.current_length) }
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.current_length
|
||||
/// Construct a `VMMemoryImmport` variant of `VMMemory`.
|
||||
pub fn import(from: *mut VMMemoryDefinition) -> Self {
|
||||
Self {
|
||||
import: VMMemoryImport { from },
|
||||
}
|
||||
}
|
||||
|
||||
/// The storage for a WebAssembly global.
|
||||
/// 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.
|
||||
///
|
||||
/// TODO: Pack the globals more densely, rather than using the same size
|
||||
/// for every type.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct VMGlobal {
|
||||
pub struct VMGlobalDefinition {
|
||||
storage: [u8; 8],
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal {
|
||||
use super::VMGlobal;
|
||||
mod test_vmglobal_definition {
|
||||
use super::VMGlobalDefinition;
|
||||
use std::mem::{align_of, size_of};
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_alignment() {
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<i32>());
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<i64>());
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<f32>());
|
||||
assert!(align_of::<VMGlobal>() >= align_of::<f64>());
|
||||
fn check_vmglobal_definition_alignment() {
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>());
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>());
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>());
|
||||
assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_definition_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMGlobalDefinition>(),
|
||||
usize::from(offsets.size_of_vmglobal_definition())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMGlobalDefinition {
|
||||
pub unsafe fn as_i32(&mut self) -> &mut i32 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i32)
|
||||
}
|
||||
|
||||
pub unsafe fn as_i64(&mut self) -> &mut i64 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i64)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f32(&mut self) -> &mut f32 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f32)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f32_bits(&mut self) -> &mut u32 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u32)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f64(&mut self) -> &mut f64 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f64)
|
||||
}
|
||||
|
||||
pub unsafe fn as_f64_bits(&mut self) -> &mut u64 {
|
||||
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly global
|
||||
/// variable imported from another instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMGlobalImport {
|
||||
/// A pointer to the imported global variable description.
|
||||
from: *mut VMGlobalDefinition,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal_import {
|
||||
use super::VMGlobalImport;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_import_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMGlobalImport>(),
|
||||
usize::from(offsets.size_of_vmglobal_import())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMGlobalImport, from),
|
||||
usize::from(offsets.vmglobal_import_from())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly global
|
||||
/// variable. It must know whether the global variable is defined within the
|
||||
/// instance or imported.
|
||||
#[repr(C)]
|
||||
pub union VMGlobal {
|
||||
/// A global variable defined within the instance.
|
||||
definition: VMGlobalDefinition,
|
||||
|
||||
/// An imported global variable.
|
||||
import: VMGlobalImport,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmglobal {
|
||||
use super::VMGlobal;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmglobal_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
@@ -106,20 +256,122 @@ mod test_vmglobal {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMGlobal {
|
||||
fn default() -> Self {
|
||||
VMGlobal { storage: [0; 8] }
|
||||
impl VMGlobal {
|
||||
/// Construct a `VMGlobalDefinition` variant of `VMGlobal`.
|
||||
pub fn definition(global: &Global) -> Self {
|
||||
let mut result = VMGlobalDefinition { storage: [0; 8] };
|
||||
unsafe {
|
||||
match global.initializer {
|
||||
GlobalInit::I32Const(x) => *result.as_i32() = x,
|
||||
GlobalInit::I64Const(x) => *result.as_i64() = x,
|
||||
GlobalInit::F32Const(x) => *result.as_f32_bits() = x,
|
||||
GlobalInit::F64Const(x) => *result.as_f64_bits() = x,
|
||||
GlobalInit::GetGlobal(_x) => unimplemented!("globals init with get_global"),
|
||||
GlobalInit::Import => panic!("attempting to initialize imported global"),
|
||||
}
|
||||
}
|
||||
Self { definition: result }
|
||||
}
|
||||
|
||||
/// Construct a `VMGlobalImmport` variant of `VMGlobal`.
|
||||
pub fn import(from: *mut VMGlobalDefinition) -> Self {
|
||||
Self {
|
||||
import: VMGlobalImport { from },
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly table,
|
||||
/// namely the start address and the number of elements.
|
||||
/// 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 VMTable {
|
||||
pub struct VMTableDefinition {
|
||||
base: *mut u8,
|
||||
current_elements: usize,
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmtable_definition {
|
||||
use super::VMTableDefinition;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmtable_definition_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMTableDefinition>(),
|
||||
usize::from(offsets.size_of_vmtable_definition())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTableDefinition, base),
|
||||
usize::from(offsets.vmtable_definition_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTableDefinition, current_elements),
|
||||
usize::from(offsets.vmtable_definition_current_elements())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The fields a JIT needs to access to utilize a WebAssembly table
|
||||
/// imported from another instance.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMTableImport {
|
||||
/// A pointer to the imported table description.
|
||||
from: *mut VMTableDefinition,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmtable_import {
|
||||
use super::VMTableImport;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmtable_import_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMTableImport>(),
|
||||
usize::from(offsets.size_of_vmtable_import())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTableImport, from),
|
||||
usize::from(offsets.vmtable_import_from())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly table.
|
||||
/// It must know whether the table is defined within the instance
|
||||
/// or imported.
|
||||
#[repr(C)]
|
||||
pub union VMTable {
|
||||
/// A table defined within the instance.
|
||||
definition: VMTableDefinition,
|
||||
|
||||
/// An imported table.
|
||||
import: VMTableImport,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -132,43 +384,44 @@ mod test_vmtable {
|
||||
fn check_vmtable_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(size_of::<VMTable>(), usize::from(offsets.size_of_vmtable()));
|
||||
assert_eq!(
|
||||
offset_of!(VMTable, base),
|
||||
usize::from(offsets.vmtable_base())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMTable, current_elements),
|
||||
usize::from(offsets.vmtable_current_elements())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMTable {
|
||||
pub fn new(base: *mut u8, current_elements: usize) -> Self {
|
||||
/// Construct a `VMTableDefinition` variant of `VMTable`.
|
||||
pub fn definition(base: *mut u8, current_elements: usize) -> Self {
|
||||
Self {
|
||||
definition: VMTableDefinition {
|
||||
base,
|
||||
current_elements,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.base, self.current_elements) }
|
||||
/// Construct a `VMTableImmport` variant of `VMTable`.
|
||||
pub fn import(from: *mut VMTableDefinition) -> Self {
|
||||
Self {
|
||||
import: VMTableImport { from },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.base, self.current_elements) }
|
||||
/// Get the underlying `VMTableDefinition`.
|
||||
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMTableDefinition {
|
||||
if is_import {
|
||||
&mut *self.import.from
|
||||
} else {
|
||||
&mut self.definition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.current_elements
|
||||
impl fmt::Debug for VMTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "VMTable {{")?;
|
||||
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||
write!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +491,10 @@ impl Default for VMCallerCheckedAnyfunc {
|
||||
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
|
||||
/// This has pointers to the globals, memories, tables, and other runtime
|
||||
/// state associated with the current instance.
|
||||
///
|
||||
/// TODO: The number of memories, globals, tables, and signature IDs does
|
||||
/// not change dynamically, and pointer arrays are not indexed dynamically,
|
||||
/// so these fields could all be contiguously allocated.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct VMContext {
|
||||
@@ -300,28 +557,8 @@ impl VMContext {
|
||||
}
|
||||
|
||||
/// Return the base pointer of the globals array.
|
||||
pub unsafe fn global_storage(&mut self, index: GlobalIndex) -> *mut VMGlobal {
|
||||
self.globals.add(index.index() * size_of::<VMGlobal>())
|
||||
}
|
||||
|
||||
/// Return a mutable reference to global `index` which has type i32.
|
||||
pub unsafe fn global_i32(&mut self, index: GlobalIndex) -> &mut i32 {
|
||||
&mut *(self.global_storage(index) as *mut i32)
|
||||
}
|
||||
|
||||
/// Return a mutable reference to global `index` which has type i64.
|
||||
pub unsafe fn global_i64(&mut self, index: GlobalIndex) -> &mut i64 {
|
||||
&mut *(self.global_storage(index) as *mut i64)
|
||||
}
|
||||
|
||||
/// Return a mutable reference to global `index` which has type f32.
|
||||
pub unsafe fn global_f32(&mut self, index: GlobalIndex) -> &mut f32 {
|
||||
&mut *(self.global_storage(index) as *mut f32)
|
||||
}
|
||||
|
||||
/// Return a mutable reference to global `index` which has type f64.
|
||||
pub unsafe fn global_f64(&mut self, index: GlobalIndex) -> &mut f64 {
|
||||
&mut *(self.global_storage(index) as *mut f64)
|
||||
pub unsafe fn global(&mut self, index: GlobalIndex) -> &mut VMGlobal {
|
||||
&mut *self.globals.add(index.index())
|
||||
}
|
||||
|
||||
/// Return a mutable reference to linear memory `index`.
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex};
|
||||
use export::Resolver;
|
||||
use std::str;
|
||||
use vmcontext::VMGlobal;
|
||||
use wasmtime_environ::{Compilation, Module, ModuleEnvironment, Tunables};
|
||||
use {compile_and_link_module, finish_instantiation, invoke, Code, Instance, InvokeOutcome, Value};
|
||||
use {
|
||||
compile_and_link_module, finish_instantiation, get, invoke, Code, Instance, InvokeOutcome,
|
||||
Value,
|
||||
};
|
||||
|
||||
/// A module, an instance of that module, and accompanying compilation artifacts.
|
||||
///
|
||||
@@ -14,8 +20,14 @@ pub struct InstanceWorld {
|
||||
|
||||
impl InstanceWorld {
|
||||
/// Create a new `InstanceWorld` by compiling the wasm module in `data` and instatiating it.
|
||||
pub fn new(code: &mut Code, isa: &isa::TargetIsa, data: &[u8]) -> Result<Self, String> {
|
||||
pub fn new(
|
||||
code: &mut Code,
|
||||
isa: &isa::TargetIsa,
|
||||
data: &[u8],
|
||||
resolver: &mut Resolver,
|
||||
) -> Result<Self, String> {
|
||||
let mut module = Module::new();
|
||||
// TODO: Allow the tunables to be overridden.
|
||||
let tunables = Tunables::default();
|
||||
let (instance, compilation) = {
|
||||
let translation = {
|
||||
@@ -24,9 +36,7 @@ impl InstanceWorld {
|
||||
environ.translate(&data).map_err(|e| e.to_string())?
|
||||
};
|
||||
|
||||
let imports_resolver = |_env: &str, _function: &str| None;
|
||||
|
||||
let compilation = compile_and_link_module(isa, &translation, &imports_resolver)?;
|
||||
let compilation = compile_and_link_module(isa, &translation, resolver)?;
|
||||
let mut instance = Instance::new(
|
||||
translation.module,
|
||||
&compilation,
|
||||
@@ -64,4 +74,19 @@ impl InstanceWorld {
|
||||
)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Read a global in this `InstanceWorld` by name.
|
||||
pub fn get(&mut self, global_name: &str) -> Result<Value, String> {
|
||||
get(&self.module, self.instance.vmctx(), global_name).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Returns a slice of the contents of allocated linear memory.
|
||||
pub fn inspect_memory(&self, memory_index: MemoryIndex, address: usize, len: usize) -> &[u8] {
|
||||
self.instance.inspect_memory(memory_index, address, len)
|
||||
}
|
||||
|
||||
/// Shows the value of a global variable.
|
||||
pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal {
|
||||
self.instance.inspect_global(global_index)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,3 @@ for writing out native object files, using the wasm ABI defined by
|
||||
[`wasmtime-environ`].
|
||||
|
||||
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ
|
||||
|
||||
|
||||
@@ -12,8 +12,11 @@ readme = "README.md"
|
||||
[dependencies]
|
||||
cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||
cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||
wasmtime-execute = { path = "../execute" }
|
||||
wasmtime-environ = { path = "../environ" }
|
||||
wabt = "0.7"
|
||||
target-lexicon = "0.2.0"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
@@ -94,8 +94,7 @@ fn ignore(testsuite: &str, name: &str) -> bool {
|
||||
match testsuite {
|
||||
"spec_testsuite" => match name {
|
||||
// These are the remaining spec testsuite failures.
|
||||
"data" | "elem" | "exports" | "func" | "func_ptrs" | "globals" | "imports"
|
||||
| "linking" | "names" | "start" => true,
|
||||
"data" | "elem" | "imports" | "linking" => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
)]
|
||||
|
||||
extern crate cranelift_codegen;
|
||||
extern crate cranelift_wasm;
|
||||
extern crate target_lexicon;
|
||||
extern crate wabt;
|
||||
extern crate wasmtime_environ;
|
||||
extern crate wasmtime_execute;
|
||||
|
||||
mod spectest;
|
||||
mod wast;
|
||||
|
||||
pub use wast::{wast_buffer, wast_file};
|
||||
|
||||
210
lib/wast/src/spectest.rs
Normal file
210
lib/wast/src/spectest.rs
Normal file
@@ -0,0 +1,210 @@
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_codegen::{ir, isa};
|
||||
use cranelift_wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
||||
use std::ptr;
|
||||
use target_lexicon::HOST;
|
||||
use wasmtime_environ::{translate_signature, MemoryPlan, MemoryStyle, TablePlan, TableStyle};
|
||||
use wasmtime_execute::{ExportValue, Resolver, VMGlobal, VMMemory, VMTable};
|
||||
|
||||
extern "C" fn spectest_print() {}
|
||||
|
||||
extern "C" fn spectest_print_i32(x: i32) {
|
||||
println!("{}: i32", x);
|
||||
}
|
||||
|
||||
extern "C" fn spectest_print_i64(x: i64) {
|
||||
println!("{}: i64", x);
|
||||
}
|
||||
|
||||
extern "C" fn spectest_print_f32(x: f32) {
|
||||
println!("{}: f32", x);
|
||||
}
|
||||
|
||||
extern "C" fn spectest_print_f64(x: f64) {
|
||||
println!("{}: f64", x);
|
||||
}
|
||||
|
||||
extern "C" fn spectest_print_i32_f32(x: i32, y: f32) {
|
||||
println!("{}: i32", x);
|
||||
println!("{}: f32", y);
|
||||
}
|
||||
|
||||
extern "C" fn spectest_print_f64_f64(x: f64, y: f64) {
|
||||
println!("{}: f64", x);
|
||||
println!("{}: f64", y);
|
||||
}
|
||||
|
||||
pub struct SpecTest {
|
||||
spectest_global_i32: VMGlobal,
|
||||
spectest_global_f32: VMGlobal,
|
||||
spectest_global_f64: VMGlobal,
|
||||
spectest_table: VMTable,
|
||||
spectest_memory: VMMemory,
|
||||
}
|
||||
|
||||
impl SpecTest {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
spectest_global_i32: VMGlobal::definition(&Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I32Const(0),
|
||||
}),
|
||||
spectest_global_f32: VMGlobal::definition(&Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F32Const(0),
|
||||
}),
|
||||
spectest_global_f64: VMGlobal::definition(&Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F64Const(0),
|
||||
}),
|
||||
spectest_table: VMTable::definition(ptr::null_mut(), 0),
|
||||
spectest_memory: VMMemory::definition(ptr::null_mut(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolver for SpecTest {
|
||||
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue> {
|
||||
let call_conv = isa::CallConv::triple_default(&HOST);
|
||||
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||
match module {
|
||||
"spectest" => match field {
|
||||
"print" => Some(ExportValue::function(
|
||||
spectest_print as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_i32" => Some(ExportValue::function(
|
||||
spectest_print_i32 as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_i64" => Some(ExportValue::function(
|
||||
spectest_print_i64 as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_f32" => Some(ExportValue::function(
|
||||
spectest_print_f32 as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_f64" => Some(ExportValue::function(
|
||||
spectest_print_f64 as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_i32_f32" => Some(ExportValue::function(
|
||||
spectest_print_i32_f32 as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![
|
||||
ir::AbiParam::new(types::I32),
|
||||
ir::AbiParam::new(types::F32),
|
||||
],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_f64_f64" => Some(ExportValue::function(
|
||||
spectest_print_f64_f64 as usize,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![
|
||||
ir::AbiParam::new(types::F64),
|
||||
ir::AbiParam::new(types::F64),
|
||||
],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"global_i32" => Some(ExportValue::global(
|
||||
&mut self.spectest_global_i32,
|
||||
Global {
|
||||
ty: ir::types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I32Const(0),
|
||||
},
|
||||
)),
|
||||
"global_f32" => Some(ExportValue::global(
|
||||
&mut self.spectest_global_f32,
|
||||
Global {
|
||||
ty: ir::types::F32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F32Const(0),
|
||||
},
|
||||
)),
|
||||
"global_f64" => Some(ExportValue::global(
|
||||
&mut self.spectest_global_f64,
|
||||
Global {
|
||||
ty: ir::types::F64,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F64Const(0),
|
||||
},
|
||||
)),
|
||||
"table" => Some(ExportValue::table(
|
||||
&mut self.spectest_table,
|
||||
TablePlan {
|
||||
table: Table {
|
||||
ty: TableElementType::Func,
|
||||
minimum: 0,
|
||||
maximum: None,
|
||||
},
|
||||
style: TableStyle::CallerChecksSignature,
|
||||
},
|
||||
)),
|
||||
"memory" => Some(ExportValue::memory(
|
||||
&mut self.spectest_memory,
|
||||
MemoryPlan {
|
||||
memory: Memory {
|
||||
minimum: 0,
|
||||
maximum: None,
|
||||
shared: false,
|
||||
},
|
||||
style: MemoryStyle::Dynamic,
|
||||
offset_guard_size: 0,
|
||||
},
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use cranelift_codegen::isa;
|
||||
use spectest::SpecTest;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
@@ -12,6 +13,7 @@ struct Instances {
|
||||
current: Option<InstanceWorld>,
|
||||
namespace: HashMap<String, InstanceWorld>,
|
||||
code: Code,
|
||||
spectest: SpecTest,
|
||||
}
|
||||
|
||||
impl Instances {
|
||||
@@ -20,11 +22,12 @@ impl Instances {
|
||||
current: None,
|
||||
namespace: HashMap::new(),
|
||||
code: Code::new(),
|
||||
spectest: SpecTest::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate(&mut self, isa: &isa::TargetIsa, module: ModuleBinary) -> InstanceWorld {
|
||||
InstanceWorld::new(&mut self.code, isa, &module.into_vec()).unwrap()
|
||||
InstanceWorld::new(&mut self.code, isa, &module.into_vec(), &mut self.spectest).unwrap()
|
||||
}
|
||||
|
||||
pub fn define_unnamed_module(&mut self, isa: &isa::TargetIsa, module: ModuleBinary) {
|
||||
@@ -41,6 +44,7 @@ impl Instances {
|
||||
self.namespace.insert(name, world);
|
||||
}
|
||||
|
||||
// fixme: Rename InvokeOutcome to ActionOutcome.
|
||||
pub fn perform_action(&mut self, isa: &isa::TargetIsa, action: Action) -> InvokeOutcome {
|
||||
match action {
|
||||
Action::Invoke {
|
||||
@@ -72,7 +76,25 @@ impl Instances {
|
||||
.expect(&format!("error invoking {} in module {}", field, name)),
|
||||
}
|
||||
}
|
||||
_ => panic!("unsupported action {:?}", action),
|
||||
Action::Get { module, field } => {
|
||||
let value = match module {
|
||||
None => match self.current {
|
||||
None => panic!("get performed with no module present"),
|
||||
Some(ref mut instance_world) => instance_world
|
||||
.get(&field)
|
||||
.expect(&format!("error getting {} in current module", field)),
|
||||
},
|
||||
Some(name) => self
|
||||
.namespace
|
||||
.get_mut(&name)
|
||||
.expect(&format!("module {} not declared", name))
|
||||
.get(&field)
|
||||
.expect(&format!("error getting {} in module {}", field, name)),
|
||||
};
|
||||
InvokeOutcome::Returned {
|
||||
values: vec![value],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ extern crate cranelift_entity;
|
||||
extern crate cranelift_native;
|
||||
extern crate cranelift_wasm;
|
||||
extern crate docopt;
|
||||
extern crate wasmtime_environ;
|
||||
extern crate wasmtime_execute;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
@@ -57,8 +56,7 @@ use std::io::stdout;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
use wasmtime_environ::{Module, ModuleEnvironment, Tunables};
|
||||
use wasmtime_execute::{compile_and_link_module, finish_instantiation, invoke, Code, Instance};
|
||||
use wasmtime_execute::{Code, InstanceWorld, NullResolver};
|
||||
|
||||
static LOG_FILENAME_PREFIX: &str = "cranelift.dbg.";
|
||||
|
||||
@@ -147,51 +145,14 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
|
||||
if !data.starts_with(&[b'\0', b'a', b's', b'm']) {
|
||||
data = wabt::wat2wasm(data).map_err(|err| String::from(err.description()))?;
|
||||
}
|
||||
let mut module = Module::new();
|
||||
// TODO: Expose the tunables as command-line flags.
|
||||
let tunables = Tunables::default();
|
||||
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
|
||||
|
||||
let imports_resolver = |_env: &str, _function: &str| None;
|
||||
|
||||
let translation = environ.translate(&data).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut resolver = NullResolver {};
|
||||
let mut code = Code::new();
|
||||
|
||||
let instance = match compile_and_link_module(isa, &translation, &imports_resolver) {
|
||||
Ok(compilation) => {
|
||||
let mut instance = Instance::new(
|
||||
translation.module,
|
||||
&compilation,
|
||||
&translation.lazy.data_initializers,
|
||||
)?;
|
||||
|
||||
finish_instantiation(
|
||||
&mut code,
|
||||
isa,
|
||||
&translation.module,
|
||||
&compilation,
|
||||
&mut instance,
|
||||
)?;
|
||||
let mut world = InstanceWorld::new(&mut code, isa, &data, &mut resolver)?;
|
||||
|
||||
if let Some(ref f) = args.flag_function {
|
||||
invoke(
|
||||
&mut code,
|
||||
isa,
|
||||
&translation.module,
|
||||
&compilation,
|
||||
instance.vmctx(),
|
||||
&f,
|
||||
&[],
|
||||
)?;
|
||||
world.invoke(&mut code, isa, &f, &[])?;
|
||||
}
|
||||
|
||||
instance
|
||||
}
|
||||
Err(s) => {
|
||||
return Err(s);
|
||||
}
|
||||
};
|
||||
if args.flag_memory {
|
||||
let mut input = String::new();
|
||||
println!("Inspecting memory");
|
||||
@@ -210,7 +171,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
|
||||
if split.len() != 3 {
|
||||
break;
|
||||
}
|
||||
let memory = instance.inspect_memory(
|
||||
let memory = world.inspect_memory(
|
||||
MemoryIndex::new(str::parse(split[0]).unwrap()),
|
||||
str::parse(split[1]).unwrap(),
|
||||
str::parse(split[2]).unwrap(),
|
||||
@@ -235,7 +196,7 @@ mod tests {
|
||||
use cranelift_codegen::settings::Configurable;
|
||||
use std::path::PathBuf;
|
||||
use wabt;
|
||||
use wasmtime_environ::{Module, ModuleEnvironment, Tunables};
|
||||
use wasmtime_execute::{Code, InstanceWorld, NullResolver};
|
||||
|
||||
const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat";
|
||||
|
||||
@@ -257,11 +218,9 @@ mod tests {
|
||||
});
|
||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||
|
||||
let mut module = Module::new();
|
||||
let tunables = Tunables::default();
|
||||
let environ = ModuleEnvironment::new(&*isa, &mut module, tunables);
|
||||
|
||||
let translation = environ.translate(&data);
|
||||
assert!(translation.is_ok());
|
||||
let mut resolver = NullResolver {};
|
||||
let mut code = Code::new();
|
||||
let world = InstanceWorld::new(&mut code, &*isa, &data, &mut resolver);
|
||||
assert!(world.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user