Reserve the wasm32 virtual address space when creating an i32 memory.
Crash if the reservation fails instead of using ByteBuffer storage.
This keeps wasm32 memory on the virtual path used for fault recovery.
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.
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
The throw and try_table validators looked up the tag referenced by the
instruction, then indexed m_context.types with the tag's type index
without checking it was in range. validate(TagIndex) only validates the
tag index itself, and the tag section is validated after the code
section, so a module whose tag carries an out-of-range type index
reached the unchecked m_context.types[...] access and tripped a Vector
bounds assertion during validation.
Any WebAssembly.compile() of such a module aborts the WebContent
process.
Validate the tag's type index before using it, the same check
validate(TagType) already performs.
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().
By default, `rustfmt` persists the import granularity. In practice, most
Rust code has import granularity "Module" due to LSP's actions.
"Item" gets rid of import groupings and achieves cleaner diffs and
better conflict resolution. Better greppability is a positive side
effect.
Note: it's an unstable rustfmt feature. `cargo +nightly fmt` must be
used instead of `cargo fmt`.
synthetic_br_nostack and synthetic_br_if_nostack assumed the runtime
value stack already matches the target label's expected size. The
register allocator can leave more values on the stack than the
validator's abstract count predicted, so the bare nostack variants get
us out of sync and we crash later. This makes it so they drop to regular
br/br_if behaviour if they notice that the size is not as expected.
Switches the compiled function table from HashMap<u32, ...> to a Vector
indexed by function index. The keys are densely packed up to
functions.size() so the hash probe wasn't buying us anything.
Also drops the bounds check from MemoryInstance::unsafe_get to match
FunctionInstance::unsafe_get; the caller has already proved the address
is in range by the time we get here.
Problem: Loading WebAssembly modules that use “return” mid-function can
corrupt the heap and crash the browser.
Cause: HANDLE_INSTRUCTION(return_) shrank the label stack but left any
working values pushed before the “return” instruction on the shared
value stack. Those residuals leaked into the caller’s frame and
accumulated across calls — until they overflowed the value stack’s
inline storage and corrupted adjacent allocator metadata.
Fix: After shrinking the label stack down to the function-level label,
also remove value-stack entries between that label’s recorded
stack_height and the top .arity() result values — mirroring the cleanup
that branch_to_label<true> already performs for br/br_if.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9614
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.
Wasm opcodes only need one byte plus a 24-bit selector for
prefixed instructions. Store them in u32 instead of u64 and reject
selectors that would not fit. Update the Rust opcode generator to
accept the new integer suffixes used by Opcode.h.
macOS POSIX shared memory objects returned by shm_open() only support
mmap() and ftruncate(). Calls to read(), write(), pread() or pwrite()
all fail with ESPIPE. The cranelift-compiler child was using pread/
pwrite via File::read_exact_at/write_all_at, which works fine on
Linux where the parent uses memfd_create(), but fails on macOS where
the parent uses shm_open().
Add a macOS-specific path that mmaps the inherited shm fd with
MAP_SHARED, mirroring the Windows MapViewOfFile path. Writes through
the mapping are automatically visible to the parent, so the explicit
write-back step at the end is also skipped on macOS.
On macOS, the cranelift JIT compilation step works by creating an
anonymous shared memory segment via shm_open() in the parent process
and passing the fd as an argument to the spawned cranelift-compiler
child. POSIX requires shm_open() to set FD_CLOEXEC on the returned
fd, which caused the child to immediately fail with EBADF when it
tried to read from the inherited fd number.
Clear FD_CLOEXEC after shm_open() so the fd actually survives the
spawn. The Linux memfd_create() path is unaffected since we call it
with flags=0 (no MFD_CLOEXEC), and Windows passes an inheritable
HANDLE instead of an fd.
Introduce MemoryBuffer, a memory backing store that uses mmap to reserve
the full wasm32 address space (4 GiB + guard pages) upfront, growing
without a copy and falling back to a ByteBuffer when mmap fails.
Also let Frame know how to handle non-owned locals (to e.g. allow
allocating them on the native stack.)