Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 24527bbb9ac882ec69bbb900711d5ca479d12316
https://github.com/WebKit/WebKit/commit/24527bbb9ac882ec69bbb900711d5ca479d12316
Author: Keith Miller <[email protected]>
Date: 2026-08-01 (Sat, 01 Aug 2026)
Changed paths:
M Source/JavaScriptCore/assembler/ARM64Assembler.h
M Source/JavaScriptCore/assembler/ARMv7Assembler.h
M Source/JavaScriptCore/assembler/AbstractMacroAssembler.h
M Source/JavaScriptCore/assembler/LinkBuffer.cpp
M Source/JavaScriptCore/assembler/LinkBuffer.h
M Source/JavaScriptCore/assembler/RISCV64Assembler.h
M Source/JavaScriptCore/assembler/X86Assembler.h
M Source/JavaScriptCore/wasm/WasmBBQPlan.cpp
M Source/JavaScriptCore/wasm/WasmCalleeGroup.cpp
M Source/JavaScriptCore/wasm/WasmCalleeGroup.h
M Source/JavaScriptCore/wasm/WasmEntryPlan.cpp
M Source/JavaScriptCore/wasm/WasmEntryPlan.h
M Source/JavaScriptCore/wasm/WasmIPIntPlan.cpp
M Source/JavaScriptCore/wasm/WasmIPIntSlowPaths.cpp
M Source/JavaScriptCore/wasm/WasmMachineThreads.cpp
M Source/JavaScriptCore/wasm/WasmMachineThreads.h
M Source/JavaScriptCore/wasm/WasmOMGPlan.cpp
M Source/JavaScriptCore/wasm/WasmOSREntryPlan.cpp
M Source/JavaScriptCore/wasm/WasmOSREntryPlan.h
M Source/WTF/wtf/ThreadMessage.h
M Source/WTF/wtf/Threading.h
M Source/WTF/wtf/posix/ThreadingPOSIX.cpp
M Source/WTF/wtf/win/ThreadingWin.cpp
Log Message:
-----------
[Wasm] Optimize JITCallee publication
https://bugs.webkit.org/show_bug.cgi?id=320747
rdar://183747593
Reviewed by Yusuke Suzuki.
Publishing a freshly compiled wasm function held CalleeGroup::m_lock across the
two most
expensive steps in the sequence: a per-callsite icache flush for every outgoing
call, and
barrierCacheOnAllThreads(), which forces a kernel round-trip per tracked wasm
thread.
Neither reads nor mutates CalleeGroup state, yet every other compiler thread
blocked
behind them.
This does three things.
First, correctness: Relying on LinkBuffer's finalize flush to cover a
function's outgoing
call sites is unsafe: those targets are written after finalization, so an
adjacent live
instruction sharing a cache line with a call site can pull the stale pre-link
bytes into
the instruction cache. relink{Jump,Call,TailCall} and repatchNearCall gain a
RepatchingInfo template parameter so a repatch can skip its flush, with
flush{Jump,Call,TailCall} counterparts to perform it later, and wasm's
LinkBuffers are
constructed with CacheFlushOnFinalize::No. A single whole-range cacheFlush then
runs after
every outgoing call is linked. machineCodeCopy already static_asserts that a
memcpy
repatch carries no Flush bit, so the split cannot be quietly broken.
Second, the critical section: installOptimizedCallee now links outgoing calls
under the
lock, drops it via DropLockForScope to flush and synchronize, then re-acquires
to publish
and relink callers. The window is safe because the callee is not yet in
m_optimizedCallees,
so no execution thread can reach unflushed code. It is not, however,
undiscoverable:
m_pendingPublishCallees keeps it findable as a direct-call target, so a thread
that retires
one of its callees during the window still relinks it rather than leaving it
calling code
that is about to be freed. Since an OMG install for the same index can land
mid-window,
the decision to redirect callers is recomputed after re-acquiring; acting on
the stale one
would relink callers to BBQ over a newer OMG and tier the function back down.
Third, the readers: Maintains the lock-free tier-up peeks from 299970@main for
the two hot
paths — the IPInt loop back-edge and the BBQ loop's OMG check — now that the
critical
section they contended with is short. Both are sound because a callee only
appears in
m_optimizedCallees after its code is flushed and published. The staging slot
that commit
needed is gone: m_pendingPublishCallees subsumes it, and more cleanly, since an
unpublished
callee is simply absent rather than visible-but-half-baked. m_bbqCalleeLock has
to stay
because ThreadSafeWeakOrStrongPtr is a tagged union whose strong->weak
transition and
assignment are both non-atomic. The IPInt prologue reader deliberately stays on
m_lock: it
is only reached when IPInt cannot interpret the function at all, so it must
find whichever
JIT callee exists, and it is disabled in the default configuration.
barrierCacheOnAllThreads() no longer "claims" to invalidate any instruction
cache, only
context-synchronizes, and is rewritten on top of a new
Thread::barrierInstructionCache().
The old implementation passed crossModifyingCodeFence() to sendMessage, which
runs the functor
on the calling thread, not the target, so it only ever worked via the
suspend/resume the
message mechanism performs incidentally. On ARM64 Darwin the new primitive
reads the target's
register state instead, whose returning ERET is the context-synchronizing
event. Elsewhere it
stays as a thread suspend/resume sequence.
OSR entry installation shares all of this rather than duplicating it, as it did
before. An
OMGOSREntryCallee is not the function's entrypoint even though it shares its
index, so one
predicate gates both the recursive-call resolution and the caller relinking;
its reservation
in m_osrEntryCallees also registers it, which is safe because that map is
relinking
bookkeeping and only BBQCallee::setOSREntryCallee, after the flush, makes the
code enterable.
Also removes EntryPlan::m_unlinkedWasmToWasmCalls and IPIntPlan's relink loop
over it,
which were dead (parseAndCompileMetadata never populated the vector).
No new tests, no behavior change. Covered by existing tests.
Canonical link: https://commits.webkit.org/318386@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications