Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: ca0c6bb186577b89265aad4fb02ff9042b38442a
      
https://github.com/WebKit/WebKit/commit/ca0c6bb186577b89265aad4fb02ff9042b38442a
  Author: Keith Miller <[email protected]>
  Date:   2026-05-17 (Sun, 17 May 2026)

  Changed paths:
    A JSTests/wasm/stress/lazy-table-copy-preserves-laziness.js
    A JSTests/wasm/stress/lazy-table-cross-instance.js
    A JSTests/wasm/stress/lazy-table-identity-across-paths.js
    A JSTests/wasm/stress/lazy-table-wrappers.js
    M 
Source/JavaScriptCore/SaferCPPExpectations/UncountedLocalVarsCheckerExpectations
    M Source/JavaScriptCore/wasm/WasmFormat.h
    M Source/JavaScriptCore/wasm/WasmTable.cpp
    M Source/JavaScriptCore/wasm/WasmTable.h
    M Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp

  Log Message:
  -----------
  [Wasm] JS Wrappers for Wasm Tables Should be Lazily Created
https://bugs.webkit.org/show_bug.cgi?id=314958
rdar://177252875

Reviewed by Yusuke Suzuki.

This is a follow-up to 313362@main, which made WebAssemblyFunction wrappers
and JSToWasmCallees lazy for ref.func / passive-element / host-import paths
but left active element segments eager. Active element segments install
entries through JSWebAssemblyInstance::initElementSegment, which called
ensureFunctionWrapper for every funcref entry and stored the resulting
WebAssemblyFunction (and its boxed JSToWasmCallee) into
FuncRefTable::Function::m_value at instantiation time. Modules with sizable
funcref tables paid that wrapper/callee allocation cost even when the
entries were only ever consumed by wasm-internal call_indirect.

This patch makes the table slot itself lazy. A funcref entry installed by an
element segment now carries only the raw WasmOrJSImportableFunction
metadata (boxedCallee, targetInstance, entrypointLoadLocation, RTT) in
FuncRefTable::Function::m_function; m_value is left null. The JS wrapper is
materialized on demand the first time JS observes the slot via
FuncRefTable::get (table.get / Table.prototype.get / ref.func going through
ensureFunctionWrapper). call_indirect is unaffected because it already
reads m_function via fixed offsets and never depended on m_value.

The materialization path uses the slot's existing targetInstance:

slot.m_function.targetInstance->ensureFunctionWrapper(
       slot.m_function.boxedCallee.asNativeCallee()->index())

For internal functions targetInstance is the table's owning instance; for
wasm-to-wasm imports it is the source instance, so the returned wrapper
matches the source-instance wrapper that the eager path would have produced.
JS-function imports stay eager because their boxedCallee is the
WasmToJSCallee singleton, whose index() returns a sentinel rather than a
real FunctionSpaceIndex. table.set / table.fill / table.grow with an
existing funcref keep populating m_value as before — the wrapper is already
in hand, and caching it preserves identity for repeated table.get.

Concretely:

* Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::initElementSegment):
on FromRefFunc entries, internal functions delegate to
FuncRefTable::setLazy(dstIndex, this, functionIndex); imports continue
through ensureFunctionWrapper and jsTable->set as before. Wasm validation
guarantees the destination table is a funcref table when the entry is
FromRefFunc.

* Source/JavaScriptCore/wasm/WasmTable.h:
(JSC::Wasm::FuncRefTable::Function::isEmpty):
new predicate that reads !m_function.rtt; replaces three open-coded checks
in the .cpp.

* Source/JavaScriptCore/wasm/WasmTable.cpp:
(JSC::Wasm::Table::visitAggregateImpl):
appends each funcref slot's m_value, and for populated slots also the
slot's m_function.targetInstance and m_function.importFunction directly,
since the transitive reachability chain through m_value no longer exists
for lazy slots.

(JSC::Wasm::FuncRefTable::setLazy):
takes the target JSWebAssemblyInstance and a FunctionSpaceIndex, looks up
the wasm callee, entrypoint, and RTT from that instance's calleeGroup and
module, installs them into m_function, clears m_value, and fires a write
barrier on the owning JSWebAssemblyTable so the embedded WriteBarriers
are tracked.

(JSC::Wasm::FuncRefTable::get):
now non-const. It returns a cached m_value if set; otherwise, for a
populated slot, it materializes the wrapper via the formula above and
caches it into m_value for subsequent reads. An empty slot (isEmpty())
returns jsNull.

(JSC::Wasm::FuncRefTable::copyFunction):
becomes non-const-srcTable and switches to isEmpty() instead of calling
get(), so table.copy copies the lazy state verbatim without triggering
materialization.

Tests: JSTests/wasm/stress/lazy-table-copy-preserves-laziness.js
       JSTests/wasm/stress/lazy-table-cross-instance.js
       JSTests/wasm/stress/lazy-table-identity-across-paths.js
       JSTests/wasm/stress/lazy-table-wrappers.js

Canonical link: https://commits.webkit.org/313378@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to