Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 26aa84fcd527016df60838ccc19c722f39b6b68a
https://github.com/WebKit/WebKit/commit/26aa84fcd527016df60838ccc19c722f39b6b68a
Author: Keith Miller <[email protected]>
Date: 2026-08-28 (Fri, 28 Aug 2026)
Changed paths:
A
JSTests/stress/arguments-elimination-inlined-load-varargs-preserves-recoveries.js
A
JSTests/stress/arguments-elimination-load-varargs-kills-promoted-recoveries.js
M Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
M Source/JavaScriptCore/dfg/DFGCombinedLiveness.cpp
M Source/JavaScriptCore/dfg/DFGCombinedLiveness.h
M Source/JavaScriptCore/dfg/DFGForAllKills.h
M Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp
Log Message:
-----------
OSR Availability Fails to Invalidate Local Recoveries Across LoadVarargs
https://bugs.webkit.org/show_bug.cgi?id=318348
rdar://178255543
Reviewed by Yusuke Suzuki.
When a strict-mode `arguments` allocation produced by an inlined varargs call
is eliminated to PhantomClonedArguments, OSR availability records each argument
as a promoted heap location backed by the inlined frame's stack slots. A second
inlined varargs call that lowers to LoadVarargs may reuse those same virtual
registers. If an OSR exit later materialises the first allocation it must not
do so from those reused slots.
Two independent invariants were violated:
1. LocalOSRAvailabilityCalculator::executeNode() handles PutStack/KillStack by
calling killHeaps() before replacing a stack operand's availability so that
any promoted heap location flushed to that operand is invalidated. The
LoadVarargs / ForwardVarargs case replaced the count and argument operands
without that invalidation, leaving stale promoted recoveries pointing at the
overwritten slots. Apply the same killHeaps() calls before each replacement.
2. DFG arguments-elimination interference analysis must disqualify a candidate
whose source-frame stack slots are clobbered while the candidate is still
OSR-live. It establishes the candidate's live range using
forAllKilledNodesAtNodeIndex() plus CombinedLiveness::liveAtTail.
liveAtTail was computed only as the union of CFG successors' liveAtHead,
each of which is pruned by bytecode liveness at the successor's first node.
A candidate that is OSR-live at the block's terminal node but whose
backing local is bytecode-dead at every CFG successor e.g.
```
// block 1
let a = ...;
try {
foo();
} catch (e) {
// block 2
use(a);
}
// block 3
```
Since DFG does not directly model exceptional control flow in the CFG
`a` would be absent from the CFG/bytecode at block 3 and therefore absent
from block 1's liveAtTail, so removeViaKill() would never be called on `a`.
Note: For 2, we don't include the bytecodeLiveness for the tail of
CombinedLiveness because this is both misleading with respect to how tail is
used in the rest of the DFG. Additionally, it breaks ObjectAllocationSinking.
ObjectAllocationSinking propagates its heap by pruning at each tail with
liveAtTail then merging into successors. Since we don't prune at the head we
can end up pushing phantom allocations into blocks where they don't actually
dominate.
Tests:
JSTests/stress/arguments-elimination-inlined-load-varargs-preserves-recoveries.js
JSTests/stress/arguments-elimination-load-varargs-kills-promoted-recoveries.js
Originally-landed-as: [email protected] (6c004fb89bd7).
rdar://185367795
Canonical link: https://commits.webkit.org/320072@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications