diff --git a/Cargo.lock b/Cargo.lock index 6e809d78afa6..a87da73c2f55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2381,6 +2381,7 @@ dependencies = [ "cc", "cfg-if", "indexmap", + "lazy_static", "libc", "memoffset", "more-asserts", diff --git a/RELEASES.md b/RELEASES.md index 8146b5d867bb..8a812b99132d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -43,6 +43,10 @@ is now enabled by default. [#1667](https://github.com/bytecodealliance/wasmtime/pull/1667) +The Rust API does not require a store provided during `Module::new` operation. The `Module` can be send accross threads and instantiate for a specific store. The `Instance::new` now requires the store. + + [#1761](https://github.com/bytecodealliance/wasmtime/pull/1761) + -------------------------------------------------------------------------------- ## 0.16.0 diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h index 1b43f97422fd..d5081298e42a 100644 --- a/crates/c-api/include/wasmtime.h +++ b/crates/c-api/include/wasmtime.h @@ -244,6 +244,7 @@ WASM_API_EXTERN own wasmtime_error_t *wasmtime_global_set( // instance is returned), or an instance can be returned (meaning no error or // trap is returned). WASM_API_EXTERN own wasmtime_error_t *wasmtime_instance_new( + wasm_store_t *store, const wasm_module_t *module, const wasm_extern_t* const imports[], size_t num_imports, diff --git a/crates/c-api/src/instance.rs b/crates/c-api/src/instance.rs index d974a5f2dd21..aed706b686aa 100644 --- a/crates/c-api/src/instance.rs +++ b/crates/c-api/src/instance.rs @@ -36,19 +36,10 @@ pub unsafe extern "C" fn wasm_instance_new( imports: *const Box, result: Option<&mut *mut wasm_trap_t>, ) -> Option> { - let store = &store.store; - let module = &wasm_module.module.borrow(); - if !Store::same(&store, module.store()) { - if let Some(result) = result { - let trap = Trap::new("wasm_store_t must match store in wasm_module_t"); - let trap = Box::new(wasm_trap_t::new(store, trap)); - *result = Box::into_raw(trap); - } - return None; - } let mut instance = ptr::null_mut(); let mut trap = ptr::null_mut(); let err = wasmtime_instance_new( + store, wasm_module, imports, wasm_module.imports.len(), @@ -60,7 +51,7 @@ pub unsafe extern "C" fn wasm_instance_new( assert!(trap.is_null()); assert!(instance.is_null()); if let Some(result) = result { - *result = Box::into_raw(err.to_trap(store)); + *result = Box::into_raw(err.to_trap(&store.store)); } None } @@ -83,6 +74,7 @@ pub unsafe extern "C" fn wasm_instance_new( #[no_mangle] pub unsafe extern "C" fn wasmtime_instance_new( + store: &wasm_store_t, module: &wasm_module_t, imports: *const Box, num_imports: usize, @@ -90,6 +82,7 @@ pub unsafe extern "C" fn wasmtime_instance_new( trap_ptr: &mut *mut wasm_trap_t, ) -> Option> { _wasmtime_instance_new( + store, module, std::slice::from_raw_parts(imports, num_imports), instance_ptr, @@ -98,11 +91,13 @@ pub unsafe extern "C" fn wasmtime_instance_new( } fn _wasmtime_instance_new( + store: &wasm_store_t, module: &wasm_module_t, imports: &[Box], instance_ptr: &mut *mut wasm_instance_t, trap_ptr: &mut *mut wasm_trap_t, ) -> Option> { + let store = &store.store; let imports = imports .iter() .map(|import| match &import.which { @@ -114,8 +109,8 @@ fn _wasmtime_instance_new( .collect::>(); let module = &module.module.borrow(); handle_instantiate( - module.store(), - Instance::new(module, &imports), + store, + Instance::new(store, module, &imports), instance_ptr, trap_ptr, ) diff --git a/crates/c-api/src/module.rs b/crates/c-api/src/module.rs index 605c3e7233dd..9e3d3434fb1b 100644 --- a/crates/c-api/src/module.rs +++ b/crates/c-api/src/module.rs @@ -1,9 +1,10 @@ use crate::host_ref::HostRef; -use crate::{handle_result, wasmtime_error_t}; -use crate::{wasm_byte_vec_t, wasm_exporttype_vec_t, wasm_importtype_vec_t}; -use crate::{wasm_exporttype_t, wasm_importtype_t, wasm_store_t}; +use crate::{ + handle_result, wasm_byte_vec_t, wasm_exporttype_t, wasm_exporttype_vec_t, wasm_importtype_t, + wasm_importtype_vec_t, wasm_store_t, wasmtime_error_t, +}; use std::ptr; -use wasmtime::Module; +use wasmtime::{Engine, Module}; #[repr(C)] #[derive(Clone)] @@ -21,6 +22,14 @@ impl wasm_module_t { } } +#[repr(C)] +#[derive(Clone)] +pub struct wasm_shared_module_t { + module: Module, +} + +wasmtime_c_api_macros::declare_own!(wasm_shared_module_t); + #[no_mangle] pub extern "C" fn wasm_module_new( store: &wasm_store_t, @@ -44,7 +53,7 @@ pub extern "C" fn wasmtime_module_new( ) -> Option> { let binary = binary.as_slice(); let store = &store.store; - handle_result(Module::from_binary(store, binary), |module| { + handle_result(Module::from_binary(store.engine(), binary), |module| { let imports = module .imports() .map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty())) @@ -73,8 +82,7 @@ pub extern "C" fn wasmtime_module_validate( binary: &wasm_byte_vec_t, ) -> Option> { let binary = binary.as_slice(); - let store = &store.store; - handle_result(Module::validate(store, binary), |()| {}) + handle_result(Module::validate(store.store.engine(), binary), |()| {}) } #[no_mangle] @@ -96,3 +104,34 @@ pub extern "C" fn wasm_module_imports(module: &wasm_module_t, out: &mut wasm_imp .collect::>(); out.set_buffer(buffer); } + +#[no_mangle] +pub extern "C" fn wasm_module_share(module: &wasm_module_t) -> Box { + Box::new(wasm_shared_module_t { + module: module.module.borrow().clone(), + }) +} + +#[no_mangle] +pub extern "C" fn wasm_module_obtain( + store: &wasm_store_t, + shared_module: &wasm_shared_module_t, +) -> Option> { + let module = shared_module.module.clone(); + if !Engine::same(store.store.engine(), module.engine()) { + return None; + } + let imports = module + .imports() + .map(|i| wasm_importtype_t::new(i.module().to_owned(), i.name().to_owned(), i.ty())) + .collect::>(); + let exports = module + .exports() + .map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty())) + .collect::>(); + Some(Box::new(wasm_module_t { + module: HostRef::new(&store.store, module), + imports, + exports, + })) +} diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index a3d960843755..7242da627d8f 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -56,7 +56,7 @@ pub fn instantiate_with_config(wasm: &[u8], config: Config) { let store = Store::new(&engine); log_wasm(wasm); - let module = match Module::new(&store, wasm) { + let module = match Module::new(&engine, wasm) { Ok(module) => module, Err(_) => return, }; @@ -75,7 +75,7 @@ pub fn instantiate_with_config(wasm: &[u8], config: Config) { // aren't caught during validation or compilation. For example, an imported // table might not have room for an element segment that we want to // initialize into it. - let _result = Instance::new(&module, &imports); + let _result = Instance::new(&store, &module, &imports); } /// Compile the Wasm buffer, and implicitly fail if we have an unexpected @@ -88,9 +88,8 @@ pub fn compile(wasm: &[u8], strategy: Strategy) { crate::init_fuzzing(); let engine = Engine::new(&crate::fuzz_default_config(strategy).unwrap()); - let store = Store::new(&engine); log_wasm(wasm); - let _ = Module::new(&store, wasm); + let _ = Module::new(&engine, wasm); } /// Instantiate the given Wasm module with each `Config` and call all of its @@ -128,7 +127,7 @@ pub fn differential_execution( let engine = Engine::new(config); let store = Store::new(&engine); - let module = match Module::new(&store, &ttf.wasm) { + let module = match Module::new(&engine, &ttf.wasm) { Ok(module) => module, // The module might rely on some feature that our config didn't // enable or something like that. @@ -158,7 +157,7 @@ pub fn differential_execution( // aren't caught during validation or compilation. For example, an imported // table might not have room for an element segment that we want to // initialize into it. - let instance = match Instance::new(&module, &imports) { + let instance = match Instance::new(&store, &module, &imports) { Ok(instance) => instance, Err(e) => { eprintln!( @@ -304,7 +303,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { ApiCall::ModuleNew { id, wasm } => { log::debug!("creating module: {}", id); log_wasm(&wasm.wasm); - let module = match Module::new(store.as_ref().unwrap(), &wasm.wasm) { + let module = match Module::new(engine.as_ref().unwrap(), &wasm.wasm) { Ok(m) => m, Err(_) => continue, }; @@ -324,7 +323,9 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { None => continue, }; - let imports = match dummy_imports(store.as_ref().unwrap(), module.imports()) { + let store = store.as_ref().unwrap(); + + let imports = match dummy_imports(store, module.imports()) { Ok(imps) => imps, Err(_) => { // There are some value types that we can't synthesize a @@ -338,7 +339,7 @@ pub fn make_api_calls(api: crate::generators::api::ApiCalls) { // aren't caught during validation or compilation. For example, an imported // table might not have room for an element segment that we want to // initialize into it. - if let Ok(instance) = Instance::new(&module, &imports) { + if let Ok(instance) = Instance::new(store, &module, &imports) { instances.insert(id, instance); } } diff --git a/crates/jit/src/code_memory.rs b/crates/jit/src/code_memory.rs index e0b7315c4d01..9ad259f9959a 100644 --- a/crates/jit/src/code_memory.rs +++ b/crates/jit/src/code_memory.rs @@ -22,10 +22,10 @@ impl CodeMemoryEntry { Ok(Self { mmap, registry }) } - fn contains(&self, addr: usize) -> bool { + fn range(&self) -> (usize, usize) { let start = self.mmap.as_ptr() as usize; let end = start + self.mmap.len(); - start <= addr && addr < end + (start, end) } } @@ -243,11 +243,10 @@ impl CodeMemory { Ok(()) } - /// Returns whether any published segment of this code memory contains - /// `addr`. - pub fn published_contains(&self, addr: usize) -> bool { + /// Returns all published segment ranges. + pub fn published_ranges<'a>(&'a self) -> impl Iterator + 'a { self.entries[..self.published] .iter() - .any(|entry| entry.contains(addr)) + .map(|entry| entry.range()) } } diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index 7ba94986a59a..7b7408ad6687 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -9,20 +9,16 @@ use cranelift_codegen::Context; use cranelift_codegen::{binemit, ir}; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use std::collections::HashMap; -use std::sync::Arc; use wasmtime_debug::{emit_debugsections_image, DebugInfoData}; use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; -use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex}; +use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex, SignatureIndex}; use wasmtime_environ::{ CacheConfig, CompileError, CompiledFunction, Compiler as _C, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation, ModuleVmctxInfo, Relocation, RelocationTarget, Relocations, Traps, Tunables, VMOffsets, }; -use wasmtime_runtime::{ - InstantiationError, SignatureRegistry, VMFunctionBody, VMInterrupts, VMSharedSignatureIndex, - VMTrampoline, -}; +use wasmtime_runtime::{InstantiationError, VMFunctionBody, VMTrampoline}; /// Select which kind of compilation to use. #[derive(Copy, Clone, Debug)] @@ -48,12 +44,9 @@ pub enum CompilationStrategy { /// TODO: Consider using cranelift-module. pub struct Compiler { isa: Box, - code_memory: CodeMemory, - signatures: SignatureRegistry, strategy: CompilationStrategy, cache_config: CacheConfig, tunables: Tunables, - interrupts: Arc, } impl Compiler { @@ -66,22 +59,25 @@ impl Compiler { ) -> Self { Self { isa, - code_memory: CodeMemory::new(), - signatures: SignatureRegistry::new(), strategy, cache_config, tunables, - interrupts: Arc::new(VMInterrupts::default()), } } } +fn _assert_compiler_send_sync() { + fn _assert() {} + _assert::(); +} + #[allow(missing_docs)] pub struct Compilation { + pub code_memory: CodeMemory, pub finished_functions: PrimaryMap, pub relocations: Relocations, - pub trampolines: HashMap, - pub trampoline_relocations: HashMap>, + pub trampolines: PrimaryMap, + pub trampoline_relocations: HashMap>, pub jt_offsets: PrimaryMap, pub dbg_image: Option>, pub traps: Traps, @@ -89,6 +85,11 @@ pub struct Compilation { } impl Compiler { + /// Return the isa. + pub fn isa(&self) -> &dyn TargetIsa { + self.isa.as_ref() + } + /// Return the target's frontend configuration settings. pub fn frontend_config(&self) -> TargetFrontendConfig { self.isa.frontend_config() @@ -99,17 +100,14 @@ impl Compiler { &self.tunables } - /// Return the handle by which to interrupt instances - pub fn interrupts(&self) -> &Arc { - &self.interrupts - } - /// Compile the given function bodies. pub(crate) fn compile<'data>( - &mut self, + &self, translation: &ModuleTranslation, debug_data: Option, ) -> Result { + let mut code_memory = CodeMemory::new(); + let (compilation, relocations, address_transform, value_ranges, stack_slots, traps) = match self.strategy { // For now, interpret `Auto` as `Cranelift` since that's the most stable @@ -135,7 +133,7 @@ impl Compiler { // Allocate all of the compiled functions into executable memory, // copying over their contents. let finished_functions = - allocate_functions(&mut self.code_memory, &compilation).map_err(|message| { + allocate_functions(&mut code_memory, &compilation).map_err(|message| { SetupError::Instantiate(InstantiationError::Resource(format!( "failed to allocate memory for functions: {}", message @@ -147,23 +145,17 @@ impl Compiler { // guarantees that all functions (including indirect ones through // tables) have a trampoline when invoked through the wasmtime API. let mut cx = FunctionBuilderContext::new(); - let mut trampolines = HashMap::new(); + let mut trampolines = PrimaryMap::new(); let mut trampoline_relocations = HashMap::new(); - for (wasm_func_ty, native_sig) in translation.module.local.signatures.values() { - let index = self - .signatures - .register(wasm_func_ty.clone(), native_sig.clone()); - if trampolines.contains_key(&index) { - continue; - } + for (index, (_, native_sig)) in translation.module.local.signatures.iter() { let (trampoline, relocations) = make_trampoline( &*self.isa, - &mut self.code_memory, + &mut code_memory, &mut cx, native_sig, std::mem::size_of::(), )?; - trampolines.insert(index, trampoline); + trampolines.push(trampoline); // Typically trampolines do not have relocations, so if one does // show up be sure to log it in case anyone's listening and there's @@ -217,6 +209,7 @@ impl Compiler { let jt_offsets = compilation.get_jt_offsets(); Ok(Compilation { + code_memory, finished_functions, relocations, trampolines, @@ -227,22 +220,6 @@ impl Compiler { address_transform, }) } - - /// Make memory containing compiled code executable. - pub(crate) fn publish_compiled_code(&mut self) { - self.code_memory.publish(self.isa.as_ref()); - } - - /// Shared signature registry. - pub fn signatures(&self) -> &SignatureRegistry { - &self.signatures - } - - /// Returns whether or not the given address falls within the JIT code - /// managed by the compiler - pub fn is_in_jit_code(&self, addr: usize) -> bool { - self.code_memory.published_contains(addr) - } } /// Create a trampoline for invoking a function. diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index d92a55fe3c83..c8cdc937a9f1 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -3,6 +3,7 @@ //! `CompiledModule` to allow compiling and instantiating to be done as separate //! steps. +use crate::code_memory::CodeMemory; use crate::compiler::Compiler; use crate::imports::resolve_imports; use crate::link::link_module; @@ -10,7 +11,6 @@ use crate::resolver::Resolver; use std::any::Any; use std::collections::HashMap; use std::io::Write; -use std::rc::Rc; use std::sync::Arc; use thiserror::Error; use wasmtime_debug::read_debuginfo; @@ -24,7 +24,7 @@ use wasmtime_profiling::ProfilingAgent; use wasmtime_runtime::VMInterrupts; use wasmtime_runtime::{ GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator, - SignatureRegistry, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline, + SignatureRegistry, VMFunctionBody, VMTrampoline, }; /// An error condition while setting up a wasm instance, be it validation, @@ -49,23 +49,33 @@ pub enum SetupError { DebugInfo(#[from] anyhow::Error), } -/// This is similar to `CompiledModule`, but references the data initializers -/// from the wasm buffer rather than holding its own copy. -struct RawCompiledModule<'data> { - module: Module, - finished_functions: BoxedSlice, - trampolines: HashMap, - data_initializers: Box<[DataInitializer<'data>]>, - signatures: BoxedSlice, +struct FinishedFunctions(BoxedSlice); + +unsafe impl Send for FinishedFunctions {} +unsafe impl Sync for FinishedFunctions {} + +/// Container for data needed for an Instance function to exist. +pub struct ModuleCode { + code_memory: CodeMemory, + #[allow(dead_code)] dbg_jit_registration: Option, +} + +/// A compiled wasm module, ready to be instantiated. +pub struct CompiledModule { + module: Arc, + code: Arc, + finished_functions: FinishedFunctions, + trampolines: PrimaryMap, + data_initializers: Box<[OwnedDataInitializer]>, traps: Traps, address_transform: ModuleAddressMap, } -impl<'data> RawCompiledModule<'data> { - /// Create a new `RawCompiledModule` by compiling the wasm module in `data` and instatiating it. - fn new( - compiler: &mut Compiler, +impl CompiledModule { + /// Compile a data buffer into a `CompiledModule`, which may then be instantiated. + pub fn new<'data>( + compiler: &Compiler, data: &'data [u8], profiler: &dyn ProfilingAgent, ) -> Result { @@ -83,26 +93,24 @@ impl<'data> RawCompiledModule<'data> { let compilation = compiler.compile(&translation, debug_data)?; - link_module(&translation.module, &compilation); + let module = translation.module; - // Compute indices into the shared signature table. - let signatures = { - let signature_registry = compiler.signatures(); - translation - .module - .local - .signatures - .values() - .map(|(wasm, native)| signature_registry.register(wasm.clone(), native.clone())) - .collect::>() - }; + link_module(&module, &compilation); // Make all code compiled thus far executable. - compiler.publish_compiled_code(); + let mut code_memory = compilation.code_memory; + code_memory.publish(compiler.isa()); + + let data_initializers = translation + .data_initializers + .into_iter() + .map(OwnedDataInitializer::new) + .collect::>() + .into_boxed_slice(); // Initialize profiler and load the wasm module profiler.module_load( - &translation.module, + &module, &compilation.finished_functions, compilation.dbg_image.as_deref(), ); @@ -116,82 +124,22 @@ impl<'data> RawCompiledModule<'data> { None }; + let finished_functions = + FinishedFunctions(compilation.finished_functions.into_boxed_slice()); + Ok(Self { - module: translation.module, - finished_functions: compilation.finished_functions.into_boxed_slice(), + module: Arc::new(module), + code: Arc::new(ModuleCode { + code_memory, + dbg_jit_registration, + }), + finished_functions, trampolines: compilation.trampolines, - data_initializers: translation.data_initializers.into_boxed_slice(), - signatures: signatures.into_boxed_slice(), - dbg_jit_registration, + data_initializers, traps: compilation.traps, address_transform: compilation.address_transform, }) } -} - -/// A compiled wasm module, ready to be instantiated. -pub struct CompiledModule { - module: Arc, - finished_functions: BoxedSlice, - trampolines: HashMap, - data_initializers: Box<[OwnedDataInitializer]>, - signatures: BoxedSlice, - dbg_jit_registration: Option>, - traps: Traps, - address_transform: ModuleAddressMap, - interrupts: Arc, -} - -impl CompiledModule { - /// Compile a data buffer into a `CompiledModule`, which may then be instantiated. - pub fn new<'data>( - compiler: &mut Compiler, - data: &'data [u8], - profiler: &dyn ProfilingAgent, - ) -> Result { - let raw = RawCompiledModule::<'data>::new(compiler, data, profiler)?; - - Ok(Self::from_parts( - raw.module, - raw.finished_functions, - raw.trampolines, - raw.data_initializers - .iter() - .map(OwnedDataInitializer::new) - .collect::>() - .into_boxed_slice(), - raw.signatures.clone(), - raw.dbg_jit_registration, - raw.traps, - raw.address_transform, - compiler.interrupts().clone(), - )) - } - - /// Construct a `CompiledModule` from component parts. - pub fn from_parts( - module: Module, - finished_functions: BoxedSlice, - trampolines: HashMap, - data_initializers: Box<[OwnedDataInitializer]>, - signatures: BoxedSlice, - dbg_jit_registration: Option, - traps: Traps, - address_transform: ModuleAddressMap, - interrupts: Arc, - ) -> Self { - Self { - module: Arc::new(module), - finished_functions, - trampolines, - data_initializers, - signatures, - dbg_jit_registration: dbg_jit_registration.map(Rc::new), - traps, - address_transform, - interrupts, - } - } /// Crate an `Instance` from this `CompiledModule`. /// @@ -205,21 +153,41 @@ impl CompiledModule { pub unsafe fn instantiate( &self, resolver: &mut dyn Resolver, - sig_registry: &SignatureRegistry, + signature_registry: &mut SignatureRegistry, mem_creator: Option<&dyn RuntimeMemoryCreator>, + interrupts: Arc, host_state: Box, ) -> Result { - let imports = resolve_imports(&self.module, &sig_registry, resolver)?; + // Compute indices into the shared signature table. + let signatures = { + self.module + .local + .signatures + .values() + .map(|(wasm_sig, native)| { + signature_registry.register(wasm_sig.clone(), native.clone()) + }) + .collect::>() + }; + + let mut trampolines = HashMap::new(); + for (i, trampoline) in self.trampolines.iter() { + trampolines.insert(signatures[i], trampoline.clone()); + } + + let finished_functions = self.finished_functions.0.clone(); + + let imports = resolve_imports(&self.module, signature_registry, resolver)?; InstanceHandle::new( - Arc::clone(&self.module), - self.finished_functions.clone(), - self.trampolines.clone(), + self.module.clone(), + self.code.clone(), + finished_functions, + trampolines, imports, mem_creator, - self.signatures.clone(), - self.dbg_jit_registration.as_ref().map(|r| Rc::clone(&r)), + signatures.into_boxed_slice(), host_state, - self.interrupts.clone(), + interrupts, ) } @@ -239,19 +207,14 @@ impl CompiledModule { &self.module } - /// Return a reference-counting pointer to a module. - pub fn module_mut(&mut self) -> &mut Arc { - &mut self.module - } - - /// Return a reference to a module. - pub fn module_ref(&self) -> &Module { - &self.module + /// Return a reference to a mutable module (if possible). + pub fn module_mut(&mut self) -> Option<&mut Module> { + Arc::get_mut(&mut self.module) } /// Returns the map of all finished JIT functions compiled for this module pub fn finished_functions(&self) -> &BoxedSlice { - &self.finished_functions + &self.finished_functions.0 } /// Returns the a map for all traps in this module. @@ -263,6 +226,16 @@ impl CompiledModule { pub fn address_transform(&self) -> &ModuleAddressMap { &self.address_transform } + + /// Returns all ranges convered by JIT code. + pub fn jit_code_ranges<'a>(&'a self) -> impl Iterator + 'a { + self.code.code_memory.published_ranges() + } + + /// Returns module's JIT code. + pub fn code(&self) -> &Arc { + &self.code + } } /// Similar to `DataInitializer`, but owns its own copy of the data rather @@ -276,7 +249,7 @@ pub struct OwnedDataInitializer { } impl OwnedDataInitializer { - fn new(borrowed: &DataInitializer<'_>) -> Self { + fn new(borrowed: DataInitializer<'_>) -> Self { Self { location: borrowed.location.clone(), data: borrowed.data.to_vec().into_boxed_slice(), diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index 26a9a8833ccc..9201c4e0b015 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -22,7 +22,7 @@ pub fn link_module(module: &Module, compilation: &Compilation) { for (i, function_relocs) in compilation.trampoline_relocations.iter() { for r in function_relocs.iter() { println!("tramopline relocation"); - let body = compilation.trampolines[&i] as *const VMFunctionBody; + let body = compilation.trampolines[*i] as *const VMFunctionBody; apply_reloc(module, compilation, body, r); } } diff --git a/crates/misc/rust/macro/src/lib.rs b/crates/misc/rust/macro/src/lib.rs index 9c980d91bd64..b6cd8fc54d49 100644 --- a/crates/misc/rust/macro/src/lib.rs +++ b/crates/misc/rust/macro/src/lib.rs @@ -57,7 +57,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result { let data = #root::wasmtime_interface_types::ModuleData::new(bytes.as_ref())?; - let module = Module::new(&store, bytes.as_ref())?; + let module = Module::new(&engine, bytes.as_ref())?; let mut imports: Vec = Vec::new(); if let Some(module_name) = data.find_wasi_module_name() { @@ -75,7 +75,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result { } } let instance = - Instance::new(&module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?; + Instance::new(&store, &module, &imports).map_err(|t| format_err!("instantiation trap: {:?}", t))?; Ok(#name { instance, data }) } diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml index a3ea52cc32a3..4d979c7b9d07 100644 --- a/crates/runtime/Cargo.toml +++ b/crates/runtime/Cargo.toml @@ -21,6 +21,7 @@ thiserror = "1.0.4" more-asserts = "0.2.1" cfg-if = "0.1.9" backtrace = "0.3.42" +lazy_static = "1.3.0" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi"] } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 6c2f984c4a83..8642da2aef45 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -4,7 +4,6 @@ use crate::export::Export; use crate::imports::Imports; -use crate::jit_int::GdbJitImageRegistration; use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory, RuntimeMemoryCreator}; use crate::table::Table; use crate::traphandlers::Trap; @@ -21,7 +20,6 @@ use std::any::Any; use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; -use std::rc::Rc; use std::sync::Arc; use std::{mem, ptr, slice}; use thiserror::Error; @@ -40,6 +38,9 @@ pub(crate) struct Instance { /// The `Module` this `Instance` was instantiated from. module: Arc, + /// The module's JIT code (if exists). + code: Arc, + /// Offsets in the `vmctx` region. offsets: VMOffsets, @@ -67,9 +68,6 @@ pub(crate) struct Instance { /// Hosts can store arbitrary per-instance information here. host_state: Box, - /// Optional image of JIT'ed code for debugger registration. - dbg_jit_registration: Option>, - /// Externally allocated data indicating how this instance will be /// interrupted. pub(crate) interrupts: Arc, @@ -96,14 +94,10 @@ impl Instance { unsafe { *self.signature_ids_ptr().add(index) } } - pub(crate) fn module(&self) -> &Arc { + pub(crate) fn module(&self) -> &Module { &self.module } - pub(crate) fn module_ref(&self) -> &Module { - &*self.module - } - /// Return a pointer to the `VMSharedSignatureIndex`s. fn signature_ids_ptr(&self) -> *mut VMSharedSignatureIndex { unsafe { self.vmctx_plus_offset(self.offsets.vmctx_signature_ids_begin()) } @@ -782,12 +776,12 @@ impl InstanceHandle { /// safety. pub unsafe fn new( module: Arc, + code: Arc, finished_functions: BoxedSlice, trampolines: HashMap, imports: Imports, mem_creator: Option<&dyn RuntimeMemoryCreator>, vmshared_signatures: BoxedSlice, - dbg_jit_registration: Option>, host_state: Box, interrupts: Arc, ) -> Result { @@ -815,6 +809,7 @@ impl InstanceHandle { let handle = { let instance = Instance { module, + code, offsets, memories, tables, @@ -822,7 +817,6 @@ impl InstanceHandle { passive_data, finished_functions, trampolines, - dbg_jit_registration, host_state, interrupts, vmctx: VMContext {}, @@ -941,14 +935,9 @@ impl InstanceHandle { self.instance().vmctx_ptr() } - /// Return a reference-counting pointer to a module. - pub fn module(&self) -> &Arc { - self.instance().module() - } - /// Return a reference to a module. - pub fn module_ref(&self) -> &Module { - self.instance().module_ref() + pub fn module(&self) -> &Module { + self.instance().module() } /// Lookup an export with the given name. @@ -1065,8 +1054,7 @@ impl InstanceHandle { } fn check_table_init_bounds(instance: &Instance) -> Result<(), InstantiationError> { - let module = Arc::clone(&instance.module); - for init in &module.table_elements { + for init in &instance.module().table_elements { let start = get_table_init_start(init, instance); let table = instance.get_table(init.table_index); @@ -1170,8 +1158,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize { /// Initialize the table memory from the provided initializers. fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> { - let module = Arc::clone(&instance.module); - for init in &module.table_elements { + for init in &instance.module().table_elements { let start = get_table_init_start(init, instance); let table = instance.get_table(init.table_index); @@ -1284,7 +1271,7 @@ fn create_globals(module: &Module) -> BoxedSlice = Mutex::new(Default::default()); +} + /// Registeration for JIT image pub struct GdbJitImageRegistration { - entry: *mut JITCodeEntry, - file: Vec, + entry: Pin>, + file: Pin>, } impl GdbJitImageRegistration { /// Registers JIT image using __jit_debug_register_code pub fn register(file: Vec) -> Self { - Self { - entry: unsafe { register_gdb_jit_image(&file) }, - file, + let file = Pin::new(file.into_boxed_slice()); + + // Create a code entry for the file, which gives the start and size + // of the symbol file. + let mut entry = Pin::new(Box::new(JITCodeEntry { + next_entry: ptr::null_mut(), + prev_entry: ptr::null_mut(), + symfile_addr: file.as_ptr(), + symfile_size: file.len() as u64, + })); + + unsafe { + register_gdb_jit_image(&mut *entry); } + + Self { entry, file } } /// JIT image used in registration @@ -67,20 +92,19 @@ impl GdbJitImageRegistration { impl Drop for GdbJitImageRegistration { fn drop(&mut self) { unsafe { - unregister_gdb_jit_image(self.entry); + unregister_gdb_jit_image(&mut *self.entry); } } } -unsafe fn register_gdb_jit_image(file: &[u8]) -> *mut JITCodeEntry { - // Create a code entry for the file, which gives the start and size of the symbol file. - let entry = Box::into_raw(Box::new(JITCodeEntry { - next_entry: __jit_debug_descriptor.first_entry, - prev_entry: ptr::null_mut(), - symfile_addr: file.as_ptr(), - symfile_size: file.len() as u64, - })); +unsafe impl Send for GdbJitImageRegistration {} +unsafe impl Sync for GdbJitImageRegistration {} + +unsafe fn register_gdb_jit_image(entry: *mut JITCodeEntry) { + let _lock = GDB_REGISTRATION.lock().unwrap(); + // Add it to the linked list in the JIT descriptor. + (*entry).next_entry = __jit_debug_descriptor.first_entry; if !__jit_debug_descriptor.first_entry.is_null() { (*__jit_debug_descriptor.first_entry).prev_entry = entry; } @@ -93,10 +117,11 @@ unsafe fn register_gdb_jit_image(file: &[u8]) -> *mut JITCodeEntry { __jit_debug_descriptor.action_flag = JIT_NOACTION; __jit_debug_descriptor.relevant_entry = ptr::null_mut(); - entry } unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) { + let _lock = GDB_REGISTRATION.lock().unwrap(); + // Remove the code entry corresponding to the code from the linked list. if !(*entry).prev_entry.is_null() { (*(*entry).prev_entry).next_entry = (*entry).next_entry; @@ -114,5 +139,4 @@ unsafe fn unregister_gdb_jit_image(entry: *mut JITCodeEntry) { __jit_debug_descriptor.action_flag = JIT_NOACTION; __jit_debug_descriptor.relevant_entry = ptr::null_mut(); - let _box = Box::from_raw(entry); } diff --git a/crates/runtime/src/sig_registry.rs b/crates/runtime/src/sig_registry.rs index 7ea07ddf4895..a0427ca37d5c 100644 --- a/crates/runtime/src/sig_registry.rs +++ b/crates/runtime/src/sig_registry.rs @@ -3,27 +3,16 @@ use crate::vmcontext::VMSharedSignatureIndex; use more_asserts::assert_lt; -use std::collections::HashMap; +use std::collections::{hash_map, HashMap}; use std::convert::TryFrom; -use std::sync::RwLock; use wasmtime_environ::{ir, wasm::WasmFuncType}; /// WebAssembly requires that the caller and callee signatures in an indirect /// call must match. To implement this efficiently, keep a registry of all /// signatures, shared by all instances, so that call sites can just do an /// index comparison. -#[derive(Debug)] -pub struct SignatureRegistry { - // This structure is stored in a `Compiler` and is intended to be shared - // across many instances. Ideally instances can themselves be sent across - // threads, and ideally we can compile across many threads. As a result we - // use interior mutability here with a lock to avoid having callers to - // externally synchronize calls to compilation. - inner: RwLock, -} - #[derive(Debug, Default)] -struct Inner { +pub struct SignatureRegistry { wasm2index: HashMap, // Maps the index to the original Wasm signature. @@ -34,35 +23,31 @@ struct Inner { } impl SignatureRegistry { - /// Create a new `SignatureRegistry`. - pub fn new() -> Self { - Self { - inner: Default::default(), - } - } - /// Register a signature and return its unique index. - pub fn register(&self, wasm: WasmFuncType, native: ir::Signature) -> VMSharedSignatureIndex { - let Inner { - wasm2index, - index2wasm, - index2native, - } = &mut *self.inner.write().unwrap(); - let len = wasm2index.len(); + pub fn register( + &mut self, + wasm: WasmFuncType, + native: ir::Signature, + ) -> VMSharedSignatureIndex { + let len = self.wasm2index.len(); - *wasm2index.entry(wasm.clone()).or_insert_with(|| { - // Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX) - // is reserved for VMSharedSignatureIndex::default(). - assert_lt!( - len, - std::u32::MAX as usize, - "Invariant check: signature_hash.len() < std::u32::MAX" - ); - let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); - index2wasm.insert(index, wasm); - index2native.insert(index, native); - index - }) + match self.wasm2index.entry(wasm.clone()) { + hash_map::Entry::Occupied(entry) => *entry.get(), + hash_map::Entry::Vacant(entry) => { + // Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX) + // is reserved for VMSharedSignatureIndex::default(). + assert_lt!( + len, + std::u32::MAX as usize, + "Invariant check: signature_hash.len() < std::u32::MAX" + ); + let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); + entry.insert(index); + self.index2wasm.insert(index, wasm); + self.index2native.insert(index, native); + index + } + } } /// Looks up a shared native signature within this registry. @@ -70,7 +55,7 @@ impl SignatureRegistry { /// Note that for this operation to be semantically correct the `idx` must /// have previously come from a call to `register` of this same object. pub fn lookup_native(&self, idx: VMSharedSignatureIndex) -> Option { - self.inner.read().unwrap().index2native.get(&idx).cloned() + self.index2native.get(&idx).cloned() } /// Looks up a shared Wasm signature within this registry. @@ -78,6 +63,6 @@ impl SignatureRegistry { /// Note that for this operation to be semantically correct the `idx` must /// have previously come from a call to `register` of this same object. pub fn lookup_wasm(&self, idx: VMSharedSignatureIndex) -> Option { - self.inner.read().unwrap().index2wasm.get(&idx).cloned() + self.index2wasm.get(&idx).cloned() } } diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index de465d1309cd..0b4ba305e098 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -53,7 +53,7 @@ pub fn instantiate( snapshot1.add_to_linker(&mut linker)?; - let module = Module::new(&store, &data).context("failed to create wasm module")?; + let module = Module::new(store.engine(), &data).context("failed to create wasm module")?; linker .module("", &module) diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs index d97ccb22f94e..0aed6429fabe 100644 --- a/crates/wasmtime/src/externals.rs +++ b/crates/wasmtime/src/externals.rs @@ -659,13 +659,14 @@ impl Memory { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// let store = Store::default(); + /// let engine = Engine::default(); + /// let store = Store::new(&engine); /// /// let memory_ty = MemoryType::new(Limits::new(1, None)); /// let memory = Memory::new(&store, memory_ty); /// - /// let module = Module::new(&store, "(module (memory (import \"\" \"\") 1))")?; - /// let instance = Instance::new(&module, &[memory.into()])?; + /// let module = Module::new(&engine, "(module (memory (import \"\" \"\") 1))")?; + /// let instance = Instance::new(&store, &module, &[memory.into()])?; /// // ... /// # Ok(()) /// # } @@ -686,9 +687,10 @@ impl Memory { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// let store = Store::default(); - /// let module = Module::new(&store, "(module (memory (export \"mem\") 1))")?; - /// let instance = Instance::new(&module, &[])?; + /// let engine = Engine::default(); + /// let store = Store::new(&engine); + /// let module = Module::new(&engine, "(module (memory (export \"mem\") 1))")?; + /// let instance = Instance::new(&store, &module, &[])?; /// let memory = instance.get_memory("mem").unwrap(); /// let ty = memory.ty(); /// assert_eq!(ty.limits().min(), 1); @@ -798,9 +800,10 @@ impl Memory { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// let store = Store::default(); - /// let module = Module::new(&store, "(module (memory (export \"mem\") 1 2))")?; - /// let instance = Instance::new(&module, &[])?; + /// let engine = Engine::default(); + /// let store = Store::new(&engine); + /// let module = Module::new(&engine, "(module (memory (export \"mem\") 1 2))")?; + /// let instance = Instance::new(&store, &module, &[])?; /// let memory = instance.get_memory("mem").unwrap(); /// /// assert_eq!(memory.size(), 1); diff --git a/crates/wasmtime/src/frame_info.rs b/crates/wasmtime/src/frame_info.rs index 0a4b2b6cf6da..faf6e00c1c5d 100644 --- a/crates/wasmtime/src/frame_info.rs +++ b/crates/wasmtime/src/frame_info.rs @@ -42,6 +42,8 @@ struct ModuleFrameInfo { start: usize, functions: BTreeMap, module: Arc, + #[allow(dead_code)] + module_code: Arc, } struct FunctionInfo { @@ -192,6 +194,7 @@ pub fn register(module: &CompiledModule) -> Option start: min, functions, module: module.module().clone(), + module_code: module.code().clone(), }, ); assert!(prev.is_none()); diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 95fd6460dc47..f89d16b431be 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -38,9 +38,10 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody}; /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { -/// let store = Store::default(); -/// let module = Module::new(&store, r#"(module (func (export "foo")))"#)?; -/// let instance = Instance::new(&module, &[])?; +/// let engine = Engine::default(); +/// let store = Store::new(&engine); +/// let module = Module::new(&engine, r#"(module (func (export "foo")))"#)?; +/// let instance = Instance::new(&store, &module, &[])?; /// let foo = instance.get_func("foo").expect("export wasn't a function"); /// /// // Work with `foo` as a `Func` at this point, such as calling it @@ -76,7 +77,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody}; /// /// // Next we can hook that up to a wasm module which uses it. /// let module = Module::new( -/// &store, +/// store.engine(), /// r#" /// (module /// (import "" "" (func $add (param i32 i32) (result i32))) @@ -90,7 +91,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody}; /// i32.add)) /// "#, /// )?; -/// let instance = Instance::new(&module, &[add.into()])?; +/// let instance = Instance::new(&store, &module, &[add.into()])?; /// let call_add_twice = instance.get_func("call_add_twice").expect("export wasn't a function"); /// let call_add_twice = call_add_twice.get0::()?; /// @@ -120,7 +121,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody}; /// }); /// /// let module = Module::new( -/// &store, +/// store.engine(), /// r#" /// (module /// (import "" "" (func $double (param i32) (result i32))) @@ -131,7 +132,7 @@ use wasmtime_runtime::{Export, InstanceHandle, VMContext, VMFunctionBody}; /// (start $start)) /// "#, /// )?; -/// let instance = Instance::new(&module, &[double.into()])?; +/// let instance = Instance::new(&store, &module, &[double.into()])?; /// // .. work with `instance` if necessary /// # Ok(()) /// # } @@ -335,7 +336,7 @@ impl Func { /// # let store = Store::default(); /// let add = Func::wrap(&store, |a: i32, b: i32| a + b); /// let module = Module::new( - /// &store, + /// store.engine(), /// r#" /// (module /// (import "" "" (func $add (param i32 i32) (result i32))) @@ -345,7 +346,7 @@ impl Func { /// call $add)) /// "#, /// )?; - /// let instance = Instance::new(&module, &[add.into()])?; + /// let instance = Instance::new(&store, &module, &[add.into()])?; /// let foo = instance.get_func("foo").unwrap().get2::()?; /// assert_eq!(foo(1, 2)?, 3); /// # Ok(()) @@ -366,7 +367,7 @@ impl Func { /// } /// }); /// let module = Module::new( - /// &store, + /// store.engine(), /// r#" /// (module /// (import "" "" (func $add (param i32 i32) (result i32))) @@ -376,7 +377,7 @@ impl Func { /// call $add)) /// "#, /// )?; - /// let instance = Instance::new(&module, &[add.into()])?; + /// let instance = Instance::new(&store, &module, &[add.into()])?; /// let foo = instance.get_func("foo").unwrap().get2::()?; /// assert_eq!(foo(1, 2)?, 3); /// assert!(foo(i32::max_value(), 1).is_err()); @@ -397,7 +398,7 @@ impl Func { /// println!("d={}", d); /// }); /// let module = Module::new( - /// &store, + /// store.engine(), /// r#" /// (module /// (import "" "" (func $debug (param i32 f32 i64 f64))) @@ -409,7 +410,7 @@ impl Func { /// call $debug)) /// "#, /// )?; - /// let instance = Instance::new(&module, &[debug.into()])?; + /// let instance = Instance::new(&store, &module, &[debug.into()])?; /// let foo = instance.get_func("foo").unwrap().get0::<()>()?; /// foo()?; /// # Ok(()) @@ -453,7 +454,7 @@ impl Func { /// Ok(()) /// }); /// let module = Module::new( - /// &store, + /// store.engine(), /// r#" /// (module /// (import "" "" (func $log_str (param i32 i32))) @@ -465,7 +466,7 @@ impl Func { /// (data (i32.const 4) "Hello, world!")) /// "#, /// )?; - /// let instance = Instance::new(&module, &[log_str.into()])?; + /// let instance = Instance::new(&store, &module, &[log_str.into()])?; /// let foo = instance.get_func("foo").unwrap().get0::<()>()?; /// foo()?; /// # Ok(()) @@ -479,13 +480,7 @@ impl Func { pub fn ty(&self) -> FuncType { // Signatures should always be registered in the store's registry of // shared signatures, so we should be able to unwrap safely here. - let sig = self - .instance - .store - .compiler() - .signatures() - .lookup_wasm(self.export.signature) - .expect("failed to lookup signature"); + let sig = self.instance.store.lookup_signature(self.export.signature); // This is only called with `Export::Function`, and since it's coming // from wasmtime_runtime itself we should support all the types coming @@ -495,25 +490,13 @@ impl Func { /// Returns the number of parameters that this function takes. pub fn param_arity(&self) -> usize { - self.instance - .store - .compiler() - .signatures() - .lookup_wasm(self.export.signature) - .expect("failed to lookup signature") - .params - .len() + let sig = self.instance.store.lookup_signature(self.export.signature); + sig.params.len() } /// Returns the number of results this function produces. pub fn result_arity(&self) -> usize { - let sig = self - .instance - .store - .compiler() - .signatures() - .lookup_wasm(self.export.signature) - .expect("failed to lookup signature"); + let sig = self.instance.store.lookup_signature(self.export.signature); sig.returns.len() } @@ -749,7 +732,7 @@ pub(crate) fn catch_traps( wasmtime_runtime::catch_traps( vmctx, store.engine().config().max_wasm_stack, - |addr| store.compiler().is_in_jit_code(addr), + |addr| store.is_in_jit_code(addr), signalhandler.as_deref(), closure, ) diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index eff5139b7597..ed39a70d4d72 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -1,11 +1,11 @@ use crate::trampoline::StoreInstanceHandle; -use crate::{Export, Extern, Func, Global, Memory, Module, Store, Table, Trap}; +use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap}; use anyhow::{bail, Error, Result}; use std::any::Any; use std::mem; use wasmtime_environ::EntityIndex; use wasmtime_jit::{CompiledModule, Resolver}; -use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMContext, VMFunctionBody}; +use wasmtime_runtime::{InstantiationError, VMContext, VMFunctionBody}; struct SimpleResolver<'a> { imports: &'a [Extern], @@ -23,7 +23,6 @@ fn instantiate( store: &Store, compiled_module: &CompiledModule, imports: &[Extern], - sig_registry: &SignatureRegistry, host: Box, ) -> Result { // For now we have a restriction that the `Store` that we're working @@ -47,8 +46,9 @@ fn instantiate( let instance = unsafe { let instance = compiled_module.instantiate( &mut resolver, - sig_registry, + &mut store.signatures_mut(), config.memory_creator.as_ref().map(|a| a as _), + store.interrupts().clone(), host, )?; @@ -120,6 +120,7 @@ fn instantiate( #[derive(Clone)] pub struct Instance { pub(crate) handle: StoreInstanceHandle, + store: Store, module: Module, } @@ -177,19 +178,19 @@ impl Instance { /// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727 /// [`ExternType`]: crate::ExternType - pub fn new(module: &Module, imports: &[Extern]) -> Result { - let store = module.store(); + pub fn new(store: &Store, module: &Module, imports: &[Extern]) -> Result { + if !Engine::same(store.engine(), module.engine()) { + bail!("cross-`Engine` instantiation is not currently supported"); + } + let info = module.register_frame_info(); - let handle = instantiate( - store, - module.compiled_module(), - imports, - store.compiler().signatures(), - Box::new(info), - )?; + store.register_jit_code(module.compiled_module().jit_code_ranges()); + + let handle = instantiate(store, module.compiled_module(), imports, Box::new(info))?; Ok(Instance { handle, + store: store.clone(), module: module.clone(), }) } @@ -199,7 +200,7 @@ impl Instance { /// This is the [`Store`] that generally serves as a sort of global cache /// for various instance-related things. pub fn store(&self) -> &Store { - self.module.store() + &self.store } /// Returns the list of exported items from this [`Instance`]. diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs index 3a0682c18c13..dcf4bddb3c49 100644 --- a/crates/wasmtime/src/linker.rs +++ b/crates/wasmtime/src/linker.rs @@ -150,7 +150,7 @@ impl Linker { /// (data (global.get 0) "foo") /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// linker.instantiate(&module)?; /// # Ok(()) /// # } @@ -203,7 +203,7 @@ impl Linker { /// (import "host" "log_str" (func (param i32 i32))) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// linker.instantiate(&module)?; /// # Ok(()) /// # } @@ -241,7 +241,7 @@ impl Linker { /// /// // Instantiate a small instance... /// let wat = r#"(module (func (export "run") ))"#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// let instance = linker.instantiate(&module)?; /// /// // ... and inform the linker that the name of this instance is @@ -257,7 +257,7 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// let instance = linker.instantiate(&module)?; /// # Ok(()) /// # } @@ -312,7 +312,7 @@ impl Linker { /// // this instance is `instance1`. This defines the `instance1::run` name /// // for our next module to use. /// let wat = r#"(module (func (export "run") ))"#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// linker.module("instance1", &module)?; /// /// let wat = r#" @@ -323,7 +323,7 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// let instance = linker.instantiate(&module)?; /// # Ok(()) /// # } @@ -350,7 +350,7 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// linker.module("commander", &module)?; /// let run = linker.get_default("")?.get0::<()>()?; /// run()?; @@ -369,7 +369,7 @@ impl Linker { /// ) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// linker.module("", &module)?; /// let count = linker.get_one_by_name("", "run")?.into_func().unwrap().get0::()?()?; /// assert_eq!(count, 0, "a Command should get a fresh instance on each invocation"); @@ -400,11 +400,12 @@ impl Linker { for export in module.exports() { if let Some(func_ty) = export.ty().func() { let imports = self.compute_imports(module)?; + let store = self.store.clone(); let module = module.clone(); let export_name = export.name().to_owned(); let func = Func::new(&self.store, func_ty.clone(), move |_, params, results| { // Create a new instance for this command execution. - let instance = Instance::new(&module, &imports)?; + let instance = Instance::new(&store, &module, &imports)?; // `unwrap()` everything here because we know the instance contains a // function export with the given name and signature because we're @@ -560,7 +561,7 @@ impl Linker { /// (import "host" "double" (func (param i32) (result i32))) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(store.engine(), wat)?; /// linker.instantiate(&module)?; /// # Ok(()) /// # } @@ -568,7 +569,7 @@ impl Linker { pub fn instantiate(&self, module: &Module) -> Result { let imports = self.compute_imports(module)?; - Instance::new(module, &imports) + Instance::new(&self.store, module, &imports) } fn compute_imports(&self, module: &Module) -> Result> { diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index 102b0df82dad..391c3b63de10 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -1,5 +1,5 @@ use crate::frame_info::GlobalFrameInfoRegistration; -use crate::runtime::Store; +use crate::runtime::Engine; use crate::types::{EntityType, ExportType, ExternType, ImportType}; use anyhow::{Error, Result}; use std::path::Path; @@ -23,6 +23,8 @@ use wasmtime_jit::CompiledModule; /// compiling the original wasm module only once with a single [`Module`] /// instance. /// +/// The `Module` is threadsafe and safe to share accross threads. +/// /// ## Modules and `Clone` /// /// Using `clone` on a `Module` is a cheap operation. It will not create an @@ -38,8 +40,8 @@ use wasmtime_jit::CompiledModule; /// ```no_run /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { -/// let store = Store::default(); -/// let module = Module::from_file(&store, "path/to/foo.wasm")?; +/// let engine = Engine::default(); +/// let module = Module::from_file(&engine, "path/to/foo.wasm")?; /// # Ok(()) /// # } /// ``` @@ -49,9 +51,9 @@ use wasmtime_jit::CompiledModule; /// ```no_run /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { -/// let store = Store::default(); +/// let engine = Engine::default(); /// // Now we're using the WebAssembly text extension: `.wat`! -/// let module = Module::from_file(&store, "path/to/foo.wat")?; +/// let module = Module::from_file(&engine, "path/to/foo.wat")?; /// # Ok(()) /// # } /// ``` @@ -62,12 +64,12 @@ use wasmtime_jit::CompiledModule; /// ```no_run /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { -/// let store = Store::default(); +/// let engine = Engine::default(); /// # let wasm_bytes: Vec = Vec::new(); -/// let module = Module::new(&store, &wasm_bytes)?; +/// let module = Module::new(&engine, &wasm_bytes)?; /// /// // It also works with the text format! -/// let module = Module::new(&store, "(module (func))")?; +/// let module = Module::new(&engine, "(module (func))")?; /// # Ok(()) /// # } /// ``` @@ -75,13 +77,9 @@ use wasmtime_jit::CompiledModule; /// [`Config`]: crate::Config #[derive(Clone)] pub struct Module { - inner: Arc, -} - -struct ModuleInner { - store: Store, - compiled: CompiledModule, - frame_info_registration: Mutex>>>, + engine: Engine, + compiled: Arc, + frame_info_registration: Arc>>>>, } impl Module { @@ -103,12 +101,7 @@ impl Module { /// compilation of a module. /// /// The WebAssembly binary will be decoded and validated. It will also be - /// compiled according to the configuration of the provided `store` and - /// cached in this type. - /// - /// The provided `store` is a global cache for compiled resources as well as - /// configuration for what wasm features are enabled. It's recommended to - /// share a `store` among modules if possible. + /// compiled according to the configuration of the provided `engine`. /// /// # Errors /// @@ -121,7 +114,7 @@ impl Module { /// * Implementation-specific limits were exceeded with a valid binary (for /// example too many locals) /// * The wasm binary may use features that are not enabled in the - /// configuration of `store` + /// configuration of `enging` /// * If the `wat` feature is enabled and the input is text, then it may be /// rejected if it fails to parse. /// @@ -138,9 +131,9 @@ impl Module { /// ```no_run /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); + /// # let engine = Engine::default(); /// # let wasm_bytes: Vec = Vec::new(); - /// let module = Module::new(&store, &wasm_bytes)?; + /// let module = Module::new(&engine, &wasm_bytes)?; /// # Ok(()) /// # } /// ``` @@ -151,25 +144,28 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::new(&store, "(module (func))")?; + /// # let engine = Engine::default(); + /// let module = Module::new(&engine, "(module (func))")?; /// # Ok(()) /// # } /// ``` - pub fn new(store: &Store, bytes: impl AsRef<[u8]>) -> Result { + pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result { #[cfg(feature = "wat")] let bytes = wat::parse_bytes(bytes.as_ref())?; - Module::from_binary(store, bytes.as_ref()) + Module::from_binary(engine, bytes.as_ref()) } /// Creates a new WebAssembly `Module` from the given in-memory `binary` /// data. The provided `name` will be used in traps/backtrace details. /// /// See [`Module::new`] for other details. - pub fn new_with_name(store: &Store, bytes: impl AsRef<[u8]>, name: &str) -> Result { - let mut module = Module::new(store, bytes.as_ref())?; - let inner = Arc::get_mut(&mut module.inner).unwrap(); - Arc::get_mut(inner.compiled.module_mut()).unwrap().name = Some(name.to_string()); + pub fn new_with_name(engine: &Engine, bytes: impl AsRef<[u8]>, name: &str) -> Result { + let mut module = Module::new(engine, bytes.as_ref())?; + Arc::get_mut(&mut module.compiled) + .unwrap() + .module_mut() + .expect("mutable module") + .name = Some(name.to_string()); Ok(module) } @@ -185,8 +181,8 @@ impl Module { /// ```no_run /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// let store = Store::default(); - /// let module = Module::from_file(&store, "./path/to/foo.wasm")?; + /// let engine = Engine::default(); + /// let module = Module::from_file(&engine, "./path/to/foo.wasm")?; /// # Ok(()) /// # } /// ``` @@ -196,17 +192,17 @@ impl Module { /// ```no_run /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::from_file(&store, "./path/to/foo.wat")?; + /// # let engine = Engine::default(); + /// let module = Module::from_file(&engine, "./path/to/foo.wat")?; /// # Ok(()) /// # } /// ``` - pub fn from_file(store: &Store, file: impl AsRef) -> Result { + pub fn from_file(engine: &Engine, file: impl AsRef) -> Result { #[cfg(feature = "wat")] let wasm = wat::parse_file(file)?; #[cfg(not(feature = "wat"))] let wasm = std::fs::read(file)?; - Module::new(store, &wasm) + Module::new(engine, &wasm) } /// Creates a new WebAssembly `Module` from the given in-memory `binary` @@ -223,9 +219,9 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); + /// # let engine = Engine::default(); /// let wasm = b"\0asm\x01\0\0\0"; - /// let module = Module::from_binary(&store, wasm)?; + /// let module = Module::from_binary(&engine, wasm)?; /// # Ok(()) /// # } /// ``` @@ -235,17 +231,17 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// assert!(Module::from_binary(&store, b"(module)").is_err()); + /// # let engine = Engine::default(); + /// assert!(Module::from_binary(&engine, b"(module)").is_err()); /// # Ok(()) /// # } /// ``` - pub fn from_binary(store: &Store, binary: &[u8]) -> Result { - Module::validate(store, binary)?; + pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result { + Module::validate(engine, binary)?; // Note that the call to `from_binary_unchecked` here should be ok // because we previously validated the binary, meaning we're guaranteed - // to pass a valid binary for `store`. - unsafe { Module::from_binary_unchecked(store, binary) } + // to pass a valid binary for `engine`. + unsafe { Module::from_binary_unchecked(engine, binary) } } /// Creates a new WebAssembly `Module` from the given in-memory `binary` @@ -257,7 +253,7 @@ impl Module { /// WebAssembly. The WebAssembly binary is not validated for /// correctness and it is simply assumed as valid. /// - /// For more information about creation of a module and the `store` argument + /// For more information about creation of a module and the `engine` argument /// see the documentation of [`Module::new`]. /// /// # Unsafety @@ -275,17 +271,17 @@ impl Module { /// While this assumes that the binary is valid it still needs to actually /// be somewhat valid for decoding purposes, and the basics of decoding can /// still fail. - pub unsafe fn from_binary_unchecked(store: &Store, binary: &[u8]) -> Result { - Module::compile(store, binary) + pub unsafe fn from_binary_unchecked(engine: &Engine, binary: &[u8]) -> Result { + Module::compile(engine, binary) } /// Validates `binary` input data as a WebAssembly binary given the - /// configuration in `store`. + /// configuration in `engine`. /// /// This function will perform a speedy validation of the `binary` input /// WebAssembly module (which is in [binary form][binary], the text format /// is not accepted by this function) and return either `Ok` or `Err` - /// depending on the results of validation. The `store` argument indicates + /// depending on the results of validation. The `engine` argument indicates /// configuration for WebAssembly features, for example, which are used to /// indicate what should be valid and what shouldn't be. /// @@ -299,29 +295,23 @@ impl Module { /// validation issue will be returned. /// /// [binary]: https://webassembly.github.io/spec/core/binary/index.html - pub fn validate(store: &Store, binary: &[u8]) -> Result<()> { - let config = store.engine().config().validating_config.clone(); + pub fn validate(engine: &Engine, binary: &[u8]) -> Result<()> { + let config = engine.config().validating_config.clone(); validate(binary, Some(config)).map_err(Error::new) } - unsafe fn compile(store: &Store, binary: &[u8]) -> Result { - let compiled = CompiledModule::new( - &mut store.compiler_mut(), - binary, - &*store.engine().config().profiler, - )?; + unsafe fn compile(engine: &Engine, binary: &[u8]) -> Result { + let compiled = CompiledModule::new(engine.compiler(), binary, &*engine.config().profiler)?; Ok(Module { - inner: Arc::new(ModuleInner { - store: store.clone(), - compiled, - frame_info_registration: Mutex::new(None), - }), + engine: engine.clone(), + compiled: Arc::new(compiled), + frame_info_registration: Arc::new(Mutex::new(None)), }) } pub(crate) fn compiled_module(&self) -> &CompiledModule { - &self.inner.compiled + &self.compiled } /// Returns identifier/name that this [`Module`] has. This name @@ -336,20 +326,20 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::new(&store, "(module $foo)")?; + /// # let engine = Engine::default(); + /// let module = Module::new(&engine, "(module $foo)")?; /// assert_eq!(module.name(), Some("foo")); /// - /// let module = Module::new(&store, "(module)")?; + /// let module = Module::new(&engine, "(module)")?; /// assert_eq!(module.name(), None); /// - /// let module = Module::new_with_name(&store, "(module)", "bar")?; + /// let module = Module::new_with_name(&engine, "(module)", "bar")?; /// assert_eq!(module.name(), Some("bar")); /// # Ok(()) /// # } /// ``` pub fn name(&self) -> Option<&str> { - self.inner.compiled.module().name.as_deref() + self.compiled.module().name.as_deref() } /// Returns the list of imports that this [`Module`] has and must be @@ -371,8 +361,8 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::new(&store, "(module)")?; + /// # let engine = Engine::default(); + /// let module = Module::new(&engine, "(module)")?; /// assert_eq!(module.imports().len(), 0); /// # Ok(()) /// # } @@ -383,13 +373,13 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); + /// # let engine = Engine::default(); /// let wat = r#" /// (module /// (import "host" "foo" (func)) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(&engine, wat)?; /// assert_eq!(module.imports().len(), 1); /// let import = module.imports().next().unwrap(); /// assert_eq!(import.module(), "host"); @@ -404,7 +394,7 @@ impl Module { pub fn imports<'module>( &'module self, ) -> impl ExactSizeIterator> + 'module { - let module = self.inner.compiled.module_ref(); + let module = self.compiled.module(); module .imports .iter() @@ -429,8 +419,8 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::new(&store, "(module)")?; + /// # let engine = Engine::default(); + /// let module = Module::new(&engine, "(module)")?; /// assert!(module.exports().next().is_none()); /// # Ok(()) /// # } @@ -441,14 +431,14 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); + /// # let engine = Engine::default(); /// let wat = r#" /// (module /// (func (export "foo")) /// (memory (export "memory") 1) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(&engine, wat)?; /// assert_eq!(module.exports().len(), 2); /// /// let mut exports = module.exports(); @@ -471,7 +461,7 @@ impl Module { pub fn exports<'module>( &'module self, ) -> impl ExactSizeIterator> + 'module { - let module = self.inner.compiled.module_ref(); + let module = self.compiled.module(); module.exports.iter().map(move |(name, entity_index)| { let r#type = EntityType::new(entity_index, module); ExportType::new(name, r#type) @@ -489,8 +479,8 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); - /// let module = Module::new(&store, "(module)")?; + /// # let engine = Engine::default(); + /// let module = Module::new(&engine, "(module)")?; /// assert!(module.get_export("foo").is_none()); /// # Ok(()) /// # } @@ -501,14 +491,14 @@ impl Module { /// ``` /// # use wasmtime::*; /// # fn main() -> anyhow::Result<()> { - /// # let store = Store::default(); + /// # let engine = Engine::default(); /// let wat = r#" /// (module /// (func (export "foo")) /// (memory (export "memory") 1) /// ) /// "#; - /// let module = Module::new(&store, wat)?; + /// let module = Module::new(&engine, wat)?; /// let foo = module.get_export("foo"); /// assert!(foo.is_some()); /// @@ -522,26 +512,31 @@ impl Module { /// # } /// ``` pub fn get_export<'module>(&'module self, name: &'module str) -> Option { - let module = self.inner.compiled.module_ref(); + let module = self.compiled.module(); let entity_index = module.exports.get(name)?; Some(EntityType::new(entity_index, module).extern_type()) } - /// Returns the [`Store`] that this [`Module`] was compiled into. - pub fn store(&self) -> &Store { - &self.inner.store + /// Returns the [`Engine`] that this [`Module`] was compiled by. + pub fn engine(&self) -> &Engine { + &self.engine } /// Register this module's stack frame information into the global scope. /// /// This is required to ensure that any traps can be properly symbolicated. pub(crate) fn register_frame_info(&self) -> Option> { - let mut info = self.inner.frame_info_registration.lock().unwrap(); + let mut info = self.frame_info_registration.lock().unwrap(); if let Some(info) = &*info { return info.clone(); } - let ret = super::frame_info::register(&self.inner.compiled).map(Arc::new); + let ret = super::frame_info::register(&self.compiled).map(Arc::new); *info = Some(ret.clone()); return ret; } } + +fn _assert_send_sync() { + fn _assert() {} + _assert::(); +} diff --git a/crates/wasmtime/src/runtime.rs b/crates/wasmtime/src/runtime.rs index bf3a01d1f0c4..d3915ae29ea9 100644 --- a/crates/wasmtime/src/runtime.rs +++ b/crates/wasmtime/src/runtime.rs @@ -14,11 +14,12 @@ use std::rc::{Rc, Weak}; use std::sync::Arc; use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig}; use wasmtime_environ::settings::{self, Configurable}; -use wasmtime_environ::{CacheConfig, Tunables}; +use wasmtime_environ::{ir, wasm, CacheConfig, Tunables}; use wasmtime_jit::{native, CompilationStrategy, Compiler}; use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent}; use wasmtime_runtime::{ - debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, VMExternRef, VMInterrupts, + debug_builtins, InstanceHandle, RuntimeMemoryCreator, SignalHandler, SignatureRegistry, + VMExternRef, VMInterrupts, VMSharedSignatureIndex, }; // Runtime Environment @@ -571,6 +572,16 @@ impl Config { cmp::max(guard_size, self.tunables.static_memory_offset_guard_size); self } + + fn build_compiler(&self) -> Compiler { + let isa = native::builder().finish(settings::Flags::new(self.flags.clone())); + Compiler::new( + isa, + self.strategy, + self.cache_config.clone(), + self.tunables.clone(), + ) + } } fn round_up_to_pages(val: u64) -> u64 { @@ -687,9 +698,14 @@ pub enum ProfilingStrategy { /// You can create an engine with default configuration settings using /// `Engine::default()`. Be sure to consult the documentation of [`Config`] for /// default settings. -#[derive(Default, Clone)] +#[derive(Clone)] pub struct Engine { - config: Arc, + inner: Arc, +} + +struct EngineInner { + config: Config, + compiler: Compiler, } impl Engine { @@ -698,13 +714,31 @@ impl Engine { pub fn new(config: &Config) -> Engine { debug_builtins::ensure_exported(); Engine { - config: Arc::new(config.clone()), + inner: Arc::new(EngineInner { + config: config.clone(), + compiler: config.build_compiler(), + }), } } /// Returns the configuration settings that this engine is using. pub fn config(&self) -> &Config { - &self.config + &self.inner.config + } + + pub(crate) fn compiler(&self) -> &Compiler { + &self.inner.compiler + } + + /// Returns whether the engine `a` and `b` refer to the same configuration. + pub fn same(a: &Engine, b: &Engine) -> bool { + Arc::ptr_eq(&a.inner, &b.inner) + } +} + +impl Default for Engine { + fn default() -> Engine { + Engine::new(&Config::default()) } } @@ -734,9 +768,11 @@ pub struct Store { pub(crate) struct StoreInner { engine: Engine, - compiler: RefCell, + interrupts: Arc, + signatures: RefCell, instances: RefCell>, signal_handler: RefCell>>>, + jit_code_ranges: RefCell>, host_info: RefCell>>>, } @@ -769,19 +805,14 @@ impl Store { // each one that's not relevant just won't do anything. wasmtime_runtime::init_traps(); - let isa = native::builder().finish(settings::Flags::new(engine.config.flags.clone())); - let compiler = Compiler::new( - isa, - engine.config.strategy, - engine.config.cache_config.clone(), - engine.config.tunables.clone(), - ); Store { inner: Rc::new(StoreInner { engine: engine.clone(), - compiler: RefCell::new(compiler), + interrupts: Arc::new(Default::default()), + signatures: RefCell::new(Default::default()), instances: RefCell::new(Vec::new()), signal_handler: RefCell::new(None), + jit_code_ranges: RefCell::new(Vec::new()), host_info: RefCell::new(HashMap::new()), }), } @@ -798,15 +829,61 @@ impl Store { /// Returns an optional reference to a ['RuntimeMemoryCreator'] pub(crate) fn memory_creator(&self) -> Option<&dyn RuntimeMemoryCreator> { - self.engine().config.memory_creator.as_ref().map(|x| x as _) + self.engine() + .config() + .memory_creator + .as_ref() + .map(|x| x as _) + } + + pub(crate) fn lookup_signature(&self, sig_index: VMSharedSignatureIndex) -> wasm::WasmFuncType { + self.inner + .signatures + .borrow() + .lookup_wasm(sig_index) + .expect("failed to lookup signature") } - pub(crate) fn compiler(&self) -> std::cell::Ref<'_, Compiler> { - self.inner.compiler.borrow() + pub(crate) fn register_signature( + &self, + wasm_sig: wasm::WasmFuncType, + native: ir::Signature, + ) -> VMSharedSignatureIndex { + self.inner + .signatures + .borrow_mut() + .register(wasm_sig, native) } - pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, Compiler> { - self.inner.compiler.borrow_mut() + pub(crate) fn signatures_mut(&self) -> std::cell::RefMut<'_, SignatureRegistry> { + self.inner.signatures.borrow_mut() + } + + /// Returns whether or not the given address falls within the JIT code + /// managed by the compiler + pub(crate) fn is_in_jit_code(&self, addr: usize) -> bool { + self.inner + .jit_code_ranges + .borrow() + .iter() + .any(|(start, end)| *start <= addr && addr < *end) + } + + pub(crate) fn register_jit_code(&self, mut ranges: impl Iterator) { + // Checking of we already registered JIT code ranges by searching + // first range start. + match ranges.next() { + None => (), + Some(first) => { + if !self.is_in_jit_code(first.0) { + // The range is not registered -- add all ranges (including + // first one) to the jit_code_ranges. + let mut jit_code_ranges = self.inner.jit_code_ranges.borrow_mut(); + jit_code_ranges.push(first); + jit_code_ranges.extend(ranges); + } + } + } } pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle { @@ -875,6 +952,10 @@ impl Store { self.inner.signal_handler.borrow_mut() } + pub(crate) fn interrupts(&self) -> &Arc { + &self.inner.interrupts + } + /// Returns whether the stores `a` and `b` refer to the same underlying /// `Store`. /// @@ -946,10 +1027,10 @@ impl Store { /// let interrupt_handle = store.interrupt_handle()?; /// /// // Compile and instantiate a small example with an infinite loop. - /// let module = Module::new(&store, r#" + /// let module = Module::new(&engine, r#" /// (func (export "run") (loop br 0)) /// "#)?; - /// let instance = Instance::new(&module, &[])?; + /// let instance = Instance::new(&store, &module, &[])?; /// let run = instance /// .get_func("run") /// .ok_or(anyhow::format_err!("failed to find `run` function export"))? @@ -967,9 +1048,9 @@ impl Store { /// # } /// ``` pub fn interrupt_handle(&self) -> Result { - if self.engine().config.tunables.interruptable { + if self.engine().config().tunables.interruptable { Ok(InterruptHandle { - interrupts: self.compiler().interrupts().clone(), + interrupts: self.interrupts().clone(), }) } else { bail!("interrupts aren't enabled for this `Store`") @@ -1052,47 +1133,47 @@ mod tests { let mut cfg = Config::new(); cfg.cranelift_opt_level(OptLevel::None) .cache_config_load(&config_path)?; - let store = Store::new(&Engine::new(&cfg)); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 0); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 1); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + let engine = Engine::new(&cfg); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); let mut cfg = Config::new(); cfg.cranelift_opt_level(OptLevel::Speed) .cache_config_load(&config_path)?; - let store = Store::new(&Engine::new(&cfg)); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 0); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 1); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + let engine = Engine::new(&cfg); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); let mut cfg = Config::new(); cfg.cranelift_opt_level(OptLevel::SpeedAndSize) .cache_config_load(&config_path)?; - let store = Store::new(&Engine::new(&cfg)); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 0); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 1); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + let engine = Engine::new(&cfg); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); // FIXME(#1523) need debuginfo on aarch64 before we run this test there if !cfg!(target_arch = "aarch64") { let mut cfg = Config::new(); cfg.debug_info(true).cache_config_load(&config_path)?; - let store = Store::new(&Engine::new(&cfg)); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 0); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); - Module::new(&store, "(module (func))")?; - assert_eq!(store.engine().config.cache_config.cache_hits(), 1); - assert_eq!(store.engine().config.cache_config.cache_misses(), 1); + let engine = Engine::new(&cfg); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); } Ok(()) diff --git a/crates/wasmtime/src/trampoline/create_handle.rs b/crates/wasmtime/src/trampoline/create_handle.rs index 6d78d49f07b1..6d2f691cc69f 100644 --- a/crates/wasmtime/src/trampoline/create_handle.rs +++ b/crates/wasmtime/src/trampoline/create_handle.rs @@ -32,25 +32,20 @@ pub(crate) fn create_handle( .local .signatures .values() - .map(|(wasm, native)| { - store - .compiler() - .signatures() - .register(wasm.clone(), native.clone()) - }) + .map(|(wasm, native)| store.register_signature(wasm.clone(), native.clone())) .collect::>(); unsafe { let handle = InstanceHandle::new( Arc::new(module), + Arc::new(()), finished_functions.into_boxed_slice(), trampolines, imports, store.memory_creator(), signatures.into_boxed_slice(), - None, state, - store.compiler().interrupts().clone(), + store.interrupts().clone(), )?; Ok(store.add_instance(handle)) } diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs index 1c3a4a31d951..15a80c6cd813 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/trampoline/func.rs @@ -247,10 +247,7 @@ pub fn create_handle_with_function( mem::size_of::(), )?; assert!(relocations.is_empty()); - let sig_id = store - .compiler() - .signatures() - .register(ft.to_wasm_func_type(), sig); + let sig_id = store.register_signature(ft.to_wasm_func_type(), sig); trampolines.insert(sig_id, trampoline); // Next up we wrap everything up into an `InstanceHandle` by publishing our @@ -300,10 +297,7 @@ pub unsafe fn create_handle_with_raw_function( .exports .insert("trampoline".to_string(), EntityIndex::Function(func_id)); finished_functions.push(func); - let sig_id = store - .compiler() - .signatures() - .register(ft.to_wasm_func_type(), sig); + let sig_id = store.register_signature(ft.to_wasm_func_type(), sig); trampolines.insert(sig_id, trampoline); create_handle(module, store, finished_functions, trampolines, state) diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index 303df76100a3..56d34f6dad1b 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -84,7 +84,7 @@ impl WastContext { } fn instantiate(&mut self, module: &[u8]) -> Result> { - let module = Module::new(&self.store, module)?; + let module = Module::new(self.store.engine(), module)?; self.modules.push(module.clone()); let instance = match self.linker.instantiate(&module) { Ok(i) => i, diff --git a/docs/lang-rust.md b/docs/lang-rust.md index 85aef3dbefa7..aeca23ae45c9 100644 --- a/docs/lang-rust.md +++ b/docs/lang-rust.md @@ -55,21 +55,22 @@ use std::error::Error; use wasmtime::*; fn main() -> Result<(), Box> { + let engine = Engine::default(); // A `Store` is a sort of "global object" in a sense, but for now it suffices // to say that it's generally passed to most constructors. - let store = Store::default(); + let store = Store::new(&engine); # if false { // We start off by creating a `Module` which represents a compiled form // of our input wasm module. In this case it'll be JIT-compiled after // we parse the text format. - let module = Module::from_file(&store, "hello.wat")?; + let module = Module::from_file(&engine, "hello.wat")?; # } -# let module = Module::new(&store, r#"(module (func (export "answer") (result i32) i32.const 42))"#)?; +# let module = Module::new(&engine, r#"(module (func (export "answer") (result i32) i32.const 42))"#)?; // After we have a compiled `Module` we can then instantiate it, creating // an `Instance` which we can actually poke at functions on. - let instance = Instance::new(&module, &[])?; + let instance = Instance::new(&store, &module, &[])?; // The `Instance` gives us access to various exported functions and items, // which we access here to pull out our `answer` exported function and @@ -142,11 +143,12 @@ use std::error::Error; use wasmtime::*; fn main() -> Result<(), Box> { - let store = Store::default(); + let engine = Engine::default(); + let store = Store::new(&engine); # if false { - let module = Module::from_file(&store, "hello.wat")?; + let module = Module::from_file(&engine, "hello.wat")?; # } -# let module = Module::new(&store, r#"(module (import "" "log" (func $log (param i32))) (import "" "double" (func $double (param i32) (result i32))) (func (export "run") i32.const 0 call $log i32.const 1 call $log i32.const 2 call $double call $log))"#)?; +# let module = Module::new(&engine, r#"(module (import "" "log" (func $log (param i32))) (import "" "double" (func $double (param i32) (result i32))) (func (export "run") i32.const 0 call $log i32.const 1 call $log i32.const 2 call $double call $log))"#)?; // First we can create our `log` function, which will simply print out the // parameter it receives. @@ -160,7 +162,7 @@ fn main() -> Result<(), Box> { // When instantiating the module we now need to provide the imports to the // instantiation process. This is the second slice argument, where each // entry in the slice must line up with the imports in the module. - let instance = Instance::new(&module, &[log.into(), double.into()])?; + let instance = Instance::new(&store, &module, &[log.into(), double.into()])?; let run = instance .get_func("run") diff --git a/docs/wasm-wat.md b/docs/wasm-wat.md index 0f9153771696..9210e67c572d 100644 --- a/docs/wasm-wat.md +++ b/docs/wasm-wat.md @@ -37,7 +37,8 @@ You can also see how this works in the Rust API like so: use wasmtime::*; # fn main() -> anyhow::Result<()> { -let store = Store::default(); +let engine = Engine::default(); +let store = Store::new(&engine); let wat = r#" (module (func (export "add") (param i32 i32) (result i32) @@ -45,8 +46,8 @@ let wat = r#" local.get 1 i32.add)) "#; -let module = Module::new(&store, wat)?; -let instance = Instance::new(&module, &[])?; +let module = Module::new(&engine, wat)?; +let instance = Instance::new(&store, &module, &[])?; let add = instance.get_func("add").unwrap(); let add = add.get2::()?; println!("1 + 2 = {}", add(1, 2)?); diff --git a/examples/fib-debug/main.c b/examples/fib-debug/main.c index dfebf8993e4c..e133f8d6acc6 100644 --- a/examples/fib-debug/main.c +++ b/examples/fib-debug/main.c @@ -71,7 +71,7 @@ int main(int argc, const char* argv[]) { printf("Instantiating module...\n"); wasm_instance_t* instance = NULL; wasm_trap_t *trap = NULL; - error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); + error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap); if (error != NULL || trap != NULL) exit_with_error("failed to instantiate", error, trap); wasm_module_delete(module); diff --git a/examples/fib-debug/main.rs b/examples/fib-debug/main.rs index ff35b8c83c10..8cc2d9cd8f1a 100644 --- a/examples/fib-debug/main.rs +++ b/examples/fib-debug/main.rs @@ -17,8 +17,8 @@ fn main() -> Result<()> { // debugged in GDB. let engine = Engine::new(Config::new().debug_info(true)); let store = Store::new(&engine); - let module = Module::from_file(&store, "target/wasm32-unknown-unknown/debug/fib.wasm")?; - let instance = Instance::new(&module, &[])?; + let module = Module::from_file(&engine, "target/wasm32-unknown-unknown/debug/fib.wasm")?; + let instance = Instance::new(&store, &module, &[])?; // Invoke `fib` export let fib = instance diff --git a/examples/gcd.c b/examples/gcd.c index c4b6beb69c74..bcbeed0940c4 100644 --- a/examples/gcd.c +++ b/examples/gcd.c @@ -65,7 +65,7 @@ int main() { wasm_byte_vec_delete(&wasm); wasm_trap_t *trap = NULL; wasm_instance_t *instance = NULL; - error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); + error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap); if (instance == NULL) exit_with_error("failed to instantiate", error, trap); diff --git a/examples/gcd.rs b/examples/gcd.rs index 067ab7b2dff8..45c87d7f2619 100644 --- a/examples/gcd.rs +++ b/examples/gcd.rs @@ -11,8 +11,8 @@ fn main() -> Result<()> { // `Module` which is attached to a `Store` cache. After we've got that we // can instantiate it. let store = Store::default(); - let module = Module::from_file(&store, "examples/gcd.wat")?; - let instance = Instance::new(&module, &[])?; + let module = Module::from_file(store.engine(), "examples/gcd.wat")?; + let instance = Instance::new(&store, &module, &[])?; // Invoke `gcd` export let gcd = instance diff --git a/examples/hello.c b/examples/hello.c index 2082247203f8..f9d4b5982a86 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -87,7 +87,7 @@ int main() { wasm_trap_t *trap = NULL; wasm_instance_t *instance = NULL; const wasm_extern_t *imports[] = { wasm_func_as_extern(hello) }; - error = wasmtime_instance_new(module, imports, 1, &instance, &trap); + error = wasmtime_instance_new(store, module, imports, 1, &instance, &trap); if (instance == NULL) exit_with_error("failed to instantiate", error, trap); diff --git a/examples/hello.rs b/examples/hello.rs index 00772b22eb01..89f904670be5 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -15,7 +15,7 @@ fn main() -> Result<()> { // Compile the wasm binary into an in-memory instance of a `Module`. println!("Compiling module..."); - let module = Module::from_file(&store, "examples/hello.wat")?; + let module = Module::from_file(store.engine(), "examples/hello.wat")?; // Here we handle the imports of the module, which in this case is our // `HelloCallback` type and its associated implementation of `Callback. @@ -30,7 +30,7 @@ fn main() -> Result<()> { // Note that this is where the wasm `start` function, if any, would run. println!("Instantiating module..."); let imports = [hello_func.into()]; - let instance = Instance::new(&module, &imports)?; + let instance = Instance::new(&store, &module, &imports)?; // Next we poke around a bit to extract the `run` function from the module. println!("Extracting export..."); diff --git a/examples/interrupt.c b/examples/interrupt.c index 2a4f5cfbd289..81971b66bcd8 100644 --- a/examples/interrupt.c +++ b/examples/interrupt.c @@ -93,7 +93,7 @@ int main() { wasm_byte_vec_delete(&wasm); if (error != NULL) exit_with_error("failed to compile module", error, NULL); - error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); + error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap); if (instance == NULL) exit_with_error("failed to instantiate", error, trap); diff --git a/examples/interrupt.rs b/examples/interrupt.rs index 6a42867cc24d..b45684463f09 100644 --- a/examples/interrupt.rs +++ b/examples/interrupt.rs @@ -14,8 +14,8 @@ fn main() -> Result<()> { let interrupt_handle = store.interrupt_handle()?; // Compile and instantiate a small example with an infinite loop. - let module = Module::from_file(&store, "examples/interrupt.wat")?; - let instance = Instance::new(&module, &[])?; + let module = Module::from_file(&engine, "examples/interrupt.wat")?; + let instance = Instance::new(&store, &module, &[])?; let run = instance .get_func("run") .ok_or(anyhow::format_err!("failed to find `run` function export"))? diff --git a/examples/linking.rs b/examples/linking.rs index 4f9548739e6c..f46e7a4f89f0 100644 --- a/examples/linking.rs +++ b/examples/linking.rs @@ -7,7 +7,8 @@ use wasmtime::*; use wasmtime_wasi::{Wasi, WasiCtx}; fn main() -> Result<()> { - let store = Store::default(); + let engine = Engine::default(); + let store = Store::new(&engine); // First set up our linker which is going to be linking modules together. We // want our linker to have wasi available, so we set that up here as well. @@ -16,8 +17,8 @@ fn main() -> Result<()> { wasi.add_to_linker(&mut linker)?; // Load and compile our two modules - let linking1 = Module::from_file(&store, "examples/linking1.wat")?; - let linking2 = Module::from_file(&store, "examples/linking2.wat")?; + let linking1 = Module::from_file(&engine, "examples/linking1.wat")?; + let linking2 = Module::from_file(&engine, "examples/linking2.wat")?; // Instantiate our first module which only uses WASI, then register that // instance with the linker since the next linking will use it. diff --git a/examples/memory.c b/examples/memory.c index 1687048f9254..47453e87b981 100644 --- a/examples/memory.c +++ b/examples/memory.c @@ -167,7 +167,7 @@ int main(int argc, const char* argv[]) { printf("Instantiating module...\n"); wasm_instance_t* instance = NULL; wasm_trap_t *trap = NULL; - error = wasmtime_instance_new(module, NULL, 0, &instance, &trap); + error = wasmtime_instance_new(store, module, NULL, 0, &instance, &trap); if (!instance) exit_with_error("failed to instantiate", error, trap); diff --git a/examples/memory.rs b/examples/memory.rs index 6f952dbb21b1..9de8488ae2b4 100644 --- a/examples/memory.rs +++ b/examples/memory.rs @@ -13,8 +13,8 @@ fn main() -> Result<()> { // Create our `Store` context and then compile a module and create an // instance from the compiled module all in one go. let wasmtime_store = Store::default(); - let module = Module::from_file(&wasmtime_store, "examples/memory.wat")?; - let instance = Instance::new(&module, &[])?; + let module = Module::from_file(wasmtime_store.engine(), "examples/memory.wat")?; + let instance = Instance::new(&wasmtime_store, &module, &[])?; // Load up our exports from the instance let memory = instance diff --git a/examples/multi.c b/examples/multi.c index 0006dd0e5dcd..3248ec021580 100644 --- a/examples/multi.c +++ b/examples/multi.c @@ -115,7 +115,7 @@ int main(int argc, const char* argv[]) { const wasm_extern_t* imports[] = {wasm_func_as_extern(callback_func)}; wasm_instance_t* instance = NULL; wasm_trap_t* trap = NULL; - error = wasmtime_instance_new(module, imports, 1, &instance, &trap); + error = wasmtime_instance_new(store, module, imports, 1, &instance, &trap); if (!instance) exit_with_error("failed to instantiate", error, trap); diff --git a/examples/multi.rs b/examples/multi.rs index 2fe75b0311c1..ebdbb9101cfc 100644 --- a/examples/multi.rs +++ b/examples/multi.rs @@ -12,11 +12,12 @@ use wasmtime::*; fn main() -> Result<()> { println!("Initializing..."); - let store = Store::default(); + let engine = Engine::default(); + let store = Store::new(&engine); // Compile. println!("Compiling module..."); - let module = Module::from_file(&store, "examples/multi.wat")?; + let module = Module::from_file(&engine, "examples/multi.wat")?; // Create external print functions. println!("Creating callback..."); @@ -35,7 +36,7 @@ fn main() -> Result<()> { // Instantiate. println!("Instantiating module..."); - let instance = Instance::new(&module, &[callback_func.into()])?; + let instance = Instance::new(&store, &module, &[callback_func.into()])?; // Extract exports. println!("Extracting export..."); diff --git a/examples/threads.c b/examples/threads.c new file mode 100644 index 000000000000..bc04ba69c634 --- /dev/null +++ b/examples/threads.c @@ -0,0 +1,184 @@ +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define own + +static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); + +const int N_THREADS = 10; +const int N_REPS = 3; + +// A function to be called from Wasm code. +own wasm_trap_t* callback(const wasm_val_t args[], wasm_val_t results[]) { + assert(args[0].kind == WASM_I32); + printf("> Thread %d running\n", args[0].of.i32); + return NULL; +} + + +typedef struct { + wasm_engine_t* engine; + wasm_shared_module_t* module; + int id; +} thread_args; + +void* run(void* args_abs) { + thread_args* args = (thread_args*)args_abs; + + // Rereate store and module. + own wasm_store_t* store = wasm_store_new(args->engine); + own wasm_module_t* module = wasm_module_obtain(store, args->module); + + // Run the example N times. + for (int i = 0; i < N_REPS; ++i) { + usleep(100000); + + // Create imports. + own wasm_functype_t* func_type = wasm_functype_new_1_0(wasm_valtype_new_i32()); + own wasm_func_t* func = wasm_func_new(store, func_type, callback); + wasm_functype_delete(func_type); + + wasm_val_t val = {.kind = WASM_I32, .of = {.i32 = (int32_t)args->id}}; + own wasm_globaltype_t* global_type = + wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST); + own wasm_global_t* global = wasm_global_new(store, global_type, &val); + wasm_globaltype_delete(global_type); + + // Instantiate. + const wasm_extern_t* imports[] = { + wasm_func_as_extern(func), wasm_global_as_extern(global), + }; + own wasm_instance_t* instance = + wasm_instance_new(store, module, imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return NULL; + } + + wasm_func_delete(func); + wasm_global_delete(global); + + // Extract export. + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return NULL; + } + const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return NULL; + } + + wasm_instance_delete(instance); + + // Call. + if (wasm_func_call(run_func, NULL, NULL)) { + printf("> Error calling function!\n"); + return NULL; + } + + wasm_extern_vec_delete(&exports); + } + + wasm_module_delete(module); + wasm_store_delete(store); + + free(args_abs); + + return NULL; +} + +int main(int argc, const char *argv[]) { + // Initialize. + wasm_engine_t* engine = wasm_engine_new(); + + // Load our input file to parse it next + FILE* file = fopen("examples/threads.wat", "r"); + if (!file) { + printf("> Error loading file!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t wat; + wasm_byte_vec_new_uninitialized(&wat, file_size); + if (fread(wat.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Parse the wat into the binary wasm format + wasm_byte_vec_t binary; + wasmtime_error_t *error = wasmtime_wat2wasm(&wat, &binary); + if (error != NULL) + exit_with_error("failed to parse wat", error, NULL); + wasm_byte_vec_delete(&wat); + + // Compile and share. + own wasm_store_t* store = wasm_store_new(engine); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + own wasm_shared_module_t* shared = wasm_module_share(module); + + wasm_module_delete(module); + wasm_store_delete(store); + + // Spawn threads. + pthread_t threads[N_THREADS]; + for (int i = 0; i < N_THREADS; i++) { + thread_args* args = malloc(sizeof(thread_args)); + args->id = i; + args->engine = engine; + args->module = shared; + printf("Initializing thread %d...\n", i); + pthread_create(&threads[i], NULL, &run, args); + } + + for (int i = 0; i < N_THREADS; i++) { + printf("Waiting for thread: %d\n", i); + pthread_join(threads[i], NULL); + } + + wasm_shared_module_delete(shared); + wasm_engine_delete(engine); + + return 0; +} + +static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) { + fprintf(stderr, "error: %s\n", message); + wasm_byte_vec_t error_message; + if (error != NULL) { + wasmtime_error_message(error, &error_message); + } else { + wasm_trap_message(trap, &error_message); + } + fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data); + wasm_byte_vec_delete(&error_message); + exit(1); +} + +#else +// TODO implement example for Windows +int main(int argc, const char *argv[]) { + return 0; +} +#endif // _WIN32 diff --git a/examples/threads.rs b/examples/threads.rs new file mode 100644 index 000000000000..3d3ffe8e573a --- /dev/null +++ b/examples/threads.rs @@ -0,0 +1,70 @@ +// You can execute this example with `cargo run --example threads` + +use anyhow::{format_err, Result}; +use std::thread; +use std::time; +use wasmtime::*; + +const N_THREADS: i32 = 10; +const N_REPS: i32 = 3; + +fn print_message(_: Caller<'_>, args: &[Val], _: &mut [Val]) -> Result<(), Trap> { + println!("> Thread {} is running", args[0].unwrap_i32()); + Ok(()) +} + +fn run(engine: &Engine, module: Module, id: i32) -> Result<()> { + let store = Store::new(&engine); + + // Create external print functions. + println!("Creating callback..."); + let callback_type = FuncType::new(Box::new([ValType::I32]), Box::new([])); + let callback_func = Func::new(&store, callback_type, print_message); + + let id_type = GlobalType::new(ValType::I32, Mutability::Const); + let id_global = Global::new(&store, id_type, Val::I32(id))?; + + // Instantiate. + println!("Instantiating module..."); + let instance = Instance::new(&store, &module, &[callback_func.into(), id_global.into()])?; + + // Extract exports. + println!("Extracting export..."); + let g = instance + .get_func("run") + .ok_or(format_err!("failed to find export `eun`"))?; + + for _ in 0..N_REPS { + thread::sleep(time::Duration::from_millis(100)); + // Call `$run`. + drop(g.call(&[])?); + } + + Ok(()) +} + +fn main() -> Result<()> { + println!("Initializing..."); + let engine = Engine::default(); + + // Compile. + println!("Compiling module..."); + let module = Module::from_file(&engine, "examples/threads.wat")?; + + let mut children = Vec::new(); + for id in 0..N_THREADS { + let engine = engine.clone(); + let module = module.clone(); + children.push(thread::spawn(move || { + run(&engine, module, id).expect("Success"); + })); + } + + for (i, child) in children.into_iter().enumerate() { + if let Err(_) = child.join() { + println!("Thread #{} errors", i); + } + } + + Ok(()) +} diff --git a/examples/threads.wat b/examples/threads.wat new file mode 100644 index 000000000000..228f99f2b564 --- /dev/null +++ b/examples/threads.wat @@ -0,0 +1,5 @@ +(module + (func $message (import "" "hello") (param i32)) + (global $id (import "" "id") i32) + (func (export "run") (call $message (global.get $id))) +) diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs index c663e9527751..35cb94813567 100644 --- a/examples/wasi/main.rs +++ b/examples/wasi/main.rs @@ -18,7 +18,7 @@ fn main() -> Result<()> { wasi.add_to_linker(&mut linker)?; // Instantiate our module with the imports we've created, and run it. - let module = Module::from_file(&store, "target/wasm32-wasi/debug/wasi.wasm")?; + let module = Module::from_file(store.engine(), "target/wasm32-wasi/debug/wasi.wasm")?; linker.module("", &module)?; linker.get_default("")?.get0::<()>()?()?; diff --git a/src/commands/run.rs b/src/commands/run.rs index 4e40d057fd37..359686031e2f 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -141,7 +141,7 @@ impl RunCommand { // Load the preload wasm modules. for (name, path) in self.preloads.iter() { // Read the wasm module binary either as `*.wat` or a raw binary - let module = Module::from_file(linker.store(), path)?; + let module = Module::from_file(&engine, path)?; // Add the module's functions to the linker. linker.module(name, &module).context(format!( @@ -247,7 +247,7 @@ impl RunCommand { // Read the wasm module binary either as `*.wat` or a raw binary. // Use "" as a default module name. - let module = Module::from_file(linker.store(), &self.module)?; + let module = Module::from_file(linker.store().engine(), &self.module)?; linker .module("", &module) .context(format!("failed to instantiate {:?}", self.module))?; diff --git a/tests/all/custom_signal_handler.rs b/tests/all/custom_signal_handler.rs index 50425f0dfc5e..df35974e5e80 100644 --- a/tests/all/custom_signal_handler.rs +++ b/tests/all/custom_signal_handler.rs @@ -90,8 +90,8 @@ mod tests { fn test_custom_signal_handler_single_instance() -> Result<()> { let engine = Engine::new(&Config::default()); let store = Store::new(&engine); - let module = Module::new(&store, WAT1)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(&engine, WAT1)?; + let instance = Instance::new(&store, &module, &[])?; let (base, length) = set_up_memory(&instance); unsafe { @@ -150,11 +150,11 @@ mod tests { fn test_custom_signal_handler_multiple_instances() -> Result<()> { let engine = Engine::new(&Config::default()); let store = Store::new(&engine); - let module = Module::new(&store, WAT1)?; + let module = Module::new(&engine, WAT1)?; // Set up multiple instances - let instance1 = Instance::new(&module, &[])?; + let instance1 = Instance::new(&store, &module, &[])?; let instance1_handler_triggered = Rc::new(AtomicBool::new(false)); unsafe { @@ -196,7 +196,7 @@ mod tests { ); } - let instance2 = Instance::new(&module, &[]).expect("failed to instantiate module"); + let instance2 = Instance::new(&store, &module, &[]).expect("failed to instantiate module"); let instance2_handler_triggered = Rc::new(AtomicBool::new(false)); unsafe { @@ -244,8 +244,8 @@ mod tests { let store = Store::new(&engine); // instance1 which defines 'read' - let module1 = Module::new(&store, WAT1)?; - let instance1 = Instance::new(&module1, &[])?; + let module1 = Module::new(&engine, WAT1)?; + let instance1 = Instance::new(&store, &module1, &[])?; let (base1, length1) = set_up_memory(&instance1); unsafe { store.set_signal_handler(move |signum, siginfo, _| { @@ -258,8 +258,8 @@ mod tests { let instance1_read = instance1_exports.next().unwrap(); // instance2 which calls 'instance1.read' - let module2 = Module::new(&store, WAT2)?; - let instance2 = Instance::new(&module2, &[instance1_read.into_extern()])?; + let module2 = Module::new(&engine, WAT2)?; + let instance2 = Instance::new(&store, &module2, &[instance1_read.into_extern()])?; // since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle // SIGSEGV originating from within the memory of instance1 unsafe { diff --git a/tests/all/externals.rs b/tests/all/externals.rs index 281365d18ee6..05ef48c63271 100644 --- a/tests/all/externals.rs +++ b/tests/all/externals.rs @@ -57,8 +57,9 @@ fn bad_tables() { fn cross_store() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); - let store1 = Store::new(&Engine::new(&cfg)); - let store2 = Store::new(&Engine::new(&cfg)); + let engine = Engine::new(&cfg); + let store1 = Store::new(&engine); + let store2 = Store::new(&engine); // ============ Cross-store instantiation ============== @@ -70,17 +71,17 @@ fn cross_store() -> anyhow::Result<()> { let ty = TableType::new(ValType::FuncRef, Limits::new(1, None)); let table = Table::new(&store2, ty, Val::ExternRef(None))?; - let need_func = Module::new(&store1, r#"(module (import "" "" (func)))"#)?; - assert!(Instance::new(&need_func, &[func.into()]).is_err()); + let need_func = Module::new(&engine, r#"(module (import "" "" (func)))"#)?; + assert!(Instance::new(&store1, &need_func, &[func.into()]).is_err()); - let need_global = Module::new(&store1, r#"(module (import "" "" (global i32)))"#)?; - assert!(Instance::new(&need_global, &[global.into()]).is_err()); + let need_global = Module::new(&engine, r#"(module (import "" "" (global i32)))"#)?; + assert!(Instance::new(&store1, &need_global, &[global.into()]).is_err()); - let need_table = Module::new(&store1, r#"(module (import "" "" (table 1 funcref)))"#)?; - assert!(Instance::new(&need_table, &[table.into()]).is_err()); + let need_table = Module::new(&engine, r#"(module (import "" "" (table 1 funcref)))"#)?; + assert!(Instance::new(&store1, &need_table, &[table.into()]).is_err()); - let need_memory = Module::new(&store1, r#"(module (import "" "" (memory 1)))"#)?; - assert!(Instance::new(&need_memory, &[memory.into()]).is_err()); + let need_memory = Module::new(&engine, r#"(module (import "" "" (memory 1)))"#)?; + assert!(Instance::new(&store1, &need_memory, &[memory.into()]).is_err()); // ============ Cross-store globals ============== @@ -106,7 +107,7 @@ fn cross_store() -> anyhow::Result<()> { // ============ Cross-store funcs ============== // TODO: need to actually fill this out once we support externref params/locals - // let module = Module::new(&store1, r#"(module (func (export "a") (param funcref)))"#)?; + // let module = Module::new(&engine, r#"(module (func (export "a") (param funcref)))"#)?; Ok(()) } diff --git a/tests/all/func.rs b/tests/all/func.rs index 4d8f5d8702e5..2c3f8bd378d9 100644 --- a/tests/all/func.rs +++ b/tests/all/func.rs @@ -61,8 +61,8 @@ fn dtor_delayed() -> Result<()> { assert_eq!(HITS.load(SeqCst), 0); let wasm = wat::parse_str(r#"(import "" "" (func))"#)?; - let module = Module::new(&store, &wasm)?; - let instance = Instance::new(&module, &[func.into()])?; + let module = Module::new(store.engine(), &wasm)?; + let instance = Instance::new(&store, &module, &[func.into()])?; assert_eq!(HITS.load(SeqCst), 0); drop((instance, module, store)); assert_eq!(HITS.load(SeqCst), 1); @@ -142,8 +142,9 @@ fn import_works() -> Result<()> { "#, )?; let store = Store::default(); - let module = Module::new(&store, &wasm)?; + let module = Module::new(store.engine(), &wasm)?; Instance::new( + &store, &module, &[ Func::wrap(&store, || { @@ -195,8 +196,9 @@ fn trap_import() -> Result<()> { "#, )?; let store = Store::default(); - let module = Module::new(&store, &wasm)?; + let module = Module::new(store.engine(), &wasm)?; let trap = Instance::new( + &store, &module, &[Func::wrap(&store, || -> Result<(), Trap> { Err(Trap::new("foo")) }).into()], ) @@ -261,7 +263,7 @@ fn get_from_signature() { fn get_from_module() -> anyhow::Result<()> { let store = Store::default(); let module = Module::new( - &store, + store.engine(), r#" (module (func (export "f0")) @@ -272,7 +274,7 @@ fn get_from_module() -> anyhow::Result<()> { "#, )?; - let instance = Instance::new(&module, &[])?; + let instance = Instance::new(&store, &module, &[])?; let f0 = instance.get_func("f0").unwrap(); assert!(f0.get0::<()>().is_ok()); assert!(f0.get0::().is_err()); @@ -340,7 +342,7 @@ fn caller_memory() -> anyhow::Result<()> { assert!(c.get_export("x").is_none()); }); let module = Module::new( - &store, + store.engine(), r#" (module (import "" "" (func $f)) @@ -349,13 +351,13 @@ fn caller_memory() -> anyhow::Result<()> { "#, )?; - Instance::new(&module, &[f.into()])?; + Instance::new(&store, &module, &[f.into()])?; let f = Func::wrap(&store, |c: Caller<'_>| { assert!(c.get_export("memory").is_some()); }); let module = Module::new( - &store, + store.engine(), r#" (module (import "" "" (func $f)) @@ -365,7 +367,7 @@ fn caller_memory() -> anyhow::Result<()> { "#, )?; - Instance::new(&module, &[f.into()])?; + Instance::new(&store, &module, &[f.into()])?; let f = Func::wrap(&store, |c: Caller<'_>| { assert!(c.get_export("m").is_some()); @@ -374,7 +376,7 @@ fn caller_memory() -> anyhow::Result<()> { assert!(c.get_export("t").is_none()); }); let module = Module::new( - &store, + store.engine(), r#" (module (import "" "" (func $f)) @@ -387,7 +389,7 @@ fn caller_memory() -> anyhow::Result<()> { "#, )?; - Instance::new(&module, &[f.into()])?; + Instance::new(&store, &module, &[f.into()])?; Ok(()) } diff --git a/tests/all/globals.rs b/tests/all/globals.rs index d0e6896c1b98..3a7c8b488cf1 100644 --- a/tests/all/globals.rs +++ b/tests/all/globals.rs @@ -63,19 +63,19 @@ fn mutability() -> anyhow::Result<()> { fn use_after_drop() -> anyhow::Result<()> { let store = Store::default(); let module = Module::new( - &store, + store.engine(), r#" (module (global (export "foo") (mut i32) (i32.const 100))) "#, )?; - let instance = Instance::new(&module, &[])?; + let instance = Instance::new(&store, &module, &[])?; let g = instance.get_global("foo").unwrap(); assert_eq!(g.get().i32(), Some(100)); g.set(101.into())?; drop(instance); assert_eq!(g.get().i32(), Some(101)); - Instance::new(&module, &[])?; + Instance::new(&store, &module, &[])?; assert_eq!(g.get().i32(), Some(101)); drop(module); assert_eq!(g.get().i32(), Some(101)); diff --git a/tests/all/iloop.rs b/tests/all/iloop.rs index abfe5e4b3de2..23e6f85aa8d3 100644 --- a/tests/all/iloop.rs +++ b/tests/all/iloop.rs @@ -19,14 +19,14 @@ fn hugely_recursive_module(store: &Store) -> anyhow::Result { } wat.push_str("(func call 0)\n"); - Module::new(&store, &wat) + Module::new(store.engine(), &wat) } #[test] fn loops_interruptable() -> anyhow::Result<()> { let store = interruptable_store(); - let module = Module::new(&store, r#"(func (export "loop") (loop br 0))"#)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), r#"(func (export "loop") (loop br 0))"#)?; + let instance = Instance::new(&store, &module, &[])?; let iloop = instance.get_func("loop").unwrap().get0::<()>()?; store.interrupt_handle()?.interrupt(); let trap = iloop().unwrap_err(); @@ -39,7 +39,7 @@ fn functions_interruptable() -> anyhow::Result<()> { let store = interruptable_store(); let module = hugely_recursive_module(&store)?; let func = Func::wrap(&store, || {}); - let instance = Instance::new(&module, &[func.into()])?; + let instance = Instance::new(&store, &module, &[func.into()])?; let iloop = instance.get_func("loop").unwrap().get0::<()>()?; store.interrupt_handle()?.interrupt(); let trap = iloop().unwrap_err(); @@ -59,7 +59,7 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> { static HITS: AtomicUsize = AtomicUsize::new(0); let store = interruptable_store(); let module = Module::new( - &store, + store.engine(), r#" (import "" "" (func)) @@ -73,7 +73,7 @@ fn loop_interrupt_from_afar() -> anyhow::Result<()> { let func = Func::wrap(&store, || { HITS.fetch_add(1, SeqCst); }); - let instance = Instance::new(&module, &[func.into()])?; + let instance = Instance::new(&store, &module, &[func.into()])?; // Use the instance's interrupt handle to wait for it to enter the loop long // enough and then we signal an interrupt happens. @@ -109,7 +109,7 @@ fn function_interrupt_from_afar() -> anyhow::Result<()> { let func = Func::wrap(&store, || { HITS.fetch_add(1, SeqCst); }); - let instance = Instance::new(&module, &[func.into()])?; + let instance = Instance::new(&store, &module, &[func.into()])?; // Use the instance's interrupt handle to wait for it to enter the loop long // enough and then we signal an interrupt happens. diff --git a/tests/all/import_calling_export.rs b/tests/all/import_calling_export.rs index 9e7f96b078f2..09eb80979f6b 100644 --- a/tests/all/import_calling_export.rs +++ b/tests/all/import_calling_export.rs @@ -17,7 +17,7 @@ fn test_import_calling_export() { "#; let store = Store::default(); - let module = Module::new(&store, WAT).expect("failed to create module"); + let module = Module::new(store.engine(), WAT).expect("failed to create module"); let other = Rc::new(RefCell::new(None::)); let other2 = Rc::downgrade(&other); @@ -40,7 +40,7 @@ fn test_import_calling_export() { let imports = vec![callback_func.into()]; let instance = - Instance::new(&module, imports.as_slice()).expect("failed to instantiate module"); + Instance::new(&store, &module, imports.as_slice()).expect("failed to instantiate module"); let run_func = instance .get_func("run") @@ -67,7 +67,7 @@ fn test_returns_incorrect_type() -> Result<()> { "#; let store = Store::default(); - let module = Module::new(&store, WAT)?; + let module = Module::new(store.engine(), WAT)?; let callback_func = Func::new( &store, @@ -80,7 +80,7 @@ fn test_returns_incorrect_type() -> Result<()> { ); let imports = vec![callback_func.into()]; - let instance = Instance::new(&module, imports.as_slice())?; + let instance = Instance::new(&store, &module, imports.as_slice())?; let run_func = instance .get_func("run") diff --git a/tests/all/import_indexes.rs b/tests/all/import_indexes.rs index d13d100a9f0f..d9cd06cd6694 100644 --- a/tests/all/import_indexes.rs +++ b/tests/all/import_indexes.rs @@ -15,7 +15,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> { "#; let store = Store::default(); - let module = Module::new(&store, WAT)?; + let module = Module::new(store.engine(), WAT)?; let imports = [ Func::new( @@ -41,7 +41,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> { ) .into(), ]; - let instance = Instance::new(&module, &imports)?; + let instance = Instance::new(&store, &module, &imports)?; let func = instance.get_func("foo").unwrap(); let results = func.call(&[])?; diff --git a/tests/all/instance.rs b/tests/all/instance.rs index c0f3db86a296..83ee52b22744 100644 --- a/tests/all/instance.rs +++ b/tests/all/instance.rs @@ -4,10 +4,10 @@ use wasmtime::*; #[test] fn wrong_import_numbers() -> Result<()> { let store = Store::default(); - let module = Module::new(&store, r#"(module (import "" "" (func)))"#)?; + let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; - assert!(Instance::new(&module, &[]).is_err()); + assert!(Instance::new(&store, &module, &[]).is_err()); let func = Func::wrap(&store, || {}); - assert!(Instance::new(&module, &[func.clone().into(), func.into()]).is_err()); + assert!(Instance::new(&store, &module, &[func.clone().into(), func.into()]).is_err()); Ok(()) } diff --git a/tests/all/invoke_func_via_table.rs b/tests/all/invoke_func_via_table.rs index 8f59fb17d8d5..a73f7c1c404d 100644 --- a/tests/all/invoke_func_via_table.rs +++ b/tests/all/invoke_func_via_table.rs @@ -13,8 +13,8 @@ fn test_invoke_func_via_table() -> Result<()> { (elem (i32.const 0) $f) ) "#; - let module = Module::new(&store, wat).context("> Error compiling module!")?; - let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?; + let module = Module::new(store.engine(), wat).context("> Error compiling module!")?; + let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?; let f = instance .get_table("table") diff --git a/tests/all/linker.rs b/tests/all/linker.rs index 958dcc8ecc02..0db288aedacc 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -5,13 +5,16 @@ use wasmtime::*; fn link_undefined() -> Result<()> { let store = Store::default(); let linker = Linker::new(&store); - let module = Module::new(&store, r#"(module (import "" "" (func)))"#)?; + let module = Module::new(store.engine(), r#"(module (import "" "" (func)))"#)?; assert!(linker.instantiate(&module).is_err()); - let module = Module::new(&store, r#"(module (import "" "" (global i32)))"#)?; + let module = Module::new(store.engine(), r#"(module (import "" "" (global i32)))"#)?; assert!(linker.instantiate(&module).is_err()); - let module = Module::new(&store, r#"(module (import "" "" (memory 1)))"#)?; + let module = Module::new(store.engine(), r#"(module (import "" "" (memory 1)))"#)?; assert!(linker.instantiate(&module).is_err()); - let module = Module::new(&store, r#"(module (import "" "" (table 1 funcref)))"#)?; + let module = Module::new( + store.engine(), + r#"(module (import "" "" (table 1 funcref)))"#, + )?; assert!(linker.instantiate(&module).is_err()); Ok(()) } @@ -71,7 +74,7 @@ fn function_interposition() -> Result<()> { let mut linker = Linker::new(&store); linker.allow_shadowing(true); let mut module = Module::new( - &store, + store.engine(), r#"(module (func (export "green") (result i32) (i32.const 7)))"#, )?; for _ in 0..4 { @@ -82,7 +85,7 @@ fn function_interposition() -> Result<()> { instance.get_export("green").unwrap().clone(), )?; module = Module::new( - &store, + store.engine(), r#"(module (import "red" "green" (func (result i32))) (func (export "green") (result i32) (i32.mul (call 0) (i32.const 2))) @@ -104,7 +107,7 @@ fn function_interposition_renamed() -> Result<()> { let mut linker = Linker::new(&store); linker.allow_shadowing(true); let mut module = Module::new( - &store, + store.engine(), r#"(module (func (export "export") (result i32) (i32.const 7)))"#, )?; for _ in 0..4 { @@ -115,7 +118,7 @@ fn function_interposition_renamed() -> Result<()> { instance.get_export("export").unwrap().clone(), )?; module = Module::new( - &store, + store.engine(), r#"(module (import "red" "green" (func (result i32))) (func (export "export") (result i32) (i32.mul (call 0) (i32.const 2))) @@ -137,14 +140,14 @@ fn module_interposition() -> Result<()> { let mut linker = Linker::new(&store); linker.allow_shadowing(true); let mut module = Module::new( - &store, + store.engine(), r#"(module (func (export "export") (result i32) (i32.const 7)))"#, )?; for _ in 0..4 { let instance = linker.instantiate(&module)?; linker.instance("instance", &instance)?; module = Module::new( - &store, + store.engine(), r#"(module (import "instance" "export" (func (result i32))) (func (export "export") (result i32) (i32.mul (call 0) (i32.const 2))) diff --git a/tests/all/memory_creator.rs b/tests/all/memory_creator.rs index 013b68b02f1e..9fa8a647ba5d 100644 --- a/tests/all/memory_creator.rs +++ b/tests/all/memory_creator.rs @@ -143,14 +143,14 @@ mod not_for_windows { fn host_memory() -> anyhow::Result<()> { let (store, mem_creator) = config(); let module = Module::new( - &store, + store.engine(), r#" (module (memory (export "memory") 1) ) "#, )?; - Instance::new(&module, &[])?; + Instance::new(&store, &module, &[])?; assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 1); @@ -161,7 +161,7 @@ mod not_for_windows { fn host_memory_grow() -> anyhow::Result<()> { let (store, mem_creator) = config(); let module = Module::new( - &store, + store.engine(), r#" (module (func $f (drop (memory.grow (i32.const 1)))) @@ -171,8 +171,8 @@ mod not_for_windows { "#, )?; - let instance1 = Instance::new(&module, &[])?; - let instance2 = Instance::new(&module, &[])?; + let instance1 = Instance::new(&store, &module, &[])?; + let instance2 = Instance::new(&store, &module, &[])?; assert_eq!(*mem_creator.num_created_memories.lock().unwrap(), 2); diff --git a/tests/all/name.rs b/tests/all/name.rs index d6cea5a50c65..0b1c6e5e8645 100644 --- a/tests/all/name.rs +++ b/tests/all/name.rs @@ -2,14 +2,14 @@ use wasmtime::*; #[test] fn test_module_no_name() -> anyhow::Result<()> { - let store = Store::default(); + let engine = Engine::default(); let wat = r#" (module (func (export "run") (nop)) ) "#; - let module = Module::new(&store, wat)?; + let module = Module::new(&engine, wat)?; assert_eq!(module.name(), None); Ok(()) @@ -17,17 +17,17 @@ fn test_module_no_name() -> anyhow::Result<()> { #[test] fn test_module_name() -> anyhow::Result<()> { - let store = Store::default(); + let engine = Engine::default(); let wat = r#" (module $from_name_section (func (export "run") (nop)) ) "#; - let module = Module::new(&store, wat)?; + let module = Module::new(&engine, wat)?; assert_eq!(module.name(), Some("from_name_section")); - let module = Module::new_with_name(&store, wat, "override")?; + let module = Module::new_with_name(&engine, wat, "override")?; assert_eq!(module.name(), Some("override")); Ok(()) diff --git a/tests/all/stack_overflow.rs b/tests/all/stack_overflow.rs index 9d63dbdc4ca8..97e9dc1a0882 100644 --- a/tests/all/stack_overflow.rs +++ b/tests/all/stack_overflow.rs @@ -12,7 +12,7 @@ fn host_always_has_some_stack() -> anyhow::Result<()> { // Create a module that's infinitely recursive, but calls the host on each // level of wasm stack to always test how much host stack we have left. let module = Module::new( - &store, + store.engine(), r#" (module (import "" "" (func $host)) @@ -23,7 +23,7 @@ fn host_always_has_some_stack() -> anyhow::Result<()> { "#, )?; let func = Func::wrap(&store, test_host_stack); - let instance = Instance::new(&module, &[func.into()])?; + let instance = Instance::new(&store, &module, &[func.into()])?; let foo = instance.get_func("foo").unwrap().get0::<()>()?; // Make sure that our function traps and the trap says that the call stack diff --git a/tests/all/traps.rs b/tests/all/traps.rs index f6cc691a6ba0..27285b241a1d 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -12,11 +12,11 @@ fn test_trap_return() -> Result<()> { ) "#; - let module = Module::new(&store, wat)?; + let module = Module::new(store.engine(), wat)?; let hello_type = FuncType::new(Box::new([]), Box::new([])); let hello_func = Func::new(&store, hello_type, |_, _, _| Err(Trap::new("test 123"))); - let instance = Instance::new(&module, &[hello_func.into()])?; + let instance = Instance::new(&store, &module, &[hello_func.into()])?; let run_func = instance.get_func("run").expect("expected function export"); let e = run_func @@ -41,8 +41,8 @@ fn test_trap_trace() -> Result<()> { ) "#; - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), wat)?; + let instance = Instance::new(&store, &module, &[])?; let run_func = instance.get_func("run").expect("expected function export"); let e = run_func @@ -87,8 +87,8 @@ fn test_trap_trace_cb() -> Result<()> { let fn_type = FuncType::new(Box::new([]), Box::new([])); let fn_func = Func::new(&store, fn_type, |_, _, _| Err(Trap::new("cb throw"))); - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &[fn_func.into()])?; + let module = Module::new(store.engine(), wat)?; + let instance = Instance::new(&store, &module, &[fn_func.into()])?; let run_func = instance.get_func("run").expect("expected function export"); let e = run_func @@ -118,8 +118,8 @@ fn test_trap_stack_overflow() -> Result<()> { ) "#; - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), wat)?; + let instance = Instance::new(&store, &module, &[])?; let run_func = instance.get_func("run").expect("expected function export"); let e = run_func @@ -153,8 +153,8 @@ fn trap_display_pretty() -> Result<()> { ) "#; - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), wat)?; + let instance = Instance::new(&store, &module, &[])?; let run_func = instance.get_func("bar").expect("expected function export"); let e = run_func.call(&[]).err().expect("error calling function"); @@ -185,8 +185,8 @@ fn trap_display_multi_module() -> Result<()> { ) "#; - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), wat)?; + let instance = Instance::new(&store, &module, &[])?; let bar = instance.get_export("bar").unwrap(); let wat = r#" @@ -196,8 +196,8 @@ fn trap_display_multi_module() -> Result<()> { (func (export "bar2") call $middle) ) "#; - let module = Module::new(&store, wat)?; - let instance = Instance::new(&module, &[bar])?; + let module = Module::new(store.engine(), wat)?; + let instance = Instance::new(&store, &module, &[bar])?; let bar2 = instance.get_func("bar2").expect("expected function export"); let e = bar2.call(&[]).err().expect("error calling function"); @@ -230,10 +230,12 @@ fn trap_start_function_import() -> Result<()> { "#, )?; - let module = Module::new(&store, &binary)?; + let module = Module::new(store.engine(), &binary)?; let sig = FuncType::new(Box::new([]), Box::new([])); let func = Func::new(&store, sig, |_, _, _| Err(Trap::new("user trap"))); - let err = Instance::new(&module, &[func.into()]).err().unwrap(); + let err = Instance::new(&store, &module, &[func.into()]) + .err() + .unwrap(); assert!(err .downcast_ref::() .unwrap() @@ -257,10 +259,11 @@ fn rust_panic_import() -> Result<()> { "#, )?; - let module = Module::new(&store, &binary)?; + let module = Module::new(store.engine(), &binary)?; let sig = FuncType::new(Box::new([]), Box::new([])); let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic")); let instance = Instance::new( + &store, &module, &[ func.into(), @@ -299,18 +302,18 @@ fn rust_panic_start_function() -> Result<()> { "#, )?; - let module = Module::new(&store, &binary)?; + let module = Module::new(store.engine(), &binary)?; let sig = FuncType::new(Box::new([]), Box::new([])); let func = Func::new(&store, sig, |_, _, _| panic!("this is a panic")); let err = panic::catch_unwind(AssertUnwindSafe(|| { - drop(Instance::new(&module, &[func.into()])); + drop(Instance::new(&store, &module, &[func.into()])); })) .unwrap_err(); assert_eq!(err.downcast_ref::<&'static str>(), Some(&"this is a panic")); let func = Func::wrap(&store, || panic!("this is another panic")); let err = panic::catch_unwind(AssertUnwindSafe(|| { - drop(Instance::new(&module, &[func.into()])); + drop(Instance::new(&store, &module, &[func.into()])); })) .unwrap_err(); assert_eq!( @@ -332,8 +335,8 @@ fn mismatched_arguments() -> Result<()> { "#, )?; - let module = Module::new(&store, &binary)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), &binary)?; + let instance = Instance::new(&store, &module, &[])?; let func = instance.get_func("foo").unwrap(); assert_eq!( func.call(&[]).unwrap_err().to_string(), @@ -371,8 +374,8 @@ fn call_signature_mismatch() -> Result<()> { "#, )?; - let module = Module::new(&store, &binary)?; - let err = Instance::new(&module, &[]) + let module = Module::new(store.engine(), &binary)?; + let err = Instance::new(&store, &module, &[]) .err() .unwrap() .downcast::() @@ -397,8 +400,8 @@ fn start_trap_pretty() -> Result<()> { ) "#; - let module = Module::new(&store, wat)?; - let e = match Instance::new(&module, &[]) { + let module = Module::new(store.engine(), wat)?; + let e = match Instance::new(&store, &module, &[]) { Ok(_) => panic!("expected failure"), Err(e) => e.downcast::()?, }; @@ -420,8 +423,8 @@ wasm backtrace: #[test] fn present_after_module_drop() -> Result<()> { let store = Store::default(); - let module = Module::new(&store, r#"(func (export "foo") unreachable)"#)?; - let instance = Instance::new(&module, &[])?; + let module = Module::new(store.engine(), r#"(func (export "foo") unreachable)"#)?; + let instance = Instance::new(&store, &module, &[])?; let func = instance.get_func("foo").unwrap(); println!("asserting before we drop modules"); diff --git a/tests/host_segfault.rs b/tests/host_segfault.rs index 18849672873a..30f3a4f9ecea 100644 --- a/tests/host_segfault.rs +++ b/tests/host_segfault.rs @@ -48,22 +48,25 @@ fn main() { let tests: &[(&str, fn())] = &[ ("normal segfault", || segfault()), ("make instance then segfault", || { - let store = Store::default(); - let module = Module::new(&store, "(module)").unwrap(); - let _instance = Instance::new(&module, &[]).unwrap(); + let engine = Engine::default(); + let store = Store::new(&engine); + let module = Module::new(&engine, "(module)").unwrap(); + let _instance = Instance::new(&store, &module, &[]).unwrap(); segfault(); }), ("make instance then overrun the stack", || { - let store = Store::default(); - let module = Module::new(&store, "(module)").unwrap(); - let _instance = Instance::new(&module, &[]).unwrap(); + let engine = Engine::default(); + let store = Store::new(&engine); + let module = Module::new(&engine, "(module)").unwrap(); + let _instance = Instance::new(&store, &module, &[]).unwrap(); println!("stack overrun: {}", overrun_the_stack()); }), ("segfault in a host function", || { - let store = Store::default(); - let module = Module::new(&store, r#"(import "" "" (func)) (start 0)"#).unwrap(); + let engine = Engine::default(); + let store = Store::new(&engine); + let module = Module::new(&engine, r#"(import "" "" (func)) (start 0)"#).unwrap(); let segfault = Func::wrap(&store, || segfault()); - Instance::new(&module, &[segfault.into()]).unwrap(); + Instance::new(&store, &module, &[segfault.into()]).unwrap(); }), ]; match env::var(VAR_NAME) {