← 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];
+ }

JSC 객체의 butterfly는 음수 offset 쪽에 out-of-line property를 저장합니다. 배열이라면 양수 offset 쪽에 indexing header(publicLength, vectorLength)가 놓이고, 그 뒤에 element vector가 이어집니다. put 연산으로 property를 추가할 때 out-of-line capacity가 이미 가득 차 있으면 butterfly를 다시 할당해야 합니다. 이때 header와 element가 새 영역으로 복사되고, StructureID도 갱신됩니다. 지금까지는 모든 JIT tier가 이 과정을 C++ operation에 맡겨 왔습니다. 즉 크기 계산, 복사, hole 값 처리, GC 대응이 모두 해당 operation 안에서 처리되었습니다.

이번 commit으로 put-transition IC stub, DFG, FTL이 확장된 butterfly를 inline으로 allocate하도록 변경되었습니다. 크기는 vectorLength를 기준으로 계산되며, header와 vector는 JIT이 생성한 loop로 복사됩니다. 대상은 Undecided, Int32, Double, Contiguous 배열입니다. 반면 ArrayStorage와 typed array는 여전히 operation을 호출합니다. 한편 AccessCase::doesCalls()는 더 이상 이러한 transition을 call로 집계하지 않도록 수정되었습니다.

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)

새로 생성한 배열에 named property를 추가하는 동작이 1.20-1.25배 빨라졌습니다. dart-sass 같은 dart2js 출력물에서 흔히 나타나는 패턴입니다. 다만 그 대가로, security-critical한 연산의 allocation 및 copy codegen이 세 곳에 각각 직접 작성되었습니다.