[JSC] GreedyRegAlloc: Add loop-aware live range splitting (disabled by default)
JSTests/stress/regalloc-loop-splitting.js
B3 is JSC's optimizing JIT compiler; Air is its low-level assembly IR where physical register assignment happens. The greedy register allocator assigns physical registers to virtual registers (tmps) by processing live ranges in priority order; when a tmp cannot be allocated, it is either spilled or split into independently-allocatable sub-ranges. Loop-aware splitting frees a register held across an entire loop but unused inside it, by splitting the live range and inserting Shuffle-based fixup at loop entry/exit.
This commit adds loop-aware live range splitting to JSC's greedy register allocator. A new CFG normalization pass (ensureDedicatedLoopEntryExitBlocks) ensures safe insertion of Shuffle-based fixup code at loop entry and exit edges; the feature is gated behind airGreedyRegAllocSplitAroundLoops and disabled by default. Air's Shuffle instruction handles parallel moves including cycles (e.g., A↔B swap). The commit explicitly states that the splitting policy and integration with the allocator's priority-driven allocation order are incomplete.
Significance
Shuffle-based parallel-move fixup at loop entry/exit must resolve register cycles correctly; a bug in cycle resolution produces silent value corruption in JIT output rather than a crash.
Audit directions
- Shuffle cycle resolution at loop boundaries. When multiple tmps split around the same loop swap physical registers, parallel Shuffle must handle cycles correctly. Cycle resolution is notoriously subtle; bugs here produce silent value corruption.
- CFG normalization edge cases.
ensureDedicatedLoopEntryExitBlocksinserts new basic blocks at loop entry and exit. Problematic cases: loops with multiple back-edges, irreducible loops, loops whose header is also the only exit, and interactions with OSR entry/exit. - Acknowledged-incomplete allocation ordering. The commit explicitly states that splitting policy and priority-driven allocation order are incomplete. Adversarial inputs can make ordering decisions conflict with split-range assumptions.
rewriteCoalescedTmpsandaddSplitTmpuse/def coverage. Every use and def of the original tmp must be rewritten to reference the correct sub-range. A missed rewrite at a def site that liveness analysis treats as a boundary corrupts the register assignment silently.trySplitAroundClobbersinteraction with loop splitting. When both clobber-splitting and loop-splitting apply to the same tmp, their priority and fixup-block ordering is a potential source of conflict.