[JSC] Add Array#concat DFG nodes
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
DFG (Data Flow Graph) JIT는 JSC의 mid-tier speculative compiler입니다. bytecode를 typed node로 구성된 IR로 변환하고, type prediction 기반의 최적화를 단계적으로 적용한 뒤, assumption이 실패하는 시점에 OSR-exit할 수 있는 speculative machine code를 생성합니다. COW (Copy-on-Write) butterfly array는 array literal을 위한 storage 최적화입니다. backing store를 공유 상태로 유지하면서 read-only로 표시해두다가, 쓰기가 발생하는 시점에 복사가 수행됩니다. Array.prototype.concat은 특히 복잡한 연산입니다. spec이 임의의 객체에 [Symbol.isConcatSpreadable]을 통해 spreading 참여 여부를 결정할 수 있도록 허용하기 때문에, fast path를 적용하려면 반드시 해당 hook의 개입 가능성을 배제해야 합니다.
이 commit은 ArrayConcatArray와 ArrayConcatAppendOne이라는 두 개의 새로운 DFG/FTL intrinsic IR node를 추가하여 Array.prototype.concat의 JIT 컴파일을 가능하게 했습니다. fixup 단계에서는 type prediction을 활용해 범용 append node를 array 전용 variant로 특수화합니다. fast path는 arrayIsConcatSpreadableWatchpointSet에 대한 watchpoint와 isJSArrayWithOriginalStructure 검사로 보호됩니다. 런타임에 복잡한 side effect가 감지되면 연산은 nullptr를 반환하고, JIT는 ExoticObjectMode를 통해 OSR-exit합니다. 한편 이 patch의 계기가 된 runtime test에는 fix 이전에 존재하던 crash 사례가 기록되어 있습니다. tryConcatOneArgFast에서 non-array 객체가 JSArray에 대한 uncheckedDowncast에 도달해 잘못된 butterfly pointer를 역참조하던 문제였습니다.
Significance
fixup 단계는 type prediction을 기반으로 ArrayConcatAppendOne을 ArrayConcatArray로 변환합니다. fixup 결정이 내려진 이후에 type이 바뀌는 입력을 조작하는 것은, JIT type confusion을 유발하는 전형적인 기법입니다. COW butterfly 재사용과 새로운 OSR-exit boundary가 결합되면, JSC가 올해 추가한 코드 중 correctness 버그가 가장 발생하기 쉬운 영역 중 하나에 해당합니다.
Audit directions
-
잔존하는
uncheckedDowncast<JSArray>가정. regression test는 fix 이전에 crash를 유발했던 정확한 패턴을 가리킵니다.tryConcatOneArgFast와 공유 concat helper의 인접 로직에는 prototype poisoning이나 proxy를 통해 도달 가능한 "여기까지 왔다면 JSArray다"라는 암묵적인 가정이 여전히 남아 있을 가능성이 있습니다. -
COW butterfly 재사용 안전성. 빈 array와 COW source를
concat했을 때 동일한 butterfly를 backing store로 공유하는 array가 반환되는 경우, 정확성은 abstract interpreter, fixup, codegen 단계 전반에 걸쳐 COW tracking이 건전하게 유지되는지에 달려 있습니다. COW test는 abstract result structure set에서 CopyOnWrite variant가 누락되면 이후 단계의 speculation이 sound하지 않게 된다는 점을 명시합니다. -
Watchpoint와 실행 순서. watchpoint 등록과 JIT 컴파일된 호출 사이에
[Symbol.isConcatSpreadable]이 설치되는 경우, invalidation과 실행 중인 코드 간의 순서가 중요해집니다. watchpoint가 전체 window를 커버할 만큼 동기적으로 발동되는지 점검해야 합니다. -
OSR-exit
ExoticObjectModeinvariant 보존.nullptr반환 경로는 codegen 시점에 두 방향으로 분기됩니다. 두 경로 모두 필요한 invariant(object identity, array length, index bounds)를 보존하는지 확인해야 합니다.