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.
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.
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.
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.
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.
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.