Disconnects Store state fields from Compiler (#1761)

*  Moves CodeMemory, VMInterrupts and SignatureRegistry from Compiler
*  CompiledModule holds CodeMemory and GdbJitImageRegistration
*  Store keeps track of its JIT code
*  Makes "jit_int.rs" stuff Send+Sync
*  Adds the threads example.
This commit is contained in:
Yury Delendik
2020-06-02 13:44:39 -05:00
committed by GitHub
parent b41330393d
commit 15c68f2cc1
61 changed files with 982 additions and 663 deletions

View File

@@ -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,

View File

@@ -36,19 +36,10 @@ pub unsafe extern "C" fn wasm_instance_new(
imports: *const Box<wasm_extern_t>,
result: Option<&mut *mut wasm_trap_t>,
) -> Option<Box<wasm_instance_t>> {
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<wasm_extern_t>,
num_imports: usize,
@@ -90,6 +82,7 @@ pub unsafe extern "C" fn wasmtime_instance_new(
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
_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<wasm_extern_t>],
instance_ptr: &mut *mut wasm_instance_t,
trap_ptr: &mut *mut wasm_trap_t,
) -> Option<Box<wasmtime_error_t>> {
let store = &store.store;
let imports = imports
.iter()
.map(|import| match &import.which {
@@ -114,8 +109,8 @@ fn _wasmtime_instance_new(
.collect::<Vec<_>>();
let module = &module.module.borrow();
handle_instantiate(
module.store(),
Instance::new(module, &imports),
store,
Instance::new(store, module, &imports),
instance_ptr,
trap_ptr,
)

View File

@@ -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<Box<wasmtime_error_t>> {
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<Box<wasmtime_error_t>> {
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::<Vec<_>>();
out.set_buffer(buffer);
}
#[no_mangle]
pub extern "C" fn wasm_module_share(module: &wasm_module_t) -> Box<wasm_shared_module_t> {
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<Box<wasm_module_t>> {
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::<Vec<_>>();
let exports = module
.exports()
.map(|e| wasm_exporttype_t::new(e.name().to_owned(), e.ty()))
.collect::<Vec<_>>();
Some(Box::new(wasm_module_t {
module: HostRef::new(&store.store, module),
imports,
exports,
}))
}

View File

@@ -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);
}
}

View File

@@ -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<Item = (usize, usize)> + 'a {
self.entries[..self.published]
.iter()
.any(|entry| entry.contains(addr))
.map(|entry| entry.range())
}
}

View File

@@ -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<dyn TargetIsa>,
code_memory: CodeMemory,
signatures: SignatureRegistry,
strategy: CompilationStrategy,
cache_config: CacheConfig,
tunables: Tunables,
interrupts: Arc<VMInterrupts>,
}
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<T: Send + Sync>() {}
_assert::<Compiler>();
}
#[allow(missing_docs)]
pub struct Compilation {
pub code_memory: CodeMemory,
pub finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
pub relocations: Relocations,
pub trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
pub trampoline_relocations: HashMap<VMSharedSignatureIndex, Vec<Relocation>>,
pub trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
pub trampoline_relocations: HashMap<SignatureIndex, Vec<Relocation>>,
pub jt_offsets: PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
pub dbg_image: Option<Vec<u8>>,
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<VMInterrupts> {
&self.interrupts
}
/// Compile the given function bodies.
pub(crate) fn compile<'data>(
&mut self,
&self,
translation: &ModuleTranslation,
debug_data: Option<DebugInfoData>,
) -> Result<Compilation, SetupError> {
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::<u128>(),
)?;
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.

View File

@@ -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<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[DataInitializer<'data>]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
struct FinishedFunctions(BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>);
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<GdbJitImageRegistration>,
}
/// A compiled wasm module, ready to be instantiated.
pub struct CompiledModule {
module: Arc<Module>,
code: Arc<ModuleCode>,
finished_functions: FinishedFunctions,
trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
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<Self, SetupError> {
@@ -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::<PrimaryMap<_, _>>()
};
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::<Vec<_>>()
.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<Module>,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
traps: Traps,
address_transform: ModuleAddressMap,
interrupts: Arc<VMInterrupts>,
}
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<Self, SetupError> {
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::<Vec<_>>()
.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<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>,
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<GdbJitImageRegistration>,
traps: Traps,
address_transform: ModuleAddressMap,
interrupts: Arc<VMInterrupts>,
) -> 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<VMInterrupts>,
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
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::<PrimaryMap<_, _>>()
};
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<Module> {
&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<DefinedFuncIndex, *mut [VMFunctionBody]> {
&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<Item = (usize, usize)> + 'a {
self.code.code_memory.published_ranges()
}
/// Returns module's JIT code.
pub fn code(&self) -> &Arc<ModuleCode> {
&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(),

View File

@@ -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);
}
}

View File

@@ -57,7 +57,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
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<Extern> = Vec::new();
if let Some(module_name) = data.find_wasi_module_name() {
@@ -75,7 +75,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
}
}
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 })
}

View File

@@ -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"] }

View File

