fix(gc): classify the emitted property-GET dispatch as poll-capable (#7154) - #7284
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe GC-root dominance checker now audits ChangesPoll-capable runtime audit
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant CI
participant gc_root_dominance_check
participant POLL_CAPABLE_RUNTIME
participant ExportedRuntimeSymbols
CI->>gc_root_dominance_check: invoke --audit-poll-capable
gc_root_dominance_check->>POLL_CAPABLE_RUNTIME: read runtime entries
gc_root_dominance_check->>ExportedRuntimeSymbols: validate emitted symbols
ExportedRuntimeSymbols-->>gc_root_dominance_check: return matches
gc_root_dominance_check-->>CI: report audit result
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…erryTS#7154) `--stale-registers` and `PERRY_GC_PROTECT_FROMSPACE=1 PERRY_GC_PROTECT_FROMSPACE_DEPTH=800` disagreed at zod's `clone`: the protector faulted deterministically, the checker classified the window `MOVING: no`, and the `--moving-only` arm `gc-root-dominance.yml` gates on dropped it. The checker was wrong. `POLL_CAPABLE_RUNTIME` is an exact-symbol set. It carried `js_object_get_field_by_name` next to `js_object_set_field_by_name`, which reads as symmetric coverage of property access and is not: codegen emits the SET verbatim and never emits the GET, lowering every read to `js_object_get_field_by_name_f64` / `js_object_get_field_ic_miss` / `js_typed_feedback_object_get_field_by_name_f64` (1324 / 532 / 556 calls in the gate corpus), none of which were in the set. 31 stale uses with a GET helper in their window classified `MOVING: no`. Auditing the set the way PerryTS#7227 audits ALLOC_RE found ten of twenty-eight entries naming symbols that do not exist, four of them four different spellings of "call a JS closure". Each is replaced by the emitted symbol carrying its premise rather than deleted. `--audit-poll-capable` is the gate for that, and a new `--self-test` fixture pair pins the property-GET window in both directions. Both audits and both `--moving-only` gate arms are green; the raw `--stale-registers` count is unchanged at 4860 and `--moving-only` moves 65 -> 115. The `gc-root-dominance.yml` step that runs `--audit-poll-capable` is held back to a follow-up commit: pushing a workflow change needs an OAuth token scope this session does not have.
3bd1342 to
235c66d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/src/internals/gc-rooting-invariant.md`:
- Around line 304-310: Update docs/src/internals/gc-rooting-invariant.md at
lines 304-310 and 171-221 to state that --audit-poll-capable CI wiring is
pending a follow-up rather than already running before the build; update
changelog.d/7284-poll-capable-property-get-and-audit.md at lines 65-76 likewise
so it does not claim a shipped gc-root-dominance.yml step. Preserve the claims
about --audit-alloc-re and the audit behavior itself.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 85f78f3b-9185-4843-8910-c23e2e6e6d76
📒 Files selected for processing (3)
changelog.d/7284-poll-capable-property-get-and-audit.mddocs/src/internals/gc-rooting-invariant.mdscripts/gc_root_dominance_check.py
Three verified bot findings folded into one commit: 1. policy.rs "sound by construction" overclaim (#7291 thread): the #7280 correction at line 2379 explicitly says the deferral is NOT "sound by construction" because runtime Rust frames need explicit rooting, but the comment at line 2474 still called the same deferral "sound by construction." Aligned it to "makes the collection point precise," matching the corrected framing. 2. gc-rooting-invariant.md "collection #0" imprecision (#7281 thread): a runtime cache populated after earlier collections goes bad at the first moving collection after population, not at collection #0. Reworded to state that precisely. 3. 15 untagged TypeScript doc fences (#7281 thread): perry-doc-tests --lint reported untagged ts/typescript fences across docs/src. Tagged each as typescript,no-test. Linter now reports "docs/src ok." Refuted (no change needed): - #7284 thread claimed docs overclaim --audit-poll-capable CI wiring, but the workflow step was included in the PR squashed commit and is present on main; the docs are accurate. - #7281 thread 3 claimed the static-checker scope was unlimited, but the doc already says "Scope: emitted-LLVM rooting hazards only."
The adjudication
Two of the effort's GC instruments disagreed at zod's
clone.PERRY_GC_PROTECT_FROMSPACE=1 PERRY_GC_PROTECT_FROMSPACE_DEPTH=800faulteddeterministically;
scripts/gc_root_dominance_check.py --stale-registersclassified the same window
MOVING: no, so the--moving-onlyarmgc-root-dominance.ymlgates on could not see it.The checker was wrong.
POLL_CAPABLE_RUNTIMEis an exact-emitted-symbolset, and it carried
js_object_get_field_by_namenext tojs_object_set_field_by_name— which reads as symmetric coverage of propertyaccess and is not. Codegen emits the SET verbatim (27 sites in perry-codegen,
205 in the gate corpus) and never emits the GET at all: every property read
lowers to
js_object_get_field_by_name_f64,js_object_get_field_ic_missorjs_typed_feedback_object_get_field_by_name_f64— 1324 / 532 / 556 calls in thesame corpus — and not one was in the set. Property sets classified
MOVING: YES, property gets classifiedMOVING: no. That asymmetry is what put"all five violations the gate reports are
MOVING: YES via js_object_set_field_by_name" in the rooting-invariant doc and left the wholeGET side unread. 55 stale uses on the gate corpus have an emitted GET helper
in their window; 31 of them were dropped by
--moving-only.Auditing the set the way #7227 audits
ALLOC_REthen found a second, largerhole: ten of twenty-eight entries named symbols that do not exist
(
js_apply_function,js_array_for_each,js_array_sort,js_call_closure,js_call_value,js_function_call,js_invoke_closure,js_object_get_property,js_object_set_property,js_string_replace). Fourof the ten were four different spellings of "call a JS closure" — the most
obviously poll-capable operation in the language, covered zero times — while
RECEIVER_SINKSthree hundred lines away in the same file already spelled itclosure_call\w*. Each phantom is replaced by the emitted symbol carryingits premise, never merely deleted; deleting turns the audit green and leaves the
hole.
Full reach proofs, per symbol, are in the block comment on
POLL_CAPABLE_RUNTIMEand inchangelog.d/7284-….The two hypotheses that were not the answer
heap_source_kindgenuinely has no incoming-parameter source kind, and thatis a real gap — but not this one. Perry spills and binds parameters, so the
re-read is a
slotloadthe checker already models. Measured: 1708 paramsspilled, 256 not; of the 256, exactly 4 are used below a collecting call and
all 4 are
double/i64arithmetic in typed-f64 specialisations, none apointer. Needs its own change with its own hit count. The new fixture passes
its receiver and key as bare parameters so the shape is on the record.
DEPTH=800. Structurally impossible. A page-setenters the quarantine only because an evacuating minor retired it as
from-space (
arena/quarantine.rs; the knob gatescopying_reset_from_spaces_and_flipand nothing else), so a fault on aquarantined address is a genuine stale read at any depth. Eviction hands
blocks back to Eden, where the same read silently succeeds — depth removes
false negatives and cannot manufacture a false positive. The doc now says so.
Verification
No Rust changed, so
cargo testis untouched by construction. Corpus: 141.ll, 2321 functions, 3068 root stores, built from this worktree at97c69211d.97c69211dpoll_reaching--stale-registers, raw--stale-registers --moving-only--stale-registers --fatal-sinks--stale-registers --moving-only --fatal-sinks--moving-only(the gate)--unrooted-allocas --moving-only(the gate)Raw counts do not move: this reclassifies windows, it does not widen what counts
as a heap-value source.
The gate can now fail on the shape
New
--self-testarm, red against the parent's set and green against this one:And the gate's own command over a corpus mutant that splices exactly one
js_object_get_field_ic_missinto one real allocation-to-root-store gap:--moving-only --allowlist … --seeded-violations 40over the mutant corpusComplementary direction on the unmutated corpus: reseeding
--seeded-violations' spliced call withjs_object_get_field_ic_missinsteadof
js_call_functiongives40 planted, 0 caught, 40 MISSEDon the parent and40 planted, 40 caughthere.All six of the workflow's checker invocations exit 0 on this branch:
--self-test,--audit-alloc-re,--audit-poll-capable,--audit-immovable-sources, and both--moving-onlycorpus arms.Follow-up needed (not in this PR)
--audit-poll-capableships as a mode but is not yet wired into.github/workflows/gc-root-dominance.yml— pushing a workflow change needs anOAuth
workflowscope this session did not have. The step is a two-lineaddition next to the
--audit-alloc-restep and should land before this isrelied on as a gate; until then the mode is a command, not a check.
Summary by CodeRabbit
Documentation
Bug Fixes
Tests