[JSC] Add ArrayJoin DFG nodes
JSC's DFG is the mid-tier JIT compiler; operations are nodes in a graph. Adding ArrayJoin as a node means the compiler emits native code for Array#join instead of dispatching to a generic C++ runtime. ArrayMode encodes observed storage types (Int32, Double, Contiguous, Generic). The critical correctness invariant is that a separator object's toString() side effect must run exactly once per join() call, even when an array mutation inside toString() triggers an OSR exit — because the bytecode slow path will call toString() again if the JIT already ran it.
The commit's regression tests encode this directly: object separators whose toString() mutates the array (length shrink, indexing-type swap) must produce identical output across all tiers, with calls === testLoopCount.
Significance
New JIT intrinsics with side-effect-sensitive invariants across OSR exit boundaries are historically one of the richest categories of JIT vulnerability.
Audit directions
OSR exit + toString double-evaluation: the toString-once invariant is subtle. If the JIT calls ToString(sep) before the array walk and then OSR-exits partway through (due to array mutation inside an element's toString(), not the separator's), does the slow path call ToString(sep) again? Element-mutation paths may be undertested. The empty-separator fast path skips separator allocation and concatenates directly — check holes, type transitions mid-walk, and integer overflow in accumulated string length in DFGSpeculativeJIT64.cpp and FTLLowerDFGToB3::compileArrayJoin(). StrengthReductionPhase is patched to handle ArrayJoin; if it incorrectly folds a constant separator with a getter on the separator's prototype, the fold suppresses required side effects. ArrayMode mis-speculation: train on Int32 then pass Contiguous with custom toString() — verify the speculation guard precedes the first element access.