Skip to content

perf(runtime): #6759 C3c-r — header-stamped stable shape ids, id-keyed FIELD_CACHE, PIC ABA fix#6803

Merged
proggeramlug merged 3 commits into
mainfrom
perf/6798-c3c-header-shape-id
Jul 22, 2026
Merged

perf(runtime): #6759 C3c-r — header-stamped stable shape ids, id-keyed FIELD_CACHE, PIC ABA fix#6803
proggeramlug merged 3 commits into
mainfrom
perf/6798-c3c-header-shape-id

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Phases C3c-r (+ the C3b consumer that's sound today) of #6759 / #6798. Stacked on #6802 (C5a). Runtime-only — the codegen PIC remainder is explicitly deferred to its own review, per the C3 gate.

What changed

  • Process-global ShapeId allocator in a dedicated range (0x8000_0000..0xC000_0000), superseding C3a's per-thread counter. Two properties are load-bearing: the range is disjoint from every real and builtin class id (a stamp can never be mistaken for inheritance data), and ids are globally unique + never reused — the worker serializer replays parent_class_id verbatim, so a deep-copied object's stamp arriving on another thread must never alias that thread's ids. Exhaustion (2^30 shape births) parks the allocator and degrades to no-acceleration, never to reuse.
  • Header-resident stamps for plain objects: class_id == 0 objects carry their shape id in the otherwise-dead parent_class_id word — stamped lazily when a read resolves through the slow path, cleared exactly when the invariant would break: a keys-pointer change (set_object_keys_array, pointer-change only — in-place appends keep the stamp because slots are append-only) and in-place delete compaction (next to the existing eager shape_drop).
  • FIELD_CACHE keys on the stamp (falling back to the keys address for unstamped/class receivers): entries now survive owned grow-reallocs AND GC moves of the keys array, both of which previously orphaned them. Same validated-hit trust model — a colliding or foreign key can only miss.
  • PIC ABA fix: js_object_get_field_ic_miss now primes the per-site cache only for GC_FLAG_SHAPE_SHARED keys arrays (literal shapes and class-keys arrays — both shape-cache-resident, process-rooted, address-immortal). An OWNED keys array can die and have its address recycled under a different shape, and the emitted PIC hit path is the one unvalidated compare-and-load in the system — a stale hit reads the wrong slot. Owned/wide receivers are served by the (validated, now id-stable) FIELD_CACHE instead.

Explicitly deferred (with rationale in docs/shape-tree-plan.md)

  • typed_feedback::object_shape() keeps the keys-address token: lazy stamping would split one logical shape into two tokens per site (pre-stamp vs post-stamp observations) and read as spurious polymorphism. Canonicalizing on ids needs eager stamping at allocation — the codegen-assisted remainder.
  • The PIC comparing header ids needs a discriminated compare (the generic PIC also serves class instances, whose parent_class_id is real inheritance data) — same rung.
  • The doc also records C4's status: its concrete goals are subsumed by C1+C3a+C3c-r; the residual formalization is deferred until profiling justifies it.

Validation

Full cargo test -p perry-runtime --lib -- --test-threads=1 green (1450 passed), including three new tests: id range/stability/disjointness, the end-to-end stamp lifecycle on a real object through the real write/read paths, and the PIC shared-only predicate.

Summary by CodeRabbit

  • Performance
    • Improved property lookup performance and stability for plain objects as their structure evolves.
    • Enhanced caching so field lookups remain correct across internal keys-array growth and garbage-collection movement.
  • Bug Fixes
    • Prevented stale cache results after property deletion/compaction and object keys-array changes.
    • Reduced incorrect lookups caused by recycled internal memory addresses.
  • Tests
    • Added new coverage for stable shape-id allocation and cache/stamp lifecycle behavior.
  • Documentation
    • Updated the shape-tree roadmap with a staged plan for rolling out shape identifiers and caching improvements.

proggeramlug pushed a commit that referenced this pull request Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b2ea5320-c3ee-4ad7-88e7-53865cb83c69

📥 Commits

Reviewing files that changed from the base of the PR and between 647018a and c054111.

📒 Files selected for processing (8)
  • changelog.d/6803-header-shape-id-c3c-r.md
  • crates/perry-runtime/src/object/delete_rest.rs
  • crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
  • crates/perry-runtime/src/object/field_get_set/ic_miss.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/shapes.rs
  • crates/perry-runtime/src/typed_feedback.rs
  • docs/shape-tree-plan.md

📝 Walkthrough

Walkthrough

The runtime now allocates process-global stable shape IDs, stamps plain-object headers, keys field caches by shape identity, invalidates stale stamps after mutations, and restricts PIC priming to shape-shared keys arrays. Tests, changelog text, and shape-tree documentation are updated.

Changes

C3c-r runtime shape identity

Layer / File(s) Summary
Global shape identity and stamping
crates/perry-runtime/src/object/shapes.rs, crates/perry-runtime/src/object/mod.rs, crates/perry-runtime/src/object/typed_feedback.rs, docs/shape-tree-plan.md, changelog.d/...
Shape IDs use a process-global range, plain objects persist valid IDs in their headers, and tests and documentation describe the updated lifecycle.
Shape-keyed field cache lifecycle
crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs, crates/perry-runtime/src/object/delete_rest.rs
Field-cache lookup and writeback use stable shape keys, while compaction clears stale plain-object shape stamps.
PIC priming safety checks
crates/perry-runtime/src/object/field_get_set/ic_miss.rs
PIC priming is limited to keys arrays marked shape-shared, with coverage for owned and shared arrays.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ObjectLookup
  participant ShapeTable
  participant FieldCache
  participant PICMiss
  ObjectLookup->>ShapeTable: Ensure stable shape ID
  ShapeTable-->>ObjectLookup: Return shape ID
  ObjectLookup->>FieldCache: Lookup by shape key
  FieldCache-->>ObjectLookup: Return field index
  PICMiss->>PICMiss: Validate shape-shared keys array
  PICMiss->>FieldCache: Prime PIC entry
Loading

Possibly related issues

  • PerryTS/perry issue 6804 — Covers the remaining code-generation work that builds on this runtime C3c-r implementation.
  • PerryTS/perry issue 6798 — Tracks the broader C3c-r stable shape-ID phase implemented here.
  • PerryTS/perry issue 6759 — Covers the Phase C shape-tree architecture advanced by these shape and cache changes.

Possibly related PRs

  • PerryTS/perry#6801 — Adds the monotonic shape-ID infrastructure and keys-array migration support used by these runtime caching changes.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the runtime shape-id, FIELD_CACHE, and PIC ABA changes.
Description check ✅ Passed The description covers summary, concrete changes, deferred work, and validation, though the template's Related issue/Test plan sections are not fully filled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/6798-c3c-header-shape-id

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@proggeramlug
proggeramlug force-pushed the perf/6798-c5a-inline-guard-per-key branch from 3340446 to 0043552 Compare July 22, 2026 19:44
Base automatically changed from perf/6798-c5a-inline-guard-per-key to main July 22, 2026 19:45
@proggeramlug
proggeramlug force-pushed the perf/6798-c3c-header-shape-id branch from 647018a to c054111 Compare July 22, 2026 19:45
@proggeramlug
proggeramlug merged commit f7d1988 into main Jul 22, 2026
27 of 29 checks passed
@proggeramlug
proggeramlug deleted the perf/6798-c3c-header-shape-id branch July 22, 2026 19:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/perry-runtime/src/object/delete_rest.rs (1)

384-391: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Redundant clear — already handled by set_object_keys_array, and the comment is inaccurate.

set_object_keys_array(obj, keys_cloned) is called earlier in this same function (Line 320) with a genuinely different pointer (keys_cloned is a fresh allocation, never the same address as keys). Its own pointer-change check in mod.rs ((*obj).keys_array != keys_array && shapes::is_shape_id(...)) already clears parent_class_id at that point, so by the time this block runs, (*obj).parent_class_id is already 0 and is_shape_id(0) is always false — this is dead code. The comment also states the compaction happens "under the SAME keys address," which contradicts the clone-based approach documented just above (Lines 298-304: "clone the keys_array before mutating it").

Either remove this block, or if it's intended as defense-in-depth against a future change to set_object_keys_array, fix the comment to say so instead of claiming a same-address scenario that doesn't occur here.

♻️ Proposed cleanup
-        // `#6759` C3c: the compaction changed the layout under the SAME keys
-        //    address, so the stamped shape id no longer describes this
-        //    object. Ids are never reused, so clearing here (plus the
-        //    record drop above) makes every stale id-keyed cache entry a
-        //    permanent miss; the next resolve stamps a fresh id.
-        if (*obj).class_id == 0 && crate::object::shapes::is_shape_id((*obj).parent_class_id) {
-            (*obj).parent_class_id = 0;
-        }
🤖 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/object/delete_rest.rs` around lines 384 - 391,
Remove the redundant class_id/parent_class_id clearing block and its inaccurate
comment from the function containing the earlier set_object_keys_array(obj,
keys_cloned) call; that helper already handles the pointer-change case, so no
additional defense-in-depth logic is requested.
🤖 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/object/delete_rest.rs`:
- Around line 384-391: Remove the redundant class_id/parent_class_id clearing
block and its inaccurate comment from the function containing the earlier
set_object_keys_array(obj, keys_cloned) call; that helper already handles the
pointer-change case, so no additional defense-in-depth logic is requested.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d9578a2-00e6-44d6-857a-c9a648e61b6d

📥 Commits

Reviewing files that changed from the base of the PR and between 3340446 and 647018a.

📒 Files selected for processing (8)
  • changelog.d/6803-header-shape-id-c3c-r.md
  • crates/perry-runtime/src/object/delete_rest.rs
  • crates/perry-runtime/src/object/field_get_set/get_field_by_name_tail.rs
  • crates/perry-runtime/src/object/field_get_set/ic_miss.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/shapes.rs
  • crates/perry-runtime/src/typed_feedback.rs
  • docs/shape-tree-plan.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant