Skip to content

Move most module initialization to compiled code #13487

Merged
alexcrichton merged 4 commits into
bytecodealliance:mainfrom
alexcrichton:jit-module-init
May 28, 2026
Merged

Move most module initialization to compiled code #13487
alexcrichton merged 4 commits into
bytecodealliance:mainfrom
alexcrichton:jit-module-init

Conversation

@alexcrichton
Copy link
Copy Markdown
Member

This commit is a large refactoring of how modules are initialized in
Wasmtime. Notably all of the work done post-allocation, but pre-start,
is now done in compiled code instead. This means that global
initialization, active table segments, passive segment evaluation, etc,
now all happens in compiled code. The primary motivation for this is to
resolve some GC-related fuzz-bugs where initialization on the host is
taking an excessively long time. A secondary motivation is to apply fuel
metering and epoch yielding to these constructs in the same manner that
normal wasm code has these applied.

Much refactoring was needed in this commit to achieve this goal. Many
primitives were transitioned from runtime state to exclusively
compile-time state for example. Infrastructure was additionally added
for a new kind of FuncKey corresponding to this one-off-use startup
function. Overall though the net effect of this change is to mostly
delete code since so much of the runtime is now no longer necessary. An
example of this is that const-eval is now completely removed from the
runtime as the fully-general const-evaluation now happens exclusively
through compiled code.

Special care was needed here for the static table and memory
initialization that Wasmtime performs. For example there's a small dance
between compile-time and run-time where at compile-time we don't know if
static data segments should be applied, and it's only at run-time where
we know if CoW is in effect. Additionally care was taken throughout this
refactoring to avoid generating this new startup function unless it's
necessary. It's hypothesized that skipping this function is going to be
a worthwhile optimization, which means that one mode of startup is
configured as "only necessary if memories say needs_init()". This is a
bit tricky to document and it's a bit non-standard, but it should get
the job done (and existing tests exercise this already).

Comment thread crates/wasmtime/src/compile.rs Outdated
@alexcrichton alexcrichton marked this pull request as ready for review May 26, 2026 22:13
@alexcrichton alexcrichton requested review from a team as code owners May 26, 2026 22:13
@alexcrichton alexcrichton requested review from cfallin and removed request for a team May 26, 2026 22:13
@github-actions github-actions Bot added wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime:ref-types Issues related to reference types and GC in Wasmtime labels May 27, 2026
@github-actions
Copy link
Copy Markdown

Subscribe to Label Action

cc @fitzgen

Details This issue or pull request has been labeled: "wasmtime:api", "wasmtime:ref-types"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: wasmtime:ref-types

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

Copy link
Copy Markdown
Member

@cfallin cfallin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks largely good to me -- nice simplification! Some thoughts below but no blockers at all.

Comment thread crates/cranelift/src/compiler.rs Outdated
Comment thread crates/cranelift/src/compiler.rs Outdated
Comment thread crates/cranelift/src/func_environ.rs Outdated
.module
.global_initializers
.iter()
.find(|(def_index, _)| *def_index == index)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to sort global_initializers by def_index and do a binary search here? I know it's in the compilation path but I always have a vague worry when I see a linear search like this...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! It's already amortized a bit where I think make_global happens at most once-per-global-per-function, but the array is also already sorted so I added a binary search.

expr: &ConstExpr,
) -> WasmResult<ir::Value> {
let mut stack = Vec::new();
for op in expr.ops() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little unfortunate that we have duplication between this logic and the same opcodes compiled in ordinary Wasm function bodies. I guess the compile-time state is different enough that we can't "just impl From<ConstOp> for Op and use the existing lowerings" but I wonder how far off we are from that. In any case, anything complicated below (e.g. struct.new) is delegated to a translate helper so maybe this is fine.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point yeah, and looking into this I think the main blocker is the need for a FuncValidator<_> when translating an operator. That's not easily constructible for const-exprs currently from wasmparser's perspective. I'll file an issue on this though because it's a nice avenue for code deduplication (e.g. around fuel handling)

Copy link
Copy Markdown
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to add on top of Chris's comments.

Instead return `None`. This helps prevent corruption of the GC heap from
causing panics.
Even `ref.null func` is intern'd, so nothing is candidate for memset on
a `funcref` array. Overall just ban memset on all reference-typed arrays
for now to optimize the easy case of scalar data, but leave optimizing
reference types for later.
This commit is a large refactoring of how modules are initialized in
Wasmtime. Notably all of the work done post-allocation, but pre-start,
is now done in compiled code instead. This means that global
initialization, active table segments, passive segment evaluation, etc,
now all happens in compiled code. The primary motivation for this is to
resolve some GC-related fuzz-bugs where initialization on the host is
taking an excessively long time. A secondary motivation is to apply fuel
metering and epoch yielding to these constructs in the same manner that
normal wasm code has these applied.

Much refactoring was needed in this commit to achieve this goal. Many
primitives were transitioned from runtime state to exclusively
compile-time state for example. Infrastructure was additionally added
for a new kind of `FuncKey` corresponding to this one-off-use startup
function. Overall though the net effect of this change is to mostly
delete code since so much of the runtime is now no longer necessary. An
example of this is that const-eval is now completely removed from the
runtime as the fully-general const-evaluation now happens exclusively
through compiled code.

Special care was needed here for the static table and memory
initialization that Wasmtime performs. For example there's a small dance
between compile-time and run-time where at compile-time we don't know if
static data segments should be applied, and it's only at run-time where
we know if CoW is in effect. Additionally care was taken throughout this
refactoring to avoid generating this new startup function unless it's
necessary. It's hypothesized that skipping this function is going to be
a worthwhile optimization, which means that one mode of startup is
configured as "only necessary if memories say `needs_init()`". This is a
bit tricky to document and it's a bit non-standard, but it should get
the job done (and existing tests exercise this already).
@alexcrichton alexcrichton enabled auto-merge May 28, 2026 01:25
@alexcrichton alexcrichton added this pull request to the merge queue May 28, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 28, 2026
@alexcrichton alexcrichton added this pull request to the merge queue May 28, 2026
Merged via the queue into bytecodealliance:main with commit 5f3b67e May 28, 2026
51 checks passed
@alexcrichton alexcrichton deleted the jit-module-init branch May 28, 2026 02:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime:ref-types Issues related to reference types and GC in Wasmtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants