Skip to content

Commit f8177c2

Browse files
authored
Refactor InstanceAllocator trait impl split (#11457)
Prior to this commit Wasmtime had an `InstanceAllocatorImpl` trait with a number of required methods as well as an `InstanceAllocator` trait with a number of provided impls. The `InstanceAllocator` trait is implemented for everything implementing `InstanceAllocatorImpl` to force users to be unable to override the default methods. When adding `async` support internally to Wasmtime these are going to need to be `#[async_trait]`-annotated-traits which adds a cost to `async` functions as a future needs to be heap-allocated. The goal of this commit is to make this future `async`-ification a bit more optimal. Notably the `InstanceAllocator` trait is removed and replaced with inherent methods on `impl dyn InstanceAllocatorImpl`. After that the previous `InstanceAllocatorImpl` trait was renamed to `InstanceAllocator` meaning that there's just one `InstanceAllocator` trait which has inherent methods which cannot be overridden. A consequence of this is that the inherent methods are also forced to do virtual dispatch unlike before where they would internally use monomorphization to have static dispatch. Given the complexity of instance allocation this is expected to be a negligible cost, however. The main benefit is that `allocate_module`, `allocate_tables`, and `allocate_memories` all get to be native `async` functions without the cost of `#[async_trait]`. Only allocation of a single table/memory will require an allocation of a future which in profiling helps reduce the cost of instantiation slightly. Note that `#[async_trait]` is not currently used, this commit is just preparation for its eventual use.
1 parent ee2fcb4 commit f8177c2

6 files changed

Lines changed: 51 additions & 84 deletions

File tree

crates/wasmtime/src/runtime/store.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,11 +2505,11 @@ impl Drop for StoreOpaque {
25052505

25062506
for (id, instance) in self.instances.iter_mut() {
25072507
log::trace!("store {store_id:?} is deallocating {id:?}");
2508-
if let StoreInstanceKind::Dummy = instance.kind {
2509-
ondemand.deallocate_module(&mut instance.handle);
2510-
} else {
2511-
allocator.deallocate_module(&mut instance.handle);
2512-
}
2508+
let allocator = match instance.kind {
2509+
StoreInstanceKind::Dummy => &ondemand,
2510+
_ => allocator,
2511+
};
2512+
allocator.deallocate_module(&mut instance.handle);
25132513
}
25142514

25152515
#[cfg(feature = "component-model")]

crates/wasmtime/src/runtime/trampoline/memory.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use crate::memory::{LinearMemory, MemoryCreator};
33
use crate::prelude::*;
44
use crate::runtime::vm::mpk::ProtectionKey;
55
use crate::runtime::vm::{
6-
CompiledModuleId, InstanceAllocationRequest, InstanceAllocatorImpl, Memory,
7-
MemoryAllocationIndex, MemoryBase, ModuleRuntimeInfo, OnDemandInstanceAllocator,
8-
RuntimeLinearMemory, RuntimeMemoryCreator, SharedMemory, Table, TableAllocationIndex,
6+
CompiledModuleId, InstanceAllocationRequest, InstanceAllocator, Memory, MemoryAllocationIndex,
7+
MemoryBase, ModuleRuntimeInfo, OnDemandInstanceAllocator, RuntimeLinearMemory,
8+
RuntimeMemoryCreator, SharedMemory, Table, TableAllocationIndex,
99
};
1010
use crate::store::{AllocateInstanceKind, InstanceId, StoreOpaque};
1111
use alloc::sync::Arc;
@@ -122,9 +122,9 @@ struct SingleMemoryInstance<'a> {
122122
ondemand: OnDemandInstanceAllocator,
123123
}
124124

125-
unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> {
125+
unsafe impl InstanceAllocator for SingleMemoryInstance<'_> {
126126
#[cfg(feature = "component-model")]
127-
fn validate_component_impl<'a>(
127+
fn validate_component<'a>(
128128
&self,
129129
_component: &Component,
130130
_offsets: &VMComponentOffsets<HostPtr>,
@@ -133,18 +133,18 @@ unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> {
133133
unreachable!("`SingleMemoryInstance` allocator never used with components")
134134
}
135135

136-
fn validate_module_impl(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> {
136+
fn validate_module(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> {
137137
anyhow::ensure!(
138138
module.memories.len() == 1,
139139
"`SingleMemoryInstance` allocator can only be used for modules with a single memory"
140140
);
141-
self.ondemand.validate_module_impl(module, offsets)?;
141+
self.ondemand.validate_module(module, offsets)?;
142142
Ok(())
143143
}
144144

145145
#[cfg(feature = "gc")]
146-
fn validate_memory_impl(&self, memory: &wasmtime_environ::Memory) -> Result<()> {
147-
self.ondemand.validate_memory_impl(memory)
146+
fn validate_memory(&self, memory: &wasmtime_environ::Memory) -> Result<()> {
147+
self.ondemand.validate_memory(memory)
148148
}
149149

150150
#[cfg(feature = "component-model")]
@@ -172,11 +172,10 @@ unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> {
172172
tunables: &Tunables,
173173
memory_index: Option<DefinedMemoryIndex>,
174174
) -> Result<(MemoryAllocationIndex, Memory)> {
175-
#[cfg(debug_assertions)]
176-
{
175+
if cfg!(debug_assertions) {
177176
let module = request.runtime_info.env_module();
178177
let offsets = request.runtime_info.offsets();
179-
self.validate_module_impl(module, offsets)
178+
self.validate_module(module, offsets)
180179
.expect("should have already validated the module before allocating memory");
181180
}
182181

crates/wasmtime/src/runtime/vm.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ pub use crate::runtime::vm::export::*;
9898
pub use crate::runtime::vm::gc::*;
9999
pub use crate::runtime::vm::imports::Imports;
100100
pub use crate::runtime::vm::instance::{
101-
GcHeapAllocationIndex, Instance, InstanceAllocationRequest, InstanceAllocator,
102-
InstanceAllocatorImpl, InstanceHandle, MemoryAllocationIndex, OnDemandInstanceAllocator,
103-
StorePtr, TableAllocationIndex, initialize_instance,
101+
GcHeapAllocationIndex, Instance, InstanceAllocationRequest, InstanceAllocator, InstanceHandle,
102+
MemoryAllocationIndex, OnDemandInstanceAllocator, StorePtr, TableAllocationIndex,
103+
initialize_instance,
104104
};
105105
#[cfg(feature = "pooling-allocator")]
106106
pub use crate::runtime::vm::instance::{

crates/wasmtime/src/runtime/vm/instance/allocator.rs

Lines changed: 21 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -197,23 +197,23 @@ impl GcHeapAllocationIndex {
197197
///
198198
/// This trait is unsafe as it requires knowledge of Wasmtime's runtime
199199
/// internals to implement correctly.
200-
pub unsafe trait InstanceAllocatorImpl {
200+
pub unsafe trait InstanceAllocator: Send + Sync {
201201
/// Validate whether a component (including all of its contained core
202202
/// modules) is allocatable by this instance allocator.
203203
#[cfg(feature = "component-model")]
204-
fn validate_component_impl<'a>(
204+
fn validate_component<'a>(
205205
&self,
206206
component: &Component,
207207
offsets: &VMComponentOffsets<HostPtr>,
208208
get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module,
209209
) -> Result<()>;
210210

211211
/// Validate whether a module is allocatable by this instance allocator.
212-
fn validate_module_impl(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()>;
212+
fn validate_module(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()>;
213213

214214
/// Validate whether a memory is allocatable by this instance allocator.
215215
#[cfg(feature = "gc")]
216-
fn validate_memory_impl(&self, memory: &wasmtime_environ::Memory) -> Result<()>;
216+
fn validate_memory(&self, memory: &wasmtime_environ::Memory) -> Result<()>;
217217

218218
/// Increment the count of concurrent component instances that are currently
219219
/// allocated, if applicable.
@@ -230,7 +230,7 @@ pub unsafe trait InstanceAllocatorImpl {
230230
// associated types are not object safe.
231231
//
232232
// 2. We would want a parameterized `Drop` implementation so that we could
233-
// pass in the `InstanceAllocatorImpl` on drop, but this doesn't exist in
233+
// pass in the `InstanceAllocator` on drop, but this doesn't exist in
234234
// Rust. Therefore, we would be forced to add reference counting and
235235
// stuff like that to keep a handle on the instance allocator from this
236236
// theoretical type. That's a bummer.
@@ -359,36 +359,7 @@ pub unsafe trait InstanceAllocatorImpl {
359359
fn allow_all_pkeys(&self);
360360
}
361361

362-
/// A thing that can allocate instances.
363-
///
364-
/// Don't implement this trait directly, instead implement
365-
/// `InstanceAllocatorImpl` and you'll get this trait for free via a blanket
366-
/// impl.
367-
pub trait InstanceAllocator: InstanceAllocatorImpl {
368-
/// Validate whether a component (including all of its contained core
369-
/// modules) is allocatable with this instance allocator.
370-
#[cfg(feature = "component-model")]
371-
fn validate_component<'a>(
372-
&self,
373-
component: &Component,
374-
offsets: &VMComponentOffsets<HostPtr>,
375-
get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module,
376-
) -> Result<()> {
377-
InstanceAllocatorImpl::validate_component_impl(self, component, offsets, get_module)
378-
}
379-
380-
/// Validate whether a core module is allocatable with this instance
381-
/// allocator.
382-
fn validate_module(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> {
383-
InstanceAllocatorImpl::validate_module_impl(self, module, offsets)
384-
}
385-
386-
/// Validate whether a memory is allocatable with this instance allocator.
387-
#[cfg(feature = "gc")]
388-
fn validate_memory(&self, memory: &wasmtime_environ::Memory) -> Result<()> {
389-
InstanceAllocatorImpl::validate_memory_impl(self, memory)
390-
}
391-
362+
impl dyn InstanceAllocator + '_ {
392363
/// Allocates a fresh `InstanceHandle` for the `req` given.
393364
///
394365
/// This will allocate memories and tables internally from this allocator
@@ -402,15 +373,16 @@ pub trait InstanceAllocator: InstanceAllocatorImpl {
402373
///
403374
/// The `request` provided must be valid, e.g. the imports within are
404375
/// correctly sized/typed for the instance being created.
405-
unsafe fn allocate_module(
376+
pub(crate) unsafe fn allocate_module(
406377
&self,
407378
mut request: InstanceAllocationRequest,
408379
) -> Result<InstanceHandle> {
409380
let module = request.runtime_info.env_module();
410381

411-
#[cfg(debug_assertions)]
412-
InstanceAllocatorImpl::validate_module_impl(self, module, request.runtime_info.offsets())
413-
.expect("module should have already been validated before allocation");
382+
if cfg!(debug_assertions) {
383+
InstanceAllocator::validate_module(self, module, request.runtime_info.offsets())
384+
.expect("module should have already been validated before allocation");
385+
}
414386

415387
self.increment_core_instance_count()?;
416388

@@ -449,7 +421,7 @@ pub trait InstanceAllocator: InstanceAllocatorImpl {
449421
/// # Unsafety
450422
///
451423
/// The instance must have previously been allocated by `Self::allocate`.
452-
unsafe fn deallocate_module(&self, handle: &mut InstanceHandle) {
424+
pub(crate) unsafe fn deallocate_module(&self, handle: &mut InstanceHandle) {
453425
// SAFETY: the contract of `deallocate_*` is itself a contract of this
454426
// function, that the memories/tables were previously allocated from
455427
// here.
@@ -470,9 +442,10 @@ pub trait InstanceAllocator: InstanceAllocatorImpl {
470442
) -> Result<()> {
471443
let module = request.runtime_info.env_module();
472444

473-
#[cfg(debug_assertions)]
474-
InstanceAllocatorImpl::validate_module_impl(self, module, request.runtime_info.offsets())
475-
.expect("module should have already been validated before allocation");
445+
if cfg!(debug_assertions) {
446+
InstanceAllocator::validate_module(self, module, request.runtime_info.offsets())
447+
.expect("module should have already been validated before allocation");
448+
}
476449

477450
for (memory_index, ty) in module.memories.iter().skip(module.num_imported_memories) {
478451
let memory_index = module
@@ -520,9 +493,10 @@ pub trait InstanceAllocator: InstanceAllocatorImpl {
520493
) -> Result<()> {
521494
let module = request.runtime_info.env_module();
522495

523-
#[cfg(debug_assertions)]
524-
InstanceAllocatorImpl::validate_module_impl(self, module, request.runtime_info.offsets())
525-
.expect("module should have already been validated before allocation");
496+
if cfg!(debug_assertions) {
497+
InstanceAllocator::validate_module(self, module, request.runtime_info.offsets())
498+
.expect("module should have already been validated before allocation");
499+
}
526500

527501
for (index, table) in module.tables.iter().skip(module.num_imported_tables) {
528502
let def_index = module
@@ -556,11 +530,6 @@ pub trait InstanceAllocator: InstanceAllocatorImpl {
556530
}
557531
}
558532

559-
// Every `InstanceAllocatorImpl` is an `InstanceAllocator` when used
560-
// correctly. Also, no one is allowed to override this trait's methods, they
561-
// must use the defaults. This blanket impl provides both of those things.
562-
impl<T: InstanceAllocatorImpl> InstanceAllocator for T {}
563-
564533
fn check_table_init_bounds(
565534
store: &mut StoreOpaque,
566535
instance: InstanceId,
@@ -894,7 +863,6 @@ mod tests {
894863

895864
#[test]
896865
fn allocator_traits_are_object_safe() {
897-
fn _instance_allocator(_: &dyn InstanceAllocatorImpl) {}
898-
fn _instance_allocator_ext(_: &dyn InstanceAllocator) {}
866+
fn _instance_allocator(_: &dyn InstanceAllocator) {}
899867
}
900868
}

crates/wasmtime/src/runtime/vm/instance/allocator/on_demand.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{
2-
InstanceAllocationRequest, InstanceAllocatorImpl, MemoryAllocationIndex, TableAllocationIndex,
2+
InstanceAllocationRequest, InstanceAllocator, MemoryAllocationIndex, TableAllocationIndex,
33
};
44
use crate::prelude::*;
55
use crate::runtime::vm::CompiledModuleId;
@@ -76,9 +76,9 @@ impl Default for OnDemandInstanceAllocator {
7676
}
7777
}
7878

79-
unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator {
79+
unsafe impl InstanceAllocator for OnDemandInstanceAllocator {
8080
#[cfg(feature = "component-model")]
81-
fn validate_component_impl<'a>(
81+
fn validate_component<'a>(
8282
&self,
8383
_component: &Component,
8484
_offsets: &VMComponentOffsets<HostPtr>,
@@ -87,12 +87,12 @@ unsafe impl InstanceAllocatorImpl for OnDemandInstanceAllocator {
8787
Ok(())
8888
}
8989

90-
fn validate_module_impl(&self, _module: &Module, _offsets: &VMOffsets<HostPtr>) -> Result<()> {
90+
fn validate_module(&self, _module: &Module, _offsets: &VMOffsets<HostPtr>) -> Result<()> {
9191
Ok(())
9292
}
9393

9494
#[cfg(feature = "gc")]
95-
fn validate_memory_impl(&self, _memory: &wasmtime_environ::Memory) -> Result<()> {
95+
fn validate_memory(&self, _memory: &wasmtime_environ::Memory) -> Result<()> {
9696
Ok(())
9797
}
9898

crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use self::decommit_queue::DecommitQueue;
4444
use self::memory_pool::MemoryPool;
4545
use self::table_pool::TablePool;
4646
use super::{
47-
InstanceAllocationRequest, InstanceAllocatorImpl, MemoryAllocationIndex, TableAllocationIndex,
47+
InstanceAllocationRequest, InstanceAllocator, MemoryAllocationIndex, TableAllocationIndex,
4848
};
4949
use crate::Enabled;
5050
use crate::prelude::*;
@@ -523,9 +523,9 @@ impl PoolingInstanceAllocator {
523523
}
524524
}
525525

526-
unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator {
526+
unsafe impl InstanceAllocator for PoolingInstanceAllocator {
527527
#[cfg(feature = "component-model")]
528-
fn validate_component_impl<'a>(
528+
fn validate_component<'a>(
529529
&self,
530530
component: &Component,
531531
offsets: &VMComponentOffsets<HostPtr>,
@@ -549,7 +549,7 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator {
549549
InstantiateModule(InstantiateModule::Static(static_module_index, _)) => {
550550
let module = get_module(*static_module_index);
551551
let offsets = VMOffsets::new(HostPtr, &module);
552-
self.validate_module_impl(module, &offsets)?;
552+
self.validate_module(module, &offsets)?;
553553
num_core_instances += 1;
554554
num_memories += module.num_defined_memories();
555555
num_tables += module.num_defined_tables();
@@ -593,7 +593,7 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator {
593593
Ok(())
594594
}
595595

596-
fn validate_module_impl(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> {
596+
fn validate_module(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> {
597597
self.validate_memory_plans(module)
598598
.context("module memory does not fit in pooling allocator requirements")?;
599599
self.validate_table_plans(module)
@@ -604,7 +604,7 @@ unsafe impl InstanceAllocatorImpl for PoolingInstanceAllocator {
604604
}
605605

606606
#[cfg(feature = "gc")]
607-
fn validate_memory_impl(&self, memory: &wasmtime_environ::Memory) -> Result<()> {
607+
fn validate_memory(&self, memory: &wasmtime_environ::Memory) -> Result<()> {
608608
self.memories.validate_memory(memory)
609609
}
610610

0 commit comments

Comments
 (0)