Summary
Perry treats every call as a GC safepoint: rewrite-statepoints-for-gc wraps each non-leaf call in a gc.statepoint and relocates every live root across it. That is correct (any callee might collect) but it makes the safepoint count of a function equal to its call count, which is the × safepoints term in the RS4GC relocation fan-out live_roots × safepoints behind #8583 (the Claude Code @main has ~106k call sites).
Many production GC'd VMs instead use safepoint polling: collection can only begin at a bounded set of poll points — loop back-edges, function returns/entries, and allocation sites — and ordinary calls are not safepoints unless the callee itself polls. This shrinks the safepoint set dramatically without losing the guarantee that a collection can always make progress, because a thread reaches a poll in bounded time.
Proposal (exploratory — needs design)
Move from "every call is a safepoint" toward a polling model:
- take safepoints at loop back-edges, allocation sites, and returns, rather than at every call;
- a non-allocating / non-polling call is a
gc-leaf-style edge that does not relocate roots;
- keep a mechanism to bring a thread to a safepoint on demand (poll flag the callee checks), which perry already has pieces of (
GC_SAFEPOINT_PENDING, js_gc_loop_safepoint, the loop-poll path).
Why it's worth it
Unlike #8595 (outlining), which fixes the giant-function symptom, reducing safepoint density shrinks the × safepoints factor for every function in the codebase, not just the outlier — lowering RS4GC cost and stack-map size across the board, and reducing the number of points at which roots must be kept live (better register allocation).
Scope / risk
This is the largest of the three and a genuine GC-architecture change: it affects collection latency/progress guarantees, the interaction with the moving scavenge (#7019), write barriers, and the conservative-scan fallback. It should be scoped and prototyped carefully — filed here so the option is tracked rather than lost, not as ready work.
Context: surfaced while fixing #8583 (RS4GC relocation fan-out). Complements #8595 (outlining) and the root-spilling stopgap in #8583. Root-lowering background: #7370 (native roots default), #7280/#7173/#7174.
Summary
Perry treats every call as a GC safepoint:
rewrite-statepoints-for-gcwraps each non-leaf call in agc.statepointand relocates every live root across it. That is correct (any callee might collect) but it makes the safepoint count of a function equal to its call count, which is the× safepointsterm in the RS4GC relocation fan-outlive_roots × safepointsbehind #8583 (the Claude Code@mainhas ~106k call sites).Many production GC'd VMs instead use safepoint polling: collection can only begin at a bounded set of poll points — loop back-edges, function returns/entries, and allocation sites — and ordinary calls are not safepoints unless the callee itself polls. This shrinks the safepoint set dramatically without losing the guarantee that a collection can always make progress, because a thread reaches a poll in bounded time.
Proposal (exploratory — needs design)
Move from "every call is a safepoint" toward a polling model:
gc-leaf-style edge that does not relocate roots;GC_SAFEPOINT_PENDING,js_gc_loop_safepoint, the loop-poll path).Why it's worth it
Unlike #8595 (outlining), which fixes the giant-function symptom, reducing safepoint density shrinks the
× safepointsfactor for every function in the codebase, not just the outlier — lowering RS4GC cost and stack-map size across the board, and reducing the number of points at which roots must be kept live (better register allocation).Scope / risk
This is the largest of the three and a genuine GC-architecture change: it affects collection latency/progress guarantees, the interaction with the moving scavenge (#7019), write barriers, and the conservative-scan fallback. It should be scoped and prototyped carefully — filed here so the option is tracked rather than lost, not as ready work.
Context: surfaced while fixing #8583 (RS4GC relocation fan-out). Complements #8595 (outlining) and the root-spilling stopgap in #8583. Root-lowering background: #7370 (native roots default), #7280/#7173/#7174.