[JSC] Allocate property storage of arrays without C++ operation call
Component: JSC JIT | fa3737d
JSTests/microbenchmarks/put-by-id-transition-on-array.js
A JSC object's butterfly holds out-of-line properties at negative offsets and, for arrays, an indexing header (publicLength, vectorLength) followed by the element vector at positive offsets. When a put adds a property and out-of-line capacity is full, the butterfly is reallocated, the header and elements copied, and the StructureID updated; until now every JIT tier delegated that to a C++ operation handling sizing, copying, hole values and GC.
Put-transition IC stubs, DFG and FTL now allocate the grown butterfly inline, size it from vectorLength, and copy header and vector with an emitted loop for Undecided, Int32, Double and Contiguous arrays. ArrayStorage and typed arrays still call the operation, and AccessCase::doesCalls() no longer counts these transitions as calls.
Put transition (array), after:
└─► check structure
├─ Undecided/Int32/Double/Contiguous:
│ ├─ size = f(vectorLength, newOOLCapacity)
│ ├─ inline allocate (slow path on fail)
│ ├─ loop-copy header + vector
│ └─ store butterfly, then StructureID
└─ ArrayStorage / typed arrays: call operation (unchanged)
Significance
Adding a named property to a freshly created array, common in dart2js output such as dart-sass, gets 1.20-1.25x faster. The cost is hand-emitted allocation and copy codegen for a security-critical operation in three separate places.
Audit directions
- Size and copy arithmetic. Check size computation from
vectorLengthand new out-of-line capacity for overflow, preCapacity and unused-capacity handling, and parity with the C++ operation across all four indexing types; check copy count and direction for empty, holey, large (1200+) and copy-on-write arrays. - GC-visible contents. Hole representation (empty
JSValuevs PNaN for Double) and uninitialized tail slots betweenpublicLengthandvectorLengthmust be valid for the GC and later reads. - Ordering and call accounting. Review butterfly-store vs
StructureID-store ordering and fences, the allocation slow path (register save/restore, GC during the stub), and thedoesCalls()change — a stub treated as call-free that still reaches a slow-path call could get register preservation, exception or GC state wrong. - Tier divergence. Check whether indexing type can change between structure check and inline allocation in any tier, and diff the DFG, FTL and IC lowerings against each other; divergent logic across three copies is a typical bug source.