perf(gc): Date cells are movable — no longer pin old-gen defrag pages (#6186)#6214
Conversation
Date/Temporal were non-movable, so a promoted long-lived Date pinned its old-gen page from defrag. The movable flag ONLY gates old-page defrag (gc_type_is_movable, oldgen.rs) — the nursery copied-minor already evacuates eden objects regardless of it — and old-page defrag runs only inside moving collections (the precise pump-boundary safepoint, stack unwound), the same collections at which the nursery already evacuates Dates. So flipping the flag is safe by the identical invariant that already makes movable Promises sound. Date gets the ExoticExpandoOwner move hook (exactly like Promise) so d.foo=... expandos rekey on relocation. Temporal left non-movable (its embedded temporal_rs value + TemporalCleanup finalize are a larger, separate change). VERIFY_EVACUATION clean (no new stale-forwarded refs); full suite green.
📝 WalkthroughWalkthroughThe GC metadata now allows Date cells to move and invokes the exotic-expando relocation hook. A regression test verifies that minor collection evacuates a rooted Date, preserves its timestamp, migrates its expando, and clears the stale address. ChangesDate cell GC relocation
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/perry-runtime/src/gc/types.rs (1)
469-491: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUpdate the Date move-hook docs
Dateis now movable and rekeyed viaGcMoveHookKind::ExoticExpandoOwner, so the stale “Date / Temporal are non-movable” wording incrates/perry-runtime/src/object/exotic_expando.rsand theDateCelldoc incrates/perry-runtime/src/date.rsshould be split to keep Date separate from the still-non-movable Temporal/RegExp cases.🤖 Prompt for 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. In `@crates/perry-runtime/src/gc/types.rs` around lines 469 - 491, Update the Date move-hook documentation to reflect that Date is now movable and rekeyed by GcMoveHookKind::ExoticExpandoOwner. In exotic_expando.rs, split the stale “Date / Temporal are non-movable” wording so Date is documented separately from the still-non-movable Temporal and RegExp cases, and in date.rs update the DateCell doc/comments to match the new movable behavior and expando rekeying.
🤖 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.
Nitpick comments:
In `@crates/perry-runtime/src/gc/types.rs`:
- Around line 469-491: Update the Date move-hook documentation to reflect that
Date is now movable and rekeyed by GcMoveHookKind::ExoticExpandoOwner. In
exotic_expando.rs, split the stale “Date / Temporal are non-movable” wording so
Date is documented separately from the still-non-movable Temporal and RegExp
cases, and in date.rs update the DateCell doc/comments to match the new movable
behavior and expando rekeying.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6ae62d1a-8459-4fdb-a453-cef1b0c4b1d5
📒 Files selected for processing (2)
crates/perry-runtime/src/gc/tests/copying/survival_and_malloc.rscrates/perry-runtime/src/gc/types.rs
Implements #6186 (Wave 3 of the 2026-07-09 GC audit). This supersedes my earlier "verified unsafe" comment on the issue — a complete moving-path enumeration showed it's actually safe; details below.
Why it's safe (the enumeration that corrected the earlier verdict)
The concern was: a moving collection running while a Date sits in an un-shadow-rooted
f64stack local would leave that local dangling. Tracing every path:movableflag only gates old-page defrag. Its sole consumers areoldgen.rs:1640/1746. The nursery copied-minor (move_young/mark_addr, copying.rs) evacuates eden objects checking onlyPINNED/FORWARDED— nevermovable. So non-movable Dates in the nursery are already evacuated at every pump-boundary moving minor today; the flag only decides whether a promoted, old-gen Date can be evacuated by defrag.gc_safepoint_moving_minor— the pump boundary (microtasks.rs, stack unwound) and, under the non-default moving-loop compiler opt-in, loop back-edges (a precise-root safepoint by construction). These are the same collections at which the nursery already evacuates Dates safely.globalThis.gc()forces the conservative scan and runs a full non-moving mark-sweep (policy.rsmanual_gc_collect_now), and the raw movinggc_collect_minor()is#[cfg(test)]-only. So no production moving path runs with an active JS frame holding an un-shadow-rooted Date local.⇒ Flipping
movableis safe by the identical invariant that already makes movablePromises sound — Promise is a movable arena object with the same un-shadow-rooted-local situation, and it moves fine at these safepoints.The change
GC_TYPE_DATE_CELL:movable: false → true, andGcMoveHookKind::None → ExoticExpandoOwner— Date'sd.foo = …expandos live inobject::exotic_expandokeyed by address, rekeyed on relocation exactly like Promise's (exotic_expando_owner_moved).temporal_rsvalue +TemporalCleanupfinalize hook make it a larger, separate change; scoped out.Verification
ts, and stays a valid Date; asserts the movable flag flipped.perry-runtimelib suite: green.PERRY_GC_VERIFY_EVACUATION=1: no new stale-forwarded-ref panics (only the 2 pre-existing exact-count failures that also fail on origin/main).new Date()in a loop withd.seqexpandos, some promoted to old-gen, a fullgc()forcing moves — afterward all kept Dates are validinstanceof Datewith correctgetTime(), intact expandos, and working</arithmetic.Reference: #6186; 2026-07-09 GC audit
fable-audit-perry-gc.md.Summary by CodeRabbit
Bug Fixes
Tests