Skip to content

gc-root-dominance: 7/3 gated (8/4 unfiltered) NEW root-dominance regressions landed 2026-08-15..25 while the gate was red #8809

Closed

Description

@proggeramlug

scripts/gc_root_dominance_check.py reports 7 violations across 4 distinct fingerprints on clean main (db821b5fd), against an allowlist whose entries list is empty. CLAUDE.md states every new hit is a red build, so this is either a real regression or the gate is currently red on main.

test_gap_array_iterator_manual_next…::main::js_closure_alloc->js_object_set_field_by_name
test_gap_gc_method_receiver_rooting…::perry_fn_…__make::js_closure_alloc->js_object_set_field_by_name
test_gap_object_create_method_this…::main::js_closure_alloc->js_object_set_field_by_name
test_gap_class_advanced…::main::js_object_alloc_class_inline_keys_stamped->js_private_brand_add

Shape of the first three (all identical):

alloc  : %rN = call i64 @js_closure_alloc(...)
store  : store i64 %rM, ptr %r19
bind   : slot 9  call void @js_shadow_slot_bind(i32 9, ptr %r19)
between: js_object_set_field_by_name
MOVING : YES via js_object_set_field_by_name

i.e. the #7192 shape — the root store is emitted in-frame but after a call that can allocate.

Reproduce

cargo build --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static
PERRY_BIN=<target>/perry-dev/perry PERRY_RUNTIME_DIR=<target>/perry-dev \
  ./scripts/gc_root_dominance_corpus.sh /tmp/rdcorpus
python3 scripts/gc_root_dominance_check.py /tmp/rdcorpus; echo $?    # -> 1

Caveat on the measurement

Measured with a perry-dev binary; gc-root-dominance.yml uses release, and it passes flags this invocation did not (--moving-only, --min-binds, --min-funcs, plus its env knobs). So the absolute count may not match CI's. Worth confirming at release profile before treating the count as authoritative — but the violations are real IR at the profile measured.

Found while auditing #8783; an identical A/B showed that PR introduces none of these.

Activity

  1. proggeramlug commented on Aug 25, 2026

    @proggeramlug
    ContributorAuthor

    Correcting the counts in this issue, and re-dating the finding.

    Two corrections to my original report.

    1. "7 violations across 4 fingerprints" is a pairing that never occurs

    The count depends on which filter you run, and I mixed them:

    invocation violations fingerprints
    --moving-only (what gc-root-dominance.yml actually gates on) 7 3
    unfiltered (the reproduce command I gave above) 8 4

    The 3 gated fingerprints are all js_closure_alloc -> js_object_set_field_by_name: object_create_method_this::main (×5), array_iterator_manual_next::main (×1), gc_method_receiver_rooting::…__make (×1). The 4th, class_advanced::main::js_object_alloc_class_inline_keys_stamped -> js_private_brand_add, prints MOVING : no and is therefore dropped by --moving-only — but it is a genuine bug, not a false positive.

    Correct wording is 7/3 gated, 8/4 unfiltered. Measured at release profile with the workflow's exact flags, so the earlier "dev-profile only, confirm at release" caveat is discharged: the violations are real at the profile that matters.

    2. These are NEW regressions, not pre-existing debt

    I said these were pre-existing on main. They are not. The last green gc-root-dominance run on main is 31870805900 (2026-08-15T06:59Z) with 144/144 sources compiled and === violations: 0. The gate has been red at --audit-poll-reach since 2026-08-15T13:00Z (see #8821, which I have also corrected), so both regressions landed inside a 10-day window where the gate could not report.

    Both are #7192-shaped — root store emitted after a call that can allocate:

    • perry-codegen/src/expr/object_literal.rs::lower_by_name_props — lowers a this-capturing method closure, installs it via js_object_set_field_by_name, and only then pushes its root. The object handle is re-read from its root; the closure is not. The installer can run a user setter or Proxy trap, and the closure is reachable from the object by then, so an evacuating minor moves it — the push publishes a moved-from address. Arm became reachable from TypeScript via perf(hir): lower static method literals directly #8793 (landed in perf: static method literals, captured closure reuse, argument shape facts #8796, 2026-08-25).
    • perry-codegen/src/lower_call/new.rs::construction_runs_user_code — returns false for a class whose only private elements are methods/accessors, while emit_field_inits still emits js_private_brand_add for it (merge: land #8630 (class semantics tail) with six audit fixes #8643, 2026-08-23). That helper calls js_object_set_field_by_name, and its own body notes the marker-key allocation "can evacuate both the receiver and any live value".

    Note the first of these landed via a PR I merged earlier today. The gate that would have caught it was red, and is label-gated so it would have been skipped on the PR regardless.

  2. changed the title [-]gc-root-dominance: 7 violations / 4 fingerprints on clean main against an empty allowlist[/-] [+]gc-root-dominance: 7/3 gated (8/4 unfiltered) NEW root-dominance regressions landed 2026-08-15..25 while the gate was red[/+] on Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions