[JSC][Wasm] Inline table.get for funcref
Component: JSC WebAssembly JIT | 6a359a0
Source/JavaScriptCore/wasm/WasmBBQJIT64.cpp
Source/JavaScriptCore/wasm/WasmOMGIRGenerator.cpp
A WebAssembly Table can hold funcref elements, which are exposed to JS as regular function objects — JSC lazily creates and caches a JS wrapper object per function reference the first time it is observed. Previously every table.get on a funcref table unconditionally called into the C++ runtime (operationGetWasmTableElement) to do the bounds check, look up the cached wrapper, and materialize one if missing.
This commit inlines the funcref case into both the BBQ baseline (64-bit path) and OMG optimizing JIT tiers. Generated code now bounds-checks the index against the table's length inline, trapping directly on failure, then loads the cached wrapper from the table's wrapper array inline, branching to the C++ call only when that slot reads zero — meaning the wrapper has not been materialized yet, or the element is a genuinely null funcref. A new B3 abstract heap, WasmFuncRefTable_wrappers, is added so the compiler's alias analysis can reason about loads and stores to the wrapper array.
Significance
This removes a mandatory C++ runtime call from what was previously the only path for reading funcref table elements, so table.get gets noticeably faster in both Wasm JIT tiers. The OMG path builds an explicit Phi over the fast and rare-marked slow blocks, so the slow call no longer sits on the hot path's critical dependency chain.
Audit directions
The forward-facing pattern is a bounds-checked, GC-managed memory read moved out of a safe C++ runtime call into hand-generated machine code across two tiers. Narrow: check whether the inlined table-length check can go stale relative to table.grow, which reallocates both the backing store and the wrapper array — an inline load holding a stale base pointer or a stale length across a grow is the classic shape here, and the two tiers must agree. Also check how the inline path distinguishes "wrapper not yet cached" from a legitimate null funcref: the fast path tests the loaded value against zero, so a wrong sentinel choice could leak an uninitialized or garbage JSValue to script as if it were a function object. Wider: the new WasmFuncRefTable_wrappers abstract heap is what stops the compiler reordering the inline load across table.set, table.grow, or the write barrier protecting the wrapper cache from the GC — audit its clobber declarations the same way you would any newly-introduced heap range, and sweep the other Wasm table and memory operations that still route through runtime calls for whether the same inlining is planned. Widest: any JIT change that replaces a runtime call with inline code inherits every invariant the runtime function used to enforce implicitly; enumerate what the C++ function checked and confirm each check has an inline counterpart or a proven-unnecessary argument.