From d9b4bd1de853823150e80d99e87c12c9dbcb84a5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Thu, 6 Dec 2018 02:02:44 -0500 Subject: [PATCH] Support imports. --- lib/environ/src/compilation.rs | 25 +- lib/environ/src/environ.rs | 141 ++++++++--- lib/environ/src/lib.rs | 2 +- lib/environ/src/module.rs | 32 +++ lib/environ/src/vmoffsets.rs | 137 ++++++++-- lib/execute/src/execute.rs | 273 ++++++++++++++++++-- lib/execute/src/export.rs | 76 ++++++ lib/execute/src/get.rs | 39 +++ lib/execute/src/instance.rs | 22 +- lib/execute/src/invoke.rs | 16 +- lib/execute/src/lib.rs | 6 +- lib/execute/src/memory.rs | 3 +- lib/execute/src/sig_registry.rs | 21 +- lib/execute/src/table.rs | 3 +- lib/execute/src/vmcontext.rs | 437 ++++++++++++++++++++++++-------- lib/execute/src/world.rs | 35 ++- lib/obj/README.md | 1 - lib/wast/Cargo.toml | 3 + lib/wast/build.rs | 3 +- lib/wast/src/lib.rs | 4 + lib/wast/src/spectest.rs | 210 +++++++++++++++ lib/wast/src/wast.rs | 26 +- src/wasmtime.rs | 65 +---- 23 files changed, 1312 insertions(+), 268 deletions(-) create mode 100644 lib/execute/src/export.rs create mode 100644 lib/execute/src/get.rs create mode 100644 lib/wast/src/spectest.rs diff --git a/lib/environ/src/compilation.rs b/lib/environ/src/compilation.rs index 6540bc0118..9ceee4da5e 100644 --- a/lib/environ/src/compilation.rs +++ b/lib/environ/src/compilation.rs @@ -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>, + + /// Resolved function addresses for imported functions. + pub resolved_func_imports: PrimaryMap, + + /// Resolved function addresses for imported tables. + pub resolved_table_imports: PrimaryMap, + + /// Resolved function addresses for imported globals. + pub resolved_global_imports: PrimaryMap, + + /// Resolved function addresses for imported memories. + pub resolved_memory_imports: PrimaryMap, } impl Compilation { /// Allocates the compilation result with the given function bodies. pub fn new(functions: PrimaryMap>) -> 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)) } diff --git a/lib/environ/src/environ.rs b/lib/environ/src/environ.rs index 1117bc4d6f..6af9bc2f81 100644 --- a/lib/environ/src/environ.rs +++ b/lib/environ/src/environ.rs @@ -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, ) { - 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 { - base: globals_base, - offset: Imm64::new(i64::from(self.offsets.index_vmglobal(index.as_u32()))), - global_type: pointer_type, - }); + 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, + 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 { 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 { 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 +} diff --git a/lib/environ/src/lib.rs b/lib/environ/src/lib.rs index dfd3d83a62..72f0bebbb5 100644 --- a/lib/environ/src/lib.rs +++ b/lib/environ/src/lib.rs @@ -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, }; diff --git a/lib/environ/src/module.rs b/lib/environ/src/module.rs index 6dfff7ddcc..19c60bc2a7 100644 --- a/lib/environ/src/module.rs +++ b/lib/environ/src/module.rs @@ -139,6 +139,15 @@ pub struct Module { /// Names of imported functions. pub imported_funcs: PrimaryMap, + /// Names of imported tables. + pub imported_tables: PrimaryMap, + + /// Names of imported globals. + pub imported_globals: PrimaryMap, + + /// Names of imported memories. + pub imported_memories: PrimaryMap, + /// Types of functions, imported and local. pub functions: PrimaryMap, @@ -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. diff --git a/lib/environ/src/vmoffsets.rs b/lib/environ/src/vmoffsets.rs index 4b3ab0961a..16884c834b 100644 --- a/lib/environ/src/vmoffsets.rs +++ b/lib/environ/src/vmoffsets.rs @@ -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() } } diff --git a/lib/execute/src/execute.rs b/lib/execute/src/execute.rs index 1ac5e88732..06d196a47b 100644 --- a/lib/execute/src/execute.rs +++ b/lib/execute/src/execute.rs @@ -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 -where - F: Fn(&str, &str) -> Option, -{ + resolver: &mut Resolver, +) -> Result { 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( +/// Performs the relocations inside the function bytecode, provided the necessary metadata. +fn relocate( compilation: &mut Compilation, relocations: &PrimaryMap>, module: &Module, - imports: F, -) where - F: Fn(&str, &str) -> Option, -{ - // 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( } } } + Ok(()) } extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 { diff --git a/lib/execute/src/export.rs b/lib/execute/src/export.rs new file mode 100644 index 0000000000..0b7197ddbf --- /dev/null +++ b/lib/execute/src/export.rs @@ -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; +} + +/// `Resolver` implementation that always resolves to `None`. +pub struct NullResolver {} + +impl Resolver for NullResolver { + fn resolve(&mut self, _module: &str, _field: &str) -> Option { + None + } +} diff --git a/lib/execute/src/get.rs b/lib/execute/src/get.rs new file mode 100644 index 0000000000..6d4014a06f --- /dev/null +++ b/lib/execute/src/get.rs @@ -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 { + 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 { + // 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)), + }) + } +} diff --git a/lib/execute/src/instance.rs b/lib/execute/src/instance.rs index bf8b690912..5d6e86b5d7 100644 --- a/lib/execute/src/instance.rs +++ b/lib/execute/src/instance.rs @@ -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 { - 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 { 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 diff --git a/lib/execute/src/invoke.rs b/lib/execute/src/invoke.rs index 82649a2558..6f286f8aa6 100644 --- a/lib/execute/src/invoke.rs +++ b/lib/execute/src/invoke.rs @@ -114,12 +114,16 @@ pub fn invoke_by_index( fn_index: FuncIndex, args: &[Value], ) -> Result { - 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( diff --git a/lib/execute/src/lib.rs b/lib/execute/src/lib.rs index 2dc0cb6bd0..4b89f1f76f 100644 --- a/lib/execute/src/lib.rs +++ b/lib/execute/src/lib.rs @@ -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"))] diff --git a/lib/execute/src/memory.rs b/lib/execute/src/memory.rs index 033369dce2..e5f7c78ef3 100644 --- a/lib/execute/src/memory.rs +++ b/lib/execute/src/memory.rs @@ -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()) } } diff --git a/lib/execute/src/sig_registry.rs b/lib/execute/src/sig_registry.rs index 264a299c23..18f624e8f5 100644 --- a/lib/execute/src/sig_registry.rs +++ b/lib/execute/src/sig_registry.rs @@ -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, - signature_ids: SecondaryMap, + signature_ids: PrimaryMap, } 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] } } diff --git a/lib/execute/src/table.rs b/lib/execute/src/table.rs index 1fd8470882..1338b3b9c6 100644 --- a/lib/execute/src/table.rs +++ b/lib/execute/src/table.rs @@ -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()) } } diff --git a/lib/execute/src/vmcontext.rs b/lib/execute/src/vmcontext.rs index 67b702cc67..58350f9aea 100644 --- a/lib/execute/src/vmcontext.rs +++ b/lib/execute/src/vmcontext.rs @@ -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::(), + 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::(), + 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::(), usize::from(offsets.size_of_vmmemory()) ); - assert_eq!( - offset_of!(VMMemory, base), - usize::from(offsets.vmmemory_base()) - ); - assert_eq!( - offset_of!(VMMemory, current_length), - usize::from(offsets.vmmemory_current_length()) - ); } } impl VMMemory { - pub fn new(base: *mut u8, current_length: usize) -> Self { + /// Construct a `VMMemoryDefinition` variant of `VMMemory`. + pub fn definition(base: *mut u8, current_length: usize) -> Self { Self { - base, - current_length, + definition: VMMemoryDefinition { + base, + current_length, + }, } } - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.base, self.current_length) } + /// Construct a `VMMemoryImmport` variant of `VMMemory`. + pub fn import(from: *mut VMMemoryDefinition) -> Self { + Self { + import: VMMemoryImport { from }, + } } - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.base, self.current_length) } - } - - pub fn as_ptr(&self) -> *const u8 { - self.base - } - - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.base - } - - pub fn len(&self) -> usize { - self.current_length + /// Get the underlying `VMMemoryDefinition`. + pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMMemoryDefinition { + if is_import { + &mut *self.import.from + } else { + &mut self.definition + } } } -/// The storage for a WebAssembly global. +impl fmt::Debug for VMMemory { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VMMemory {{")?; + write!(f, " definition: {:?},", unsafe { self.definition })?; + write!(f, " import: {:?},", unsafe { self.import })?; + write!(f, "}}")?; + Ok(()) + } +} + +/// The storage for a WebAssembly global defined within the instance. /// /// TODO: Pack the globals more densely, rather than using the same size /// for every type. -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] #[repr(C, align(8))] -pub struct VMGlobal { +pub struct VMGlobalDefinition { storage: [u8; 8], // If more elements are added here, remember to add offset_of tests below! } #[cfg(test)] -mod test_vmglobal { - use super::VMGlobal; +mod test_vmglobal_definition { + use super::VMGlobalDefinition; use std::mem::{align_of, size_of}; use wasmtime_environ::VMOffsets; #[test] - fn check_vmglobal_alignment() { - assert!(align_of::() >= align_of::()); - assert!(align_of::() >= align_of::()); - assert!(align_of::() >= align_of::()); - assert!(align_of::() >= align_of::()); + fn check_vmglobal_definition_alignment() { + assert!(align_of::() >= align_of::()); + assert!(align_of::() >= align_of::()); + assert!(align_of::() >= align_of::()); + assert!(align_of::() >= align_of::()); } + #[test] + fn check_vmglobal_definition_offsets() { + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8); + assert_eq!( + size_of::(), + 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::(), + usize::from(offsets.size_of_vmglobal_import()) + ); + assert_eq!( + offset_of!(VMGlobalImport, from), + usize::from(offsets.vmglobal_import_from()) + ); + } +} + +/// The main fields a JIT needs to access to utilize a WebAssembly global +/// variable. It must know whether the global variable is defined within the +/// instance or imported. +#[repr(C)] +pub union VMGlobal { + /// A global variable defined within the instance. + definition: VMGlobalDefinition, + + /// An imported global variable. + import: VMGlobalImport, +} + +#[cfg(test)] +mod test_vmglobal { + use super::VMGlobal; + use std::mem::size_of; + use wasmtime_environ::VMOffsets; + #[test] fn check_vmglobal_offsets() { let offsets = VMOffsets::new(size_of::<*mut u8>() as u8); @@ -106,20 +256,122 @@ mod test_vmglobal { } } -impl Default for VMGlobal { - fn default() -> Self { - VMGlobal { storage: [0; 8] } +impl VMGlobal { + /// Construct a `VMGlobalDefinition` variant of `VMGlobal`. + pub fn definition(global: &Global) -> Self { + let mut result = VMGlobalDefinition { storage: [0; 8] }; + unsafe { + match global.initializer { + GlobalInit::I32Const(x) => *result.as_i32() = x, + GlobalInit::I64Const(x) => *result.as_i64() = x, + GlobalInit::F32Const(x) => *result.as_f32_bits() = x, + GlobalInit::F64Const(x) => *result.as_f64_bits() = x, + GlobalInit::GetGlobal(_x) => unimplemented!("globals init with get_global"), + GlobalInit::Import => panic!("attempting to initialize imported global"), + } + } + Self { definition: result } + } + + /// Construct a `VMGlobalImmport` variant of `VMGlobal`. + pub fn import(from: *mut VMGlobalDefinition) -> Self { + Self { + import: VMGlobalImport { from }, + } + } + + /// Get the underlying `VMGlobalDefinition`. + pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMGlobalDefinition { + if is_import { + &mut *self.import.from + } else { + &mut self.definition + } } } -#[derive(Debug)] -/// The main fields a JIT needs to access to utilize a WebAssembly table, -/// namely the start address and the number of elements. +impl fmt::Debug for VMGlobal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VMGlobal {{")?; + write!(f, " definition: {:?},", unsafe { self.definition })?; + write!(f, " import: {:?},", unsafe { self.import })?; + write!(f, "}}")?; + Ok(()) + } +} + +/// The fields a JIT needs to access to utilize a WebAssembly table +/// defined within the instance. +#[derive(Debug, Copy, Clone)] #[repr(C)] -pub struct VMTable { +pub struct VMTableDefinition { base: *mut u8, current_elements: usize, - // If more elements are added here, remember to add offset_of tests below! +} + +#[cfg(test)] +mod test_vmtable_definition { + use super::VMTableDefinition; + use std::mem::size_of; + use wasmtime_environ::VMOffsets; + + #[test] + fn check_vmtable_definition_offsets() { + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8); + assert_eq!( + size_of::(), + 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::(), + 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::(), usize::from(offsets.size_of_vmtable())); - assert_eq!( - offset_of!(VMTable, base), - usize::from(offsets.vmtable_base()) - ); - assert_eq!( - offset_of!(VMTable, current_elements), - usize::from(offsets.vmtable_current_elements()) - ); } } impl VMTable { - pub fn new(base: *mut u8, current_elements: usize) -> Self { + /// Construct a `VMTableDefinition` variant of `VMTable`. + pub fn definition(base: *mut u8, current_elements: usize) -> Self { Self { - base, - current_elements, + definition: VMTableDefinition { + base, + current_elements, + }, } } - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.base, self.current_elements) } + /// Construct a `VMTableImmport` variant of `VMTable`. + pub fn import(from: *mut VMTableDefinition) -> Self { + Self { + import: VMTableImport { from }, + } } - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.base, self.current_elements) } + /// Get the underlying `VMTableDefinition`. + pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMTableDefinition { + if is_import { + &mut *self.import.from + } else { + &mut self.definition + } } +} - pub fn as_ptr(&self) -> *const u8 { - self.base - } - - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.base - } - - pub fn len(&self) -> usize { - self.current_elements +impl fmt::Debug for VMTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VMTable {{")?; + write!(f, " definition: {:?},", unsafe { self.definition })?; + write!(f, " import: {:?},", unsafe { self.import })?; + write!(f, "}}")?; + Ok(()) } } @@ -238,6 +491,10 @@ impl Default for VMCallerCheckedAnyfunc { /// The VM "context", which is pointed to by the `vmctx` arg in Cranelift. /// This has pointers to the globals, memories, tables, and other runtime /// state associated with the current instance. +/// +/// TODO: The number of memories, globals, tables, and signature IDs does +/// not change dynamically, and pointer arrays are not indexed dynamically, +/// so these fields could all be contiguously allocated. #[derive(Debug)] #[repr(C)] pub struct VMContext { @@ -300,28 +557,8 @@ impl VMContext { } /// Return the base pointer of the globals array. - pub unsafe fn global_storage(&mut self, index: GlobalIndex) -> *mut VMGlobal { - self.globals.add(index.index() * size_of::()) - } - - /// 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`. diff --git a/lib/execute/src/world.rs b/lib/execute/src/world.rs index c0e20cf04d..47d9854a9f 100644 --- a/lib/execute/src/world.rs +++ b/lib/execute/src/world.rs @@ -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 { + pub fn new( + code: &mut Code, + isa: &isa::TargetIsa, + data: &[u8], + resolver: &mut Resolver, + ) -> Result { 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 { + 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) + } } diff --git a/lib/obj/README.md b/lib/obj/README.md index 3b3e0f1fee..0ea2b97ce5 100644 --- a/lib/obj/README.md +++ b/lib/obj/README.md @@ -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 - diff --git a/lib/wast/Cargo.toml b/lib/wast/Cargo.toml index ab4ba1aaa9..dce4c44b86 100644 --- a/lib/wast/Cargo.toml +++ b/lib/wast/Cargo.toml @@ -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" } diff --git a/lib/wast/build.rs b/lib/wast/build.rs index 3da71c67b5..4d681c1ed7 100644 --- a/lib/wast/build.rs +++ b/lib/wast/build.rs @@ -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, diff --git a/lib/wast/src/lib.rs b/lib/wast/src/lib.rs index c992a55d09..5064a55368 100644 --- a/lib/wast/src/lib.rs +++ b/lib/wast/src/lib.rs @@ -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}; diff --git a/lib/wast/src/spectest.rs b/lib/wast/src/spectest.rs new file mode 100644 index 0000000000..92d905fd74 --- /dev/null +++ b/lib/wast/src/spectest.rs @@ -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 { + 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, + } + } +} diff --git a/lib/wast/src/wast.rs b/lib/wast/src/wast.rs index c6811e0278..58e77b6962 100644 --- a/lib/wast/src/wast.rs +++ b/lib/wast/src/wast.rs @@ -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, namespace: HashMap, 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], + } + } } } } diff --git a/src/wasmtime.rs b/src/wasmtime.rs index 7038f72831..1280a83b85 100644 --- a/src/wasmtime.rs +++ b/src/wasmtime.rs @@ -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 mut world = InstanceWorld::new(&mut code, isa, &data, &mut resolver)?; - 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, - )?; + if let Some(ref f) = args.flag_function { + world.invoke(&mut code, isa, &f, &[])?; + } - finish_instantiation( - &mut code, - isa, - &translation.module, - &compilation, - &mut instance, - )?; - - if let Some(ref f) = args.flag_function { - invoke( - &mut code, - isa, - &translation.module, - &compilation, - instance.vmctx(), - &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()); } }