[JSC] Add String fast iteration
// JSTests/stress/iterator-fast-path-mode-mixing.js
var array = [1, 2, 3];
var map = new Map([[1, 2], [3, 4]]);
var set = new Set([1, 2, 3, 4]);
var ascii = "abcde";
var unicode = "a\u{20BB7}b";
// Mixing more iterable types at one for-of site than the fast iteration
// mode limit must keep working correctly.
JSC's fast iteration protocol allows the JIT to recognize that certain for-of loops iterate over known-typed collections and skip the full ECMAScript iteration protocol: no Symbol.iterator call, no .next() dispatch, no {value, done} allocation per step. Previously only Array, Map, and Set had fast modes. In DFG/FTL the pattern represents the loop body's output as a tuple node whose results are extracted with GetProjection; ObjectAllocationSinking can then prove the backing iterator never escapes and eliminate it entirely.
This commit extends the protocol to JSString across all four JIT tiers. LLInt/Baseline use a new JSStringIterator::nextWithAdvance() helper to avoid result-object allocation; DFG/FTL introduce StringIteratorNext(string, position) — a tuple-returning node that, combined with allocation sinking, eliminates the JSStringIterator allocation entirely. The 8-bit single-character case gets an inline fast path returning a cached JSString; ropes, 16-bit strings, and surrogate pairs fall back to operationStringIteratorNext. A per-site cap of 2 fast modes limits inline code growth — sites mixing more than 2 iterable types degrade to Generic.
Significance
~4x speedup on ASCII for-of loops and ~1.8x on surrogate-pair strings; adds string as a fourth recognized type in a JIT fast path that bypasses the full JS iteration protocol, making it a new high-value target for JIT correctness bugs.
Audit directions
- Surrogate pair boundary correctness in
advance(). The shared core must correctly detect high/low surrogate pairs and advance the index by 2. Fuzz with surrogates at rope page boundaries, lone surrogates, surrogates at position 0 and length-1, and mixed BMP/surrogate strings. A one-off produces a string one code unit ahead of correct for all subsequent iterations. ObjectAllocationSinkingescape analysis. IfJSStringIteratorescapes (through a getter, aSymbol.iteratoroverride, or an effect DFG does not model) but sinking still eliminates the allocation, the engine reads stale/uninitialized data from the virtual iterator. VerifyDFGClobberizecorrectly marks all effects ofStringIteratorNext.- Tuple node pipeline consistency. Each pass — AbstractInterpreter, Clobberize, DoesGC, SafeToExecute, FixupPhase, FTL lowering — must agree on
StringIteratorNext's semantics. A mismatch (DoesGC returning false while fallback allocates, or SafeToExecute incorrectly speculating) can silently miscompile JIT-optimized loops. - 2-fast-mode cap and mode-demotion races. When a site is demoted from a specific fast mode to Generic after a third type, the LLInt/Baseline profiling metadata transitions in a brief window. Investigate whether a concurrent JIT compilation can snapshot the mode mid-transition and emit a guard that passes for the wrong type.