peterhuene opened PR #2842 from engine-sig-registry to main:
This PR contains a number of internal refactorings that help make
Store::register_moduledo as little work as possible (registration is now only an insertion into aBTreeMap).This will improve module instantiation performance for modules containing lots of functions and instructions.
The changes are:
- Move the shared signature registry out of
Storeand intoEngine. Modules now register their signatures with the registry upon creation and store the signature index -> shared index mappings internally.- Engines now register the signatures of any host functions on
Configupon engine creation as well as store the anyfunc representations of the host functions (previously kept inStore).- Removed
StoreFrameInfoandStackMapRegistryin favor of a unifiedModuleRegistryper-store. The module registry can be queried based on program counter values to quickly locate the owning module.- With the removal of
StackMapRegistry, the runtime is now given a trait object to call back on for finding the stack map for a program counter value during GC. The lookup uses the existing data inCompiledModulewhich is already optimized for the search.This PR supersedes #2820.
peterhuene requested alexcrichton for a review on PR #2842.
peterhuene requested alexcrichton and fitzgen for a review on PR #2842.
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
FWIW I think originally I made this function (or something like it) to prevent access to all the internals of
FunctionInfo(or w/e it's called in this module) to ensure that the caller only got access to the items it needed, but if we're returning 3-element tuples we can probably go ahead and just return a reference to the struct itself
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
Having only read this far in the diff, this seems like something that should happen automatically and shouldn't need a destructor. Should this logic be behind a
debug_assertionscfg?
alexcrichton created PR Review Comment:
Since this returns
*const StackMapthis should be anunsafetrait I think?That being said though it'd be great if this could return
&StackMap, it feels sort of unnecessarily unsafe to return a*constpointer here. Since this is a purely internal trait and it's just for our own purposes, could we go ahead and update this to returning aRef<StackMap>so we can persist the borrow that's happening within wasmtime?
alexcrichton created PR Review Comment:
Could this be preceded with a debug assert that the entry is
None?
alexcrichton created PR Review Comment:
This could use
get_mutI think instead ofwrite()perhaps?
alexcrichton created PR Review Comment:
Instead of hashing
WasmFuncTypethis could lookupindexwithin thesigsmap just created I think?
alexcrichton created PR Review Comment:
I think the original intention was to avoid allocations here, but since
lookup_func_typeclones the signature this can probably just callself.ty().params().len()perhaps?(afaik the speed of this function isn't paramount)
alexcrichton created PR Review Comment:
I'm a bit wary having an unregister function like this mostly because it's related to memory safety in that it's comparing function signatures at the wasm level. This feels like manual memory management which can be error prone and difficult to get right.
Would it be possible, though, to have the two registration functions above return an owned handled which has a destructor? We could store that owned handle within
Func/HostFunc/Module/etc ideally and have the destructor automatically take care of the cleanup.(not sure if that requires bending over backwards too much)
alexcrichton created PR Review Comment:
Could this return
&instead of*constand it's coerced to something unsafe by the caller?
alexcrichton created PR Review Comment:
Since this is a one-liner now could this method get removed in favor of putting the
borrow_muton the caller?
alexcrichton created PR Review Comment:
Could this
debug_assertthat the start is the same?
alexcrichton created PR Review Comment:
This is one of the things I was a little worried about with the signatures and ownership of the returned shared indices. In the middle of this code it's not clear to me (immediately at least) how this is valid for the lifetime of a
RegisteredModule. I figured that the lifetime of indexes for a signature would be tied to aModuleitself but theModuleisn't persisted here and we just retain a signature map.This might just be that I don't have the signature management all in my head though. I don't think this is a great place for a comment per se, but I'm curious what you think about this. Also, how does this ensure that the module's signatures' registrations outlive this
RegisteredModule?
alexcrichton created PR Review Comment:
FWIW this seems like it could be prohibitively slow or a source of accidental n^2 things. I'm basically worried that this linear search could take a surprisingly long time in situations where a store has a number of modules or something like that (especially with module linking and whatnot).
I think this is only called from
Func::from_wasmtime_functionso the impact is at least somewhat isolated, but even then that's a pretty core piece of functionality that I'm not entirely sure how would be refactored.Do you have thoughts on how this might be possible to remove the loop?
alexcrichton created PR Review Comment:
I'm a little wary of this too, there's no need for the global registration to hang on to signatures, right?
alexcrichton created PR Review Comment:
(I think the way we access info has improved over time so "limiting what you can access" is much less important than I originally thought it might be)
alexcrichton created PR Review Comment:
If this function is only used for deregistration, could this perhaps return an iterator over the signature index and the reference count? That way the deregistration wouldn't have to do lookup multiple times in a row
alexcrichton created PR Review Comment:
As module construction gets a little more complicated, could this be outlined in a new wasmtime-only constructor on
Module? I'm imagining that ideally this function andModule::from_binarywould end up in the same place which si the only place that actually literally constructs aModule
alexcrichton created PR Review Comment:
(and also have a comment as to why it's an early-return here for double-registration of a module.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
I'm fine with changing
RegisteredModuleto storeModuleand just rely on that to get at the compiled module and signatures.I think the original intention was to just keep alive the bare minimum for the registry's operation, as with module linking implemented, there's a bunch of stuff stored in
Modulegeared towards instantiation that could be dropped while the module is still in use by aStore(i.e. no longer instantiatable, but an instance still exists for the module). But if it's confusing ownerships and lifetimes, perhaps it's not worth it.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Re: the question about how this ensures signatures outlive the
RegisteredModule: only that the module's shared signatures are only unregistered on drop ofModuleSharedSignaturesand theArcreferences the only owner of that. So for it to drop: the referencingModuleInnerneeds to drop and all stores where the module is registered need to drop; at that point the signatures are no longer needed.
peterhuene edited PR Review Comment.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
If the shared signature space were guaranteed contiguous for a module, we could easily keep a range map around for at least O(log(n)) lookup, but the free list maintained by the signature registry precludes that.
Let me think on this more.
fitzgen submitted PR Review.
fitzgen submitted PR Review.
fitzgen created PR Review Comment:
Do these
VMCallerCheckedAnyfuncs need to be boxed? The only reason I would assume so is if their addresses are taken and assumed stable. If that is the case, then I would expect some comments here warning readers about that.
fitzgen created PR Review Comment:
Having read a bit more, I guess the
SharedSignaturesstructure sort of is this RAII type, but for a whole collection of signatures.I do think that this function should still be marked
unsafethough, and have a comment laying out why, as discussed up above.
fitzgen created PR Review Comment:
Also, because memory safety relies on these indices being unique for a given signature's lifetime, and unregistering allows reusing the index, then this function should be marked
unsafe(and then the RAII type that Alex describes would be the thing that encapsulates this unsafety and internally has anunafeblock to make this call).
fitzgen created PR Review Comment:
One final thing: since nothing can guarantee that we aren't currently "borrowing" an index out of the
SharedSignaturesby the time we drop it and unregister its indices (because the indices aren't references and/or types with a lifetime and they are used by JIT code and such) I think that actually creating aSharedSignaturesshould beunsafeand have the contract that "by the time you drop this, you promise you aren't 'borrowing' any indices from within it anymore".
fitzgen submitted PR Review.
fitzgen created PR Review Comment:
(And if their addresses aren't being taken and assumed stable, then can we remove the boxing here?)
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Indeed, that is the intention as
Func's ABI representation is*mut VMCallerCheckedAnyfunc. Normally that's stored with the export inFunc, butHostFuncis shared across engines and thus the engine needs to be responsible for (stably) storing the anyfunc with the appropriate shared signature index for the engine.I'll update this with a comment.
alexcrichton created PR Review Comment:
Nah I think it's a good idea to not store
Modulehere so we can split it up as necessary.I think I just got confused a bit as we have so many module wrappers and module bits and pieces I couldn't keep them all straight in my head. I realize now though that the lifetime here is obvious in that this is the point a store retains a module's signatures, and the drop of these signatures is what deregisters.
I think that this is probably fine to leave as-is, I just needed to sit and think and understand more about what was going on
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
Since this is only called from
from_wasmtime_functioncould we perhaps use the entry point of the wasm function provided there for lookup? If this took aVMCallerCheckedAnyfuncinstead of aVMSharedSignatureIndexwe could do a pc lookup to find which module has that pc, then we look up in that module's trampoline map directly?
alexcrichton submitted PR Review.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
OooOoo excellent idea :) I'll make that change.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Is there a good way to do this given
Ref::filter_mapis unstable?
peterhuene edited PR Review Comment.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Ok I've reworked this such that
Engine,Module, andStoreall internally use aSignatureCollectionthat has a reference on the signature registry. This is an RAII type that will unregister the signatures on drop.This cleans up the code quite a bit and makes it less error prone. I'll push up the change soon.
peterhuene edited PR Review Comment.
peterhuene updated PR #2842 from engine-sig-registry to main.
peterhuene updated PR #2842 from engine-sig-registry to main.
alexcrichton submitted PR Review.
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
The leading
&*can be removed here I think
alexcrichton created PR Review Comment:
This seems to compile for me:
trait A: 'static {} fn foo(a: Option<*const dyn A>) {} fn bar(a: &dyn A) { foo(Some(a)); }so is this
transmuterequired? (same for the one ininstance.rs)
alexcrichton created PR Review Comment:
The leading
&*I think can be removed here
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Adding the
'staticconstraint to the trait did the trick, so I'll remove the transmute
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Thanks, forgot to update as I changed
stack_map_lookup's return type.
peterhuene updated PR #2842 from engine-sig-registry to main.
peterhuene updated PR #2842 from engine-sig-registry to main.
peterhuene updated PR #2842 from engine-sig-registry to main.
peterhuene updated PR #2842 from engine-sig-registry to main.
peterhuene requested alexcrichton for a review on PR #2842.
peterhuene updated PR #2842 from engine-sig-registry to main.
alexcrichton submitted PR Review.
peterhuene merged PR #2842.
Last updated: Dec 06 2025 at 06:05 UTC