JSC `ArrayIsArray` DFG Intrinsic
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
JSC's inline cache system speculatively compiles fast property access stubs based on observed object shapes. The previous Array.isArray implementation was a JS builtin backed by IsCellWithType(CellUse), which told the DFG speculative compiler to assume the input is always a GC-managed cell. The TypeCheckHoistingPhase then moved this cell check to function entry — meaning a single non-cell call (undefined, null, a boolean) was enough to trigger a BadType OSR exit and jettison the entire compiled code unit. Hot code mixing typed and untyped inputs was silently deoptimizing.
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)
This commit replaces the builtin with a C++ host function backed by a new ArrayIsArray DFG/FTL IR node using UntypedUse. The node handles all ES spec cases inline: non-cell → false, ArrayType/DerivedArrayType → true, ProxyObjectType → slow path, other cells → false. The abstract interpreter can constant-fold it when the input type is statically known.
Significance
The result eliminates deoptimization entirely for the common case of Array.isArray called with mixed types, while retaining constant-folding for the known-array case. The range check technique — subtracting ArrayType and using BelowOrEqual with DerivedArrayType - ArrayType — is compact but relies on the static_assert that these JSType constants are contiguous.