← All issues

Butterfly-less JSObject for Wasm GC

55783b9

Source/JavaScriptCore/runtime/JSObject.h

- WriteBarrier<Unknown> m_butterfly;
+ // m_butterfly moved to JSObjectWithButterfly
 
Butterfly* butterfly()
{
+ auto* b = *std::bit_cast<Butterfly**>(std::bit_cast<char*>(this) + butterflyOffset());
+ if (type() == WebAssemblyGCObjectType) [[unlikely]]
+ b = nullptr;
+ return b;
}

Source/JavaScriptCore/jit/AssemblyHelpers.cpp

- static_cast<int32_t>(sizeof(JSObject)) -
+ static_cast<int32_t>(JSObject::offsetOfInlineStorage()) -

Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

- loadp JSObject::m_butterfly[objectAndStorage], objectAndStorage
+ loadp JSObjectWithButterfly::m_butterfly[objectAndStorage], objectAndStorage
- addp sizeof JSObject - (firstOutOfLineOffset - 2) * 8, objectAndStorage
+ addp sizeof JSObjectWithButterfly - (firstOutOfLineOffset - 2) * 8, objectAndStorage

JSC에서 butterfly는 JSObject에 연결된 heap-allocated sidecar 구조체로, pointer 왼쪽에는 out-of-line named property를, 오른쪽에는 indexed storage를 보관합니다. JSObject 내에서 butterfly가 위치한 offset은 JIT 코드, LLInt assembly macro, B3 IR lowering 전반에 걸쳐 직접 내장된 값입니다. Wasm GC object(GC proposal로 정의된 struct와 array)는 JS property가 없는 고정된 typed layout을 사용하므로, butterfly를 구조적으로 가질 수 없었습니다. 항상 nullptr였고, 객체당 8바이트를 낭비해 왔습니다.

이 commit은 JSObject를 두 계층으로 분리합니다. butterfly가 없는 JSObject base와, m_butterfly를 담당하는 JSObjectWithButterfly subclass로 계층이 재편됩니다. Wasm GC object는 JSObject를 직접 상속하며, butterfly() accessor는 speculative load 방식으로 이를 보완합니다. HeapCell atom이 항상 16바이트 이상이라는 전제 하에, butterflyOffset()에서 무조건 값을 읽습니다. 이후 type() == WebAssemblyGCObjectType인 경우 결과를 0으로 설정해 common path가 branchless하게 유지됩니다. JIT가 생성한 butterfly load는 type check를 완전히 건너뜁니다. Wasm GC object가 butterfly dereference 이전의 structure/indexing-type guard를 절대 통과할 수 없다는 invariant에 의존하는 방식입니다.

Before:                                After:
  JSObject { m_butterfly, inline... }    JSObject { inline... }  ← no m_butterfly
    ├── JSNonFinalObject                   ├── JSObjectWithButterfly { m_butterfly }
    ├── JSFinalObject                      │     ├── JSNonFinalObject
    └── WebAssemblyGCObjectBase            │     └── JSFinalObject
          (m_butterfly always nullptr)     └── WebAssemblyGCObjectBase  ← 8 bytes saved

JSC의 핵심 object hierarchy 중 하나가 재구성되었습니다. butterfly pointer나 inline storage에 관여하는 모든 offset 계산, JIT inline cache, LLInt macro, B3 lowering을 빠짐없이 점검하고 수정해야 했습니다. 누락된 지점은 그대로 silent memory corruption으로 이어집니다. 직접적인 개선 효과는 Wasm GC heap 사용량 감소입니다.

🔒

The speculative butterfly load and the JIT bypass invariant both have edge cases in newly introduced code paths worth investigating.

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