Refactor module instantiation in the runtime.
This commit refactors module instantiation in the runtime to allow for different instance allocation strategy implementations. It adds an `InstanceAllocator` trait with the current implementation put behind the `OnDemandInstanceAllocator` struct. The Wasmtime API has been updated to allow a `Config` to have an instance allocation strategy set which will determine how instances get allocated. This change is in preparation for an alternative *pooling* instance allocator that can reserve all needed host process address space in advance. This commit also makes changes to the `wasmtime_environ` crate to represent compiled modules in a way that reduces copying at instantiation time.
This commit is contained in:
@@ -14,6 +14,25 @@ use wasmtime_environ::settings::{self, Configurable, SetError};
|
||||
use wasmtime_environ::{isa, isa::TargetIsa, Tunables};
|
||||
use wasmtime_jit::{native, CompilationStrategy, Compiler};
|
||||
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
|
||||
use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator};
|
||||
|
||||
/// Represents the module instance allocation strategy to use.
|
||||
#[derive(Clone)]
|
||||
pub enum InstanceAllocationStrategy {
|
||||
/// The on-demand instance allocation strategy.
|
||||
///
|
||||
/// Resources related to a module instance are allocated at instantiation time and
|
||||
/// immediately deallocated when the `Store` referencing the instance is dropped.
|
||||
///
|
||||
/// This is the default allocation strategy for Wasmtime.
|
||||
OnDemand,
|
||||
}
|
||||
|
||||
impl Default for InstanceAllocationStrategy {
|
||||
fn default() -> Self {
|
||||
Self::OnDemand
|
||||
}
|
||||
}
|
||||
|
||||
/// Global configuration options used to create an [`Engine`](crate::Engine)
|
||||
/// and customize its behavior.
|
||||
@@ -29,7 +48,10 @@ pub struct Config {
|
||||
#[cfg(feature = "cache")]
|
||||
pub(crate) cache_config: CacheConfig,
|
||||
pub(crate) profiler: Arc<dyn ProfilingAgent>,
|
||||
pub(crate) memory_creator: Option<MemoryCreatorProxy>,
|
||||
pub(crate) instance_allocator: Option<Arc<dyn InstanceAllocator>>,
|
||||
// The default instance allocator is used for instantiating host objects
|
||||
// and for module instatiation when `instance_allocator` is None
|
||||
pub(crate) default_instance_allocator: OnDemandInstanceAllocator,
|
||||
pub(crate) max_wasm_stack: usize,
|
||||
pub(crate) features: WasmFeatures,
|
||||
pub(crate) wasm_backtrace_details_env_used: bool,
|
||||
@@ -73,7 +95,8 @@ impl Config {
|
||||
#[cfg(feature = "cache")]
|
||||
cache_config: CacheConfig::new_cache_disabled(),
|
||||
profiler: Arc::new(NullProfilerAgent),
|
||||
memory_creator: None,
|
||||
instance_allocator: None,
|
||||
default_instance_allocator: OnDemandInstanceAllocator::new(None),
|
||||
max_wasm_stack: 1 << 20,
|
||||
wasm_backtrace_details_env_used: false,
|
||||
features: WasmFeatures {
|
||||
@@ -504,9 +527,24 @@ impl Config {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Sets a custom memory creator
|
||||
/// Sets a custom memory creator.
|
||||
///
|
||||
/// Custom memory creators are used when creating host `Memory` objects or when
|
||||
/// creating instance linear memories for the on-demand instance allocation strategy.
|
||||
pub fn with_host_memory(&mut self, mem_creator: Arc<dyn MemoryCreator>) -> &mut Self {
|
||||
self.memory_creator = Some(MemoryCreatorProxy { mem_creator });
|
||||
self.default_instance_allocator =
|
||||
OnDemandInstanceAllocator::new(Some(Arc::new(MemoryCreatorProxy(mem_creator))));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the instance allocation strategy to use.
|
||||
pub fn with_instance_allocation_strategy(
|
||||
&mut self,
|
||||
strategy: InstanceAllocationStrategy,
|
||||
) -> &mut Self {
|
||||
self.instance_allocator = match strategy {
|
||||
InstanceAllocationStrategy::OnDemand => None,
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
@@ -728,6 +766,12 @@ impl Config {
|
||||
let isa = self.target_isa();
|
||||
Compiler::new(isa, self.strategy, self.tunables.clone(), self.features)
|
||||
}
|
||||
|
||||
pub(crate) fn instance_allocator(&self) -> &dyn InstanceAllocator {
|
||||
self.instance_allocator
|
||||
.as_deref()
|
||||
.unwrap_or(&self.default_instance_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
fn round_up_to_pages(val: u64) -> u64 {
|
||||
|
||||
@@ -12,9 +12,9 @@ use wasmtime_environ::wasm::{
|
||||
};
|
||||
use wasmtime_environ::Initializer;
|
||||
use wasmtime_runtime::{
|
||||
Imports, InstantiationError, RuntimeInstance, StackMapRegistry, VMContext,
|
||||
VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport,
|
||||
VMTableImport,
|
||||
Imports, InstanceAllocationRequest, InstantiationError, RuntimeInstance, StackMapRegistry,
|
||||
VMContext, VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport,
|
||||
VMMemoryImport, VMTableImport,
|
||||
};
|
||||
|
||||
/// An instantiated WebAssembly module.
|
||||
@@ -492,18 +492,26 @@ impl<'a> Instantiator<'a> {
|
||||
// compiled JIT code within the `Store`.
|
||||
self.store.register_module(&self.cur.module);
|
||||
|
||||
let config = self.store.engine().config();
|
||||
unsafe {
|
||||
let instance = compiled_module.instantiate(
|
||||
self.cur.build(),
|
||||
&self.store.lookup_shared_signature(self.cur.module.types()),
|
||||
config.memory_creator.as_ref().map(|a| a as _),
|
||||
self.store.interrupts(),
|
||||
Box::new(()),
|
||||
self.store.externref_activations_table() as *const VMExternRefActivationsTable
|
||||
let config = self.store.engine().config();
|
||||
|
||||
let allocator = config.instance_allocator();
|
||||
|
||||
let instance = allocator.allocate(InstanceAllocationRequest {
|
||||
module: compiled_module.module().clone(),
|
||||
finished_functions: compiled_module.finished_functions(),
|
||||
imports: self.cur.build(),
|
||||
lookup_shared_signature: &self
|
||||
.store
|
||||
.lookup_shared_signature(self.cur.module.types()),
|
||||
host_state: Box::new(()),
|
||||
interrupts: self.store.interrupts(),
|
||||
externref_activations_table: self.store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
self.store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||
)?;
|
||||
stack_map_registry: self.store.stack_map_registry() as *const StackMapRegistry
|
||||
as *mut _,
|
||||
})?;
|
||||
|
||||
// After we've created the `InstanceHandle` we still need to run
|
||||
// initialization to set up data/elements/etc. We do this after adding
|
||||
@@ -513,8 +521,9 @@ impl<'a> Instantiator<'a> {
|
||||
// tables. This means that from this point on, regardless of whether
|
||||
// initialization is successful, we need to keep the instance alive.
|
||||
let instance = self.store.add_instance(instance);
|
||||
instance
|
||||
allocator
|
||||
.initialize(
|
||||
&instance.handle,
|
||||
config.features.bulk_memory,
|
||||
&compiled_module.data_initializers(),
|
||||
)
|
||||
|
||||
@@ -18,8 +18,8 @@ use std::task::{Context, Poll};
|
||||
use wasmtime_environ::wasm;
|
||||
use wasmtime_jit::{CompiledModule, ModuleCode, TypeTables};
|
||||
use wasmtime_runtime::{
|
||||
InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMContext,
|
||||
VMExternRef, VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex,
|
||||
InstanceHandle, SignalHandler, StackMapRegistry, TrapInfo, VMContext, VMExternRef,
|
||||
VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
/// A `Store` is a collection of WebAssembly instances and host-defined items.
|
||||
@@ -254,15 +254,6 @@ impl Store {
|
||||
&self.inner.engine
|
||||
}
|
||||
|
||||
/// 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 _)
|
||||
}
|
||||
|
||||
pub(crate) fn signatures(&self) -> &RefCell<SignatureRegistry> {
|
||||
&self.inner.signatures
|
||||
}
|
||||
@@ -969,9 +960,10 @@ impl fmt::Debug for Store {
|
||||
|
||||
impl Drop for StoreInner {
|
||||
fn drop(&mut self) {
|
||||
for instance in self.instances.get_mut().iter() {
|
||||
let allocator = self.engine.config().instance_allocator();
|
||||
for instance in self.instances.borrow().iter() {
|
||||
unsafe {
|
||||
instance.dealloc();
|
||||
allocator.deallocate(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@ use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||
use wasmtime_environ::Module;
|
||||
use wasmtime_runtime::{
|
||||
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
|
||||
VMFunctionImport, VMSharedSignatureIndex,
|
||||
Imports, InstanceAllocationRequest, InstanceAllocator, StackMapRegistry,
|
||||
VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
pub(crate) fn create_handle(
|
||||
module: Module,
|
||||
store: &Store,
|
||||
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
state: Box<dyn Any>,
|
||||
host_state: Box<dyn Any>,
|
||||
func_imports: &[VMFunctionImport],
|
||||
shared_signature_id: Option<VMSharedSignatureIndex>,
|
||||
) -> Result<StoreInstanceHandle> {
|
||||
@@ -26,17 +26,24 @@ pub(crate) fn create_handle(
|
||||
let module = Arc::new(module);
|
||||
|
||||
unsafe {
|
||||
let handle = InstanceHandle::new(
|
||||
module,
|
||||
&finished_functions,
|
||||
imports,
|
||||
store.memory_creator(),
|
||||
&|_| shared_signature_id.unwrap(),
|
||||
state,
|
||||
store.interrupts(),
|
||||
store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
|
||||
store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||
)?;
|
||||
// Use the default allocator when creating handles associated with host objects
|
||||
let handle = store
|
||||
.engine()
|
||||
.config()
|
||||
.default_instance_allocator
|
||||
.allocate(InstanceAllocationRequest {
|
||||
module: module.clone(),
|
||||
finished_functions: &finished_functions,
|
||||
imports,
|
||||
lookup_shared_signature: &|_| shared_signature_id.unwrap(),
|
||||
host_state,
|
||||
interrupts: store.interrupts(),
|
||||
externref_activations_table: store.externref_activations_table()
|
||||
as *const VMExternRefActivationsTable
|
||||
as *mut _,
|
||||
stack_map_registry: store.stack_map_registry() as *const StackMapRegistry as *mut _,
|
||||
})?;
|
||||
|
||||
Ok(store.add_instance(handle))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,9 +54,7 @@ impl RuntimeLinearMemory for LinearMemoryProxy {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct MemoryCreatorProxy {
|
||||
pub(crate) mem_creator: Arc<dyn MemoryCreator>,
|
||||
}
|
||||
pub(crate) struct MemoryCreatorProxy(pub Arc<dyn MemoryCreator>);
|
||||
|
||||
impl RuntimeMemoryCreator for MemoryCreatorProxy {
|
||||
fn new_memory(&self, plan: &MemoryPlan) -> Result<Box<dyn RuntimeLinearMemory>, String> {
|
||||
@@ -65,7 +63,7 @@ impl RuntimeMemoryCreator for MemoryCreatorProxy {
|
||||
MemoryStyle::Static { bound } => Some(bound as u64 * WASM_PAGE_SIZE as u64),
|
||||
MemoryStyle::Dynamic => None,
|
||||
};
|
||||
self.mem_creator
|
||||
self.0
|
||||
.new_memory(ty, reserved_size_in_bytes, plan.offset_guard_size)
|
||||
.map(|mem| Box::new(LinearMemoryProxy { mem }) as Box<dyn RuntimeLinearMemory>)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user