[JSC] Inline string === "constant"
Component: JSC JIT | 8bd3c1b
JSTests/stress/string-strict-equal-short-constant-stale-8bit-substring.js
FTL is JSC's top-tier JIT, lowering DFG IR to B3, then Air, then machine code. A JSString may be a rope (concatenation or substring) or resolved to a StringImpl that is either 8-bit (Latin-1) or 16-bit (UTF-16), and JSString caches an is8Bit flag; atomizing a string can swap its StringImpl for an equal atom of a different width.
FTL now inlines string === "constant" when the constant is 8-bit and at most 16 chars: it checks length, loads the string's data, and compares one or two slices as BitOr(BitXor(slice0, c0), BitXor(slice1, c1)). A new ARM64 B3 lowering rule turns that XOR/OR shape into a cmp/ccmp chain. Stress tests cover keyword-style comparisons, 8- and 16-bit sources, and the stale-8-bit substring case above.
CompareStrictEq(str, K) (K 8-bit, <= 16 chars)
├─► not a cell / not string ─► slow path
├─► length != len(K) ─► false
├─► resolve + 8-bit check ─► slow path
└─► load slice(s): BitOr(BitXor(s0,k0), BitXor(s1,k1)) == 0
└─► ARM64 B3 lowering: cmp + ccmp chain
Significance
Short-constant compares are common in parsers, tokenizers and keyword checks, and inlining removes the generic string-equality call from those hot paths. The new ARM64 cmp/ccmp lowering is not string-specific: any B3 code producing the same XOR/OR compare shape now takes it.
Audit directions
- Representation drift under raw-byte fast paths. The stale-8-bit test shows the invariant is fragile: a substring rope caches its base's 8-bit flag while property-key atomization can swap in a 16-bit impl. Enumerate every way the cached flag or length can disagree with real storage (ropes, substrings, atomization, resolution timing), and check whether the new code consults the flag or the actual impl.
- Read extent when the check is wrong. If 16-bit data is read as 8-bit, compare bytes touched against allocation size, especially for the two overlapping slice loads at lengths 9-16; probe the empty string, 1-8, 9-16 and the 16/17 cutoff, and confirm the fast path guards on a resolved
JSStringbefore loading data. - Shared B3 lowering rules. Check flag and condition-code handling, shared leaves (
testXorOrCompareChainSharedLeaf32), immediate encodings, and ZExt/32- vs 64-bit widths — a wrong match could miscompile unrelated B3 code. Also confirm OSR exit and Phi state stay correct when the fast path falls back.