JSC `ArrayIsArray` DFG Intrinsic
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
JSC의 inline cache 시스템은 관찰된 object shape을 기반으로 fast property access stub을 speculative하게 컴파일합니다. 기존 Array.isArray 구현은 IsCellWithType(CellUse) 기반의 JS builtin이었으며, DFG speculative compiler가 입력을 항상 GC-managed cell로 가정하도록 만드는 방식이었습니다. TypeCheckHoistingPhase가 이 cell check를 함수 진입 시점으로 끌어올리면서, undefined·null·boolean 등 non-cell 값이 단 한 번만 전달되어도 BadType OSR exit가 발생합니다. 컴파일된 코드 단위 전체가 jettison되는 셈입니다. typed 입력과 untyped 입력을 혼용하는 hot code는 이 때문에 조용히 deoptimization되고 있었습니다.
Before:
call Array.isArray(v)
└─► JS builtin → IsCellWithType(CellUse)
└─► TypeCheckHoistingPhase hoists cell check to entry
└─► v = undefined / null / boolean → BadType OSR exit → DFG jettison
After:
call Array.isArray(v)
└─► ArrayIsArray DFG node (UntypedUse)
├── non-cell (int/double/bool/null/undefined) ──► false (inline)
├── ArrayType / DerivedArrayType ──► true (inline)
├── ProxyObjectType ──► isArraySlow() (C++ slow path)
└── other cell ──► false (inline)
이 commit은 builtin을 C++ host function으로 교체했습니다. 새로운 ArrayIsArray DFG/FTL IR 노드는 UntypedUse를 사용하며, ES spec의 모든 케이스를 inline으로 처리합니다. non-cell은 false, ArrayType과 DerivedArrayType은 true, ProxyObjectType은 slow path, 그 외 cell은 false로 각각 분기됩니다. 입력 타입이 정적으로 알려진 경우에는 abstract interpreter가 constant-fold를 수행할 수 있습니다.
Significance
mixed type으로 Array.isArray를 호출하는 일반적인 경우, 이 변경으로 deoptimization이 완전히 제거됩니다. array임이 이미 알려진 입력에 대한 constant-folding도 그대로 유지되는 점이 중요합니다. 범위 검사는 ArrayType을 뺀 뒤 DerivedArrayType - ArrayType을 기준으로 BelowOrEqual 비교를 수행하는 방식으로, 간결하게 구현되어 있습니다. 다만 해당 JSType 상수들이 연속적으로 배치되어 있다는 static_assert에 의존한다는 점에 주의가 필요합니다.