[JSC] Private tmp mechanism in the DFG ByteCodeParser
Component: JSC DFG and FTL JIT | c415e39
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
The DFG compiles bytecode into a CPS-form graph where local variables and internal state live in numbered tmp slots, historically all allocated from the CodeBlock's own tmp count. Array.prototype.sort inlining is a newer DFG optimization that generates its own internal control flow (handleArraySort) and needs scratch tmps beyond what the bytecode's CodeBlock accounts for. This commit adds a per-InlineStackEntry private tmp allocator — m_numPrivateTmps, allocatePrivateTmps, tmpOffsetForInlineeOf — so each inlined frame gets a non-overlapping tmp range.
Before:
Outer frame: CodeBlock tmps [0..N) | checkpoint pad | outer's private sort tmps
Inlinee tmpOffset = N (ignores the caller's own private sort tmps)
=> an inlined comparator that itself sorts allocates 9 private tmps that
overlap the outer sort's scratch (tmpI, tmpArray, tmpLength, ...)
After:
Outer frame: CodeBlock tmps [0..N) | outer's private sort tmps [N..N+9), tracked
Inlinee tmpOffset = tmpOffsetForInlineeOf(caller) = N + 9
=> the inner frame's tmps start fully above the outer's private range
Significance
This is a memory-aliasing bug in a tier that emits machine code directly, so nested inlined sort comparators could read and write each other's temporary storage. The affected path — Array.prototype.sort with a comparator that sorts — is reachable from ordinary script.
Audit directions
Forward-facing, the question is which other DFG-internal tmp consumers exist or will exist. Narrow: confirm allocatePrivateTmps() and tmpOffsetForInlineeOf() are used by every DFG-internal tmp consumer, not just sort, and that OSR exit and bytecode liveness analysis correctly exclude these private tmp ranges — a private tmp that liveness treats as a bytecode local is the failure mode to look for. Wider: any compiler that allocates scratch storage out of a frame-relative index space shared with a caller has this shape; audit deeply nested or mutually recursive inline stacks (sort calling sort calling sort) for offset overflow or miscomputation as m_numPrivateTmps accumulates per frame. Match tell in review: an Operand::tmp(base + k) computed from anything other than a range object returned by the allocator.