← All issues

JSC ArrayLengthStore inline cache for JSArray length assignment

da43fc7

JSTests/stress/array-length-store-ic-warm-then-freeze-strict.js

+ function f(a, n) { 'use strict'; a.length = n; }
+ noInline(f);
+ let a = [1, 2, 3];
+ for (let i = 0; i < testLoopCount; ++i)
+ f(a, 2); // IC를 ArrayLengthStore로 warm up
+ Object.freeze(a);
+ shouldThrow(() => f(a, 1), TypeError); // frozen 배열에서 IC가 잘못 실행되어서는 안 됨

JSTests/stress/array-length-store-ic-cow.js

+ function f(a, n) { a.length = n; }
+ noInline(f);
+ for (let i = 0; i < testLoopCount; ++i) {
+ let a = mk(); // COW 배열 리터럴을 반환
+ f(a, 1);
+ shouldBe(a.length, 1);
+ }

JSC의 inline cache(IC) 시스템은 property 연산을 type에 특화된 machine-code stub으로 캐싱합니다. JIT이 property access를 만나면 객체의 structure ID를 키로 삼아 stub을 생성하고, 이후의 접근은 generic slow path를 거치지 않고 직접 해당 stub으로 처리됩니다.

JSArraylength property는 일반 stored property가 아닙니다. "custom value"로서, setter인 JSArray::setLength가 다섯 가지 array storage mode(Undecided, Int32, Contiguous, Double, ArrayStorage/SlowPutArrayStorage) 전반에 걸쳐 invariant를 강제합니다. 기존에는 JSArray::putlength에 대한 PutPropertySlot을 uncacheable로 남겨두어, arr.length = N 대입이 호출마다 operationPutByIdStrictGaveUp, 즉 generic slow path를 통과했습니다.

이 commit은 AccessCase::ArrayLengthStore라는 새로운 IC 타입을 추가하여 일반적인 shrink case를 inline으로 처리합니다. Int32Shape 또는 ContiguousShape를 가진 배열에서, 새로운 length가 현재 publicLength 이하의 non-negative int32인 경우, 생성된 stub은 butterfly storage의 tail slot을 비우고 publicLength를 직접 갱신합니다. 그 외의 경우 — grow, ArrayStorage mode, non-integer length, COW 배열 — 는 slow path로 폴백됩니다.

Before:
  arr.length = N
    └─► putById ──► tryCachePutBy ──► (uncacheable, gives up)
                                            └─► operationPutByIdStrictGaveUp
                                                    └─► JSArray::put (every call)

After:
  arr.length = N
    └─► putById ──► IC lookup
                      ├─ [ArrayLengthStore stub: Int32/Contiguous, newLen ≤ publicLen]
                      │       └─► zero tail slots ──► update publicLength  (fast, inline)
                      └─ [miss / grow / ArrayStorage / non-int]
                              └─► operationPutByIdStrictGaveUp ──► JSArray::put (slow)

DFG/FTL 티어에서 push-then-reset 패턴의 성능이 3.5–4.3배 향상되었습니다. 이 패턴은 pool 기반 allocation에서 매 반복마다 length를 0으로 줄여 배열을 재사용하는 방식에서 자주 나타납니다. 이 성능 향상은 hot shrink case에서 JSArray::putsetLength 전체 경로를 제거하고, 슬롯을 비우고 length를 저장하는 소수의 inline 명령으로 대체한 데서 비롯됩니다.

🔒

New JIT stubs directly manipulate array storage — several fast-path guard interactions and object lifecycle edge cases are worth security investigation.

더 확인하려면 구독해 주세요