@@ -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<Module>,
/// The module's JIT code (if exists).
code: Arc<dyn Any>,
/// 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<dyn Any>,
/// Optional image of JIT'ed code for debugger registration.
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
/// Externally allocated data indicating how this instance will be
/// interrupted.
pub(crate) interrupts: Arc<VMInterrupts>,
@@ -96,14 +94,10 @@ impl Instance {
unsafe { *self.signature_ids_ptr().add(index) }
}
pub(crate) fn module(&self) -> &Arc<Module> {
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<Module>,
code: Arc<dyn Any>,
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
imports: Imports,
mem_creator: Option<&dyn RuntimeMemoryCreator>,
vmshared_signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
host_state: Box<dyn Any>,
interrupts: Arc<VMInterrupts>,
) -> Result<Self, InstantiationError> {
@@ -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<Module> {
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<DefinedGlobalIndex, VMGlobalDef
}
fn initialize_globals(instance: &Instance) {
let module = Arc::clone(&instance.module);
let module = instance.module();
let num_imports = module.local.num_imported_globals;
for (index, global) in module.local.globals.iter().skip(num_imports) {
let def_index = module.local.defined_global_index(index).unwrap();

View File

@@ -2,7 +2,10 @@
//! the __jit_debug_register_code() and __jit_debug_descriptor to register
//! or unregister generated object images with debuggers.
use lazy_static::lazy_static;
use std::pin::Pin;
use std::ptr;
use std::sync::Mutex;
#[repr(C)]
struct JITCodeEntry {
@@ -43,19 +46,41 @@ extern "C" fn __jit_debug_register_code() {
}
}
lazy_static! {
// The process controls access to the __jit_debug_descriptor by itself --
// the GDB/LLDB accesses this structure and its data at the process startup
// and when paused in __jit_debug_register_code.
//
// The GDB_REGISTRATION lock is needed for GdbJitImageRegistration to protect
// access to the __jit_debug_descriptor within this process.
pub static ref GDB_REGISTRATION: Mutex<()> = Mutex::new(Default::default());
}
/// Registeration for JIT image
pub struct GdbJitImageRegistration {
entry: *mut JITCodeEntry,
file: Vec<u8>,
entry: Pin<Box<JITCodeEntry>>,
file: Pin<Box<[u8]>>,
}
impl GdbJitImageRegistration {
/// Registers JIT image using __jit_debug_register_code
pub fn register(file: Vec<u8>) -> 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);
}

View File

@@ -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<Inner>,
}
#[derive(Debug, Default)]
struct Inner {
pub struct SignatureRegistry {
wasm2index: HashMap<WasmFuncType, VMSharedSignatureIndex>,
// 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<ir::Signature> {
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<WasmFuncType> {
self.inner.read().unwrap().index2wasm.get(&idx).cloned()
self.index2wasm.get(&idx).cloned()
}
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -42,6 +42,8 @@ struct ModuleFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<Module>,
#[allow(dead_code)]
module_code: Arc<dyn std::any::Any + Send + Sync>,
}
struct FunctionInfo {
@@ -192,6 +194,7 @@ pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration>
start: min,
functions,
module: module.module().clone(),
module_code: module.code().clone(),
},
);
assert!(prev.is_none());

View File

@@ -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::<i32>()?;
///
@@ -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::<i32, i32, i32>()?;
/// 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::<i32, i32, i32>()?;
/// 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,
)

View File

@@ -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<dyn Any>,
) -> Result<StoreInstanceHandle, Error> {
// 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<Instance, Error> {
let store = module.store();
pub fn new(store: &Store, module: &Module, imports: &[Extern]) -> Result<Instance, Error> {
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`].

View File

@@ -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::<i32>()?()?;
/// 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<Instance> {
let imports = self.compute_imports(module)?;
Instance::new(module, &imports)
Instance::new(&self.store, module, &imports)
}
fn compute_imports(&self, module: &Module) -> Result<Vec<Extern>> {

View File

@@ -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<u8> = 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<ModuleInner>,
}
struct ModuleInner {
store: Store,
compiled: CompiledModule,
frame_info_registration: Mutex<Option<Option<Arc<GlobalFrameInfoRegistration>>>>,
engine: Engine,
compiled: Arc<CompiledModule>,
frame_info_registration: Arc<Mutex<Option<Option<Arc<GlobalFrameInfoRegistration>>>>>,
}
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<u8> = 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<Module> {
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
#[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<Module> {
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<Module> {
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<Path>) -> Result<Module> {
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Module> {
#[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> {
Module::validate(store, binary)?;
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
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> {
Module::compile(store, binary)
pub unsafe fn from_binary_unchecked(engine: &Engine, binary: &[u8]) -> Result<Module> {
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<Self> {
let compiled = CompiledModule::new(
&mut store.compiler_mut(),
binary,
&*store.engine().config().profiler,
)?;
unsafe fn compile(engine: &Engine, binary: &[u8]) -> Result<Self> {
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<Item = ImportType<'module>> + '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<Item = ExportType<'module>> + '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<ExternType> {
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<Arc<GlobalFrameInfoRegistration>> {
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<T: Send + Sync>() {}
_assert::<Module>();
}

View File

@@ -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<Config>,
inner: Arc<EngineInner>,
}
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<Compiler>,
interrupts: Arc<VMInterrupts>,
signatures: RefCell<SignatureRegistry>,
instances: RefCell<Vec<InstanceHandle>>,
signal_handler: RefCell<Option<Box<SignalHandler<'static>>>>,
jit_code_ranges: RefCell<Vec<(usize, usize)>>,
host_info: RefCell<HashMap<HostInfoKey, Rc<RefCell<dyn Any>>>>,
}
@@ -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 compiler(&self) -> std::cell::Ref<'_, Compiler> {
self.inner.compiler.borrow()
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_mut(&self) -> std::cell::RefMut<'_, Compiler> {
self.inner.compiler.borrow_mut()
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 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<Item = (usize, usize)>) {
// 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<VMInterrupts> {
&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<InterruptHandle> {
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(())

View File

@@ -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::<PrimaryMap<_, _>>();
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))
}

View File

@@ -247,10 +247,7 @@ pub fn create_handle_with_function(
mem::size_of::<u128>(),
)?;
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)

View File

@@ -84,7 +84,7 @@ impl WastContext {
}
fn instantiate(&mut self, module: &[u8]) -> Result<Outcome<Instance>> {
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,