You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Splitting this out of #8159, which is closed by #8240: the narrowing question is answered, the 3.9% is not.
What is established
pipeline retired +3.95% instructions at 53d63aad2 (#8084) versus 8b1b4b909 (#8157) — measured in #8159, run ranges non-overlapping, 2x the noise floor, every other row flat.
#8159 attributed that to fix(codegen): root the callee across argument evaluation in three call arms. That is falsified. Three independent confirmations on 07c8040bf:
Sharpest suspect, hypothesis not measurement: js_implicit_this_set. It is pipeline's hottest FFI function — the IR has 4 call sites against 2 closure-call sites (a save and a restore per call), so ~2.9M dynamic calls per run (400 rounds x 900 records x 3 stages, plus idf). #8084 turned it from a bare TLS replace into this_set_check(value, …); replace; this_set_check(previous, …), and each check opens with a OnceLock::get_or_init — an atomic load plus a branch on the fast path, paid twice per call, for a diagnostic (PERRY_GC_THIS_SET_CHECK) that is default-off.
Back-of-envelope that is ~1%, not 3.9%, so at most part of it. But a default-off diagnostic on the hottest path is the shape CLAUDE.md's GC-knob policy exists to prevent, and it is cheap to test: one runtime rebuild, no codegen change. I had that experiment building when I stopped; it is not measured.
Given the IR result, bisect within#8084 by file rather than re-measuring the commit — the answer is known not to be in the part everyone assumed.
Two main-side reds found on the way
Both reproduce on clean 07c8040bf with no PR applied (both arms of the #8240 A/B report them identically). Both were invisible until #8207, because gc-root-dominance aborted earlier at --audit-poll-capable and never reached the corpus steps:
curated-native is red. One stale hazard against --max-stale 0: test_gap_class_forward_capture_6523::perry_closure_test_gap_class_forward_capture_6523_ts__7, unmasked->js_box_set, MOVING via js_object_get_field_by_name_f64. And seeded violations 39/40 caught, 1 MISSED, where the workflow requires all 40 — that second one is the "can the checker still fail" arm, so it matters more than the first.
dep-native reads unrooted 2, not the 3 the workflow budgets. A ratchet only ratchets if it tracks the actual value; one new unrooted site would currently land inside budget and go green. Not tightened in perf(codegen): compute the callee-rooting window instead of hardcoding it (#8159) #8240 on purpose — the number is LLVM-version dependent and was measured with homebrew opt, not CI's, so lowering a budget that cannot be verified in CI is how the gate goes red for the next person. Worth CI measuring and ratcheting.
dep-native itself is green and non-vacuous on clean main: 12,957 functions / 81 modules, 49,114 safepoints, seeded 40/40 caught, --self-test OK.
Splitting this out of #8159, which is closed by #8240: the narrowing question is answered, the 3.9% is not.
What is established
pipelineretired +3.95% instructions at53d63aad2(#8084) versus8b1b4b909(#8157) — measured in #8159, run ranges non-overlapping, 2x the noise floor, every other row flat.#8159 attributed that to
fix(codegen): root the callee across argument evaluation in three call arms. That is falsified. Three independent confirmations on07c8040bf:gc-handoff/apps/pipeline.tsbyte-identical —cmpreturns 0, not "within noise".js_closure_call1sites in that IR are preceded bycall i64 @js_closure_unbox_callee_checked(...), which onlylower_dynamic_closure_call(lower_call/console_promise.rs) emits. That is the GC: evacuating minor drops an old-to-young field[1] edge, crashing with 'value is not a function' #7154 lowering, which has rooted its callee and re-read its arguments below the unbox since long before gc: diagnostics, two rooting fixes, and the ungated corpus/lowering cell (#7803 investigation) #8084.js_new_function_constructandjs_closure_call_apply_with_spreadappear in the module only asdeclarelines. There is no call to either.Corpus-wide, #8240 measures
pipeline+0.02%,interp−0.04%,iso_miss−0.05%,asyncpipe+0.11%,shapes−0.18% — identical runtime archives on both arms, per-arm cache dirs,PERRY_NO_AUTO_OPTIMIZE=1, min-of-5, stdout sha-identical.So the commit attribution stands and the hunk attribution does not. The cost is in #8084's other ~2,500 lines.
Search space, ranked
Most of the remainder is GC runtime, which a program allocating ~1.4M objects exercises constantly:
gc/roots/stack_maps.rs+_decode.rs+_sections.rs— ~1,660 lines changed; runs on every root scangc/copying.rs(−306) +gc/copying_pointer_set.rs(+248) — the copying minor's pointer set, restructuredgc_map.rs(+355, codegen) — bigger/differently-shaped maps cost scan timegc/pin.rs(+209),gc/tenuring.rs(+41)object/this_binding.rs,object/native_call_method.rsSharpest suspect, hypothesis not measurement:
js_implicit_this_set. It ispipeline's hottest FFI function — the IR has 4 call sites against 2 closure-call sites (a save and a restore per call), so ~2.9M dynamic calls per run (400 rounds x 900 records x 3 stages, plusidf). #8084 turned it from a bare TLSreplaceintothis_set_check(value, …); replace; this_set_check(previous, …), and each check opens with aOnceLock::get_or_init— an atomic load plus a branch on the fast path, paid twice per call, for a diagnostic (PERRY_GC_THIS_SET_CHECK) that is default-off.Back-of-envelope that is ~1%, not 3.9%, so at most part of it. But a default-off diagnostic on the hottest path is the shape CLAUDE.md's GC-knob policy exists to prevent, and it is cheap to test: one runtime rebuild, no codegen change. I had that experiment building when I stopped; it is not measured.
Given the IR result, bisect within #8084 by file rather than re-measuring the commit — the answer is known not to be in the part everyone assumed.
Two main-side reds found on the way
Both reproduce on clean
07c8040bfwith no PR applied (both arms of the #8240 A/B report them identically). Both were invisible until #8207, becausegc-root-dominanceaborted earlier at--audit-poll-capableand never reached the corpus steps:stalehazard against--max-stale 0:test_gap_class_forward_capture_6523::perry_closure_test_gap_class_forward_capture_6523_ts__7,unmasked->js_box_set, MOVING viajs_object_get_field_by_name_f64. And seeded violations 39/40 caught, 1 MISSED, where the workflow requires all 40 — that second one is the "can the checker still fail" arm, so it matters more than the first.unrooted 2, not the3the workflow budgets. A ratchet only ratchets if it tracks the actual value; one new unrooted site would currently land inside budget and go green. Not tightened in perf(codegen): compute the callee-rooting window instead of hardcoding it (#8159) #8240 on purpose — the number is LLVM-version dependent and was measured with homebrewopt, not CI's, so lowering a budget that cannot be verified in CI is how the gate goes red for the next person. Worth CI measuring and ratcheting.dep-native itself is green and non-vacuous on clean main: 12,957 functions / 81 modules, 49,114 safepoints, seeded 40/40 caught,
--self-test OK.Refs #8159, #8084, #8240, #8207.