This adds a tier-up mechanism at loop edges, making it so we can
seamlessly (ish) transition between interpreted and native code so we
can start running wasm code immediately after validation while
compilation happens in the background, and switching to native code
eventually once we hit a big enough function that would benefit from
being compiled to begin with.
The background Cranelift pass can race with the first call into a
module. In that case ensure_cranelift_compiled() waited by repeatedly
polling the module state and burning CPU until the compiler thread
finished.
Keep the atomic state for the completed fast path, but pair the
compiling state with a condition variable. Completion now broadcasts
while holding the associated mutex, so waiting callers sleep and cannot
miss the transition to finished.
Return from the jsapi calls when we have a module that satisfies the
state required by the spec, and let jit compilation (if it's happening)
continue in the background.
This also means we no longer do the full compilation pipeline for
validate().
TryTableArgs only needs catches for try_table instructions. Reuse the
structured-instruction layout with catch storage in place of the else
target, and keep the catch elements in a FixedArray.
This avoids storing a full Vector inline in every Instruction variant
alternative and keeps the catch storage copyable for the variant.
ValueType only needs a TypeIndex payload for TypeUseReference values.
Store that directly instead of using a Variant with an Empty payload,
shrinking ValueType from 12 bytes to 8 bytes.
Keep synthetic instruction pointers stable by storing them in fixed
chunks instead of one large Vector. This removes the old oversized
reserve. Expressions without synthetic instructions now avoid extra
instruction storage entirely.
Validation always fills structured instruction metadata before compiled
expressions are executed. Store the metadata directly instead of using
Optional, and update execution and printing paths accordingly.
Specialize Optional for InstructionPointer and Instruction. Use each
type's maximum value to represent the empty state.
This keeps each optional as small as the wrapped value.
These are part of the typed function references proposal, which is now
widely used by toolchains like wasm-bindgen. This makes sites like
wordsalad.online load in Ladybird.
The wasm type system distinguishes nullable (ref null $t) from
non-nullable (ref $t) references. This is needed to correctly validate
and execute `call_ref` and `return_call_ref` instructions.
This adds parsing of `(ref typeidx)` and validates that `typeidx` is a
valid index. Currently, nullability of the reference is lost.
A bug causing the code below to fail parsing has been fixed.
```wat
(module
(type $T (struct (field i32) (field f32)))
(type $T1 (struct (field i32) (field f32)))
(; many more types... ;)
(type $T64 (struct (field i32) (field f32)))
(type $f (func (result (ref null $T64))))
)
```
The spec tests type-equivalence.{0,1,3,13} have been disabled as they
were previously false positives.
This patch adds support for parsing structs in the type section.
It also removes the assumption that all types in the type section are
function types, adding appropriate validation.
Spec tests struct.3 and struct.4 have been disable as this would
require expanding `ValueType` to include more heap-types.
Instead of trying to indirectly load 2x64 bits from *cc, load addresses
directly from their own contiguous allocation.
This allows a future optimisation where we defer loading addresses to
reduce memory port pressure.
This first pass only applies to the following two cases:
- Public functions returning a view type into an object they own
- Public ctors storing a view type
This catches a grand total of one (1) issue, which is fixed in
the previous commit.
This, along with moving the sources and destination out of the config
object, makes it so we don't have to double-deref to get to them on each
instruction, leading to a ~15% perf improvement on dispatch.
Namely, find an upper bound at validation time so we can allocate the
space when entering the frame.
Also drop labels at once instead of popping them off one at a time now
that we're using a Vector.
This commit adds a register allocator, with 8 available "register"
slots.
In testing with various random blobs, this moves anywhere from 30% to
74% of value accesses into predefined slots, and is about a ~20% perf
increase end-to-end.
To actually make this usable, a few structural changes were also made:
- we no longer do one instruction per interpret call
- trapping is an (unlikely) exit condition
- the label and frame stacks are replaced with linked lists with a huge
node cache size, as we only need to touch the last element and
push/pop is very frequent.