DFG/FTL ArrayUnshift intrinsic
JSTests/stress/array-unshift-intrinsic-ftl.js
JSC uses a tiered JIT (Baseline → DFG → FTL). For hot built-in functions, the DFG bytecode parser recognises an "intrinsic" and emits a specialised DFG node. Array.prototype.unshift is more complex than push: it must shift all existing elements one or more positions toward higher indices, involving a memmove of the butterfly storage. For Contiguous arrays (which hold GC-managed JSValues), every moved element also requires a write barrier.
This commit implements Array.prototype.unshift as a first-class DFG and FTL intrinsic. Int32, Double, and Contiguous array storage types are supported with inline fast paths for 0- and 1-element unshift; ArrayStorage falls back to the slow path. The intrinsic is wired through bytecode parser, fixup, clobberize, abstract interpreter, speculative JIT (64-bit), and FTL B3 lowering.
Significance
Adds a new JIT-compiled code path for a mutation-heavy array operation that shifts all existing elements rightward in memory — a historically fertile area for type confusion and bounds errors in JIT compilers.
Audit directions
- Inline memmove bounds. The 1-element inline path shifts existing elements by computing source/destination butterfly offsets in JIT-emitted code. An off-by-one in the capacity or length check before the memmove can produce an out-of-bounds write into the butterfly.
- Write barrier completeness. Every moved Contiguous slot needs a write barrier. If any moved slot is not barrier-emitted (e.g., the barrier loop covers
[0..length-1]but the new slot at index 0 is written without a barrier), the GC's remembered set becomes inconsistent, enabling use-after-free on a future collection. - Type speculation vs structure transitions. Between the type guard and the actual butterfly mutation, a concurrent GC or an intervening store could change the array's
IndexingType(e.g., Contiguous → ArrayStorage via a hole). If the OSR-exit guard doesn't cover this window, the engine may apply Contiguous memmove logic to ArrayStorage layout. - Multi-element scratch buffer. For ≥2 arguments, elements are staged in a scratch buffer derived from the argument count. If the JIT miscounts (off-by-one in vararg handling) the buffer allocation, a write past the end of the scratch region is possible.
- Length overflow. An array near 2^32-1 elements where the length addition overflows is worth testing — the JIT may elide the overflow check if it assumes the result fits in Int32 from type propagation.