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