← All reports

[JSC] Allocate property storage of arrays without C++ operation call

Component: JSC JIT | fa3737d

JSTests/microbenchmarks/put-by-id-transition-on-array.js

+ function tag(array, value) {
+ array.tag = value;
+ return array;
+ }
+ noInline(tag);
...
+ for (var i = 0; i < 2000000; ++i) {
+ var array = tag([i, i + 1], i);
+ result += array.tag + array[1];
+ }

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)

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.