Skip to content

fix(runtime): seeded static literal shapes hold key atoms, so megamorphic reads confirm again (#11653 regression) - #11673

Merged
proggeramlug merged 3 commits into
mainfrom
fix-static-id-megamorphic-confirm
Sep 29, 2026
Merged

proggeramlug merged 3 commits into
mainfrom
fix-static-id-megamorphic-confirm

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Fixes a regression from #11653 (link-time static ShapeIds): megamorphic reads on object-literal receivers stopped hitting the confirm and fell back to walking the keys. lead_mega1 went from 225.8 to 435.8 instructions per iteration.

Root cause

  • When seeding runs: canonical_keys_for_names (object/static_shapes.rs, used by js_shape_seed_plain) builds the seeded literal shapes' canonical key lists right after js_gc_init, before any module's string pool has created its key atoms.
  • What canonicalization does: Appended::atomized swaps in an atom only if one already exists. At seed time none did, so every seeded key list held its own private strings.
  • Why it misses: the megamorphic confirm (slot_guess_confirmed) compares the listed key's pointer against the site's key, which is the pool's atom. It never matched.

Fix

canonical_keys_for_names creates each name's atom through js_string_pool_atom before building the list. It covers exactly the names a pool would atomize: non-empty, valid UTF-8, at most INTERN_MAX_BYTE_LEN bytes. The pool later finds that atom, and the canonical key list holds it.

Results (instructions per iteration, median of 3, outputs identical to node)

fixture before #11653 (10ece99) main b0bf0ae this PR
lead_mega1 225.81 392.87 182.87
lead_mega 356.81 446.68 313.87
lead_poly4 148.00 104.49 104.50
lead_lit_ctl 81.99 39.99 39.99

tsc +0.1% and Zod −0.17%, both within noise. The static-id census is unchanged.

Tests

  • a_seeded_literal_shape_answers_the_megamorphic_confirm_like_a_minted_one (runtime): fails without the fix.
  • crates/perry/tests/megamorphic_static_literal_confirm.rs: lead_mega1 must stay ≤ 260 instructions per read. It fails at 392.9 without the fix. It needs a perf counter, so it skips under CI when none is available; the runtime test is the deterministic guard.

Verification

runtime 4809/0 (serial); codegen 2336/0; the static-shape e2e tests pass; gc-root-dominance 40/40 seeded, stale 0; gc_call_effects --check, wasm abi, fmt and file size pass.

Possible follow-up (pre-existing, not this regression): class key lists are built at their module's init and could miss atoms in the same way; the fix would be the same atom creation in build_longlived_keys_array.

Summary by CodeRabbit

  • Bug Fixes
    • Improved property-read performance for literal objects with many different shapes. Reads can now confirm the expected property slot rather than falling back to a slower name-based lookup, reducing the work required for these cases.

Ralph Küpper added 2 commits September 29, 2026 14:38
…he megamorphic confirm answers it

The megamorphic read confirms its slot guess by one pointer compare of the
key listed at the guessed position against the site's key: the pool's atom
for that text (#11633). Since link-time ShapeIds (#11653) every literal with
a static id gets its record and keys list from the seed unit, which runs in
each agent right after js_gc_init, before any module's string pool. The
list's canonical copy stores the atom of each key only when one exists, and
none did yet, so a seeded list held its own strings: no read site ever
passes those, every confirm against a seeded literal missed, and each
megamorphic read of one fell to ic_slow_body and the by-name walk
(lead_mega1 225.8 -> 435.8 instructions per read).

canonical_keys_for_names now mints the atom of each name first, exactly as
the pool mints it (non-empty UTF-8 literals of at most INTERN_MAX_BYTE_LEN
bytes); the pool finds that atom later, and the seeded list holds the same
strings as a list written after the pools ran.

Tests: a seeded literal answers the megamorphic confirm like a shape minted
after the pools ran (sabotage: without the atom mint the seeded record's
confirm fails); lead_mega1 as an e2e instruction bound (<= 260 per read;
182.9 fixed, 392.9 without the fix).
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: ddc6c7c9-396a-40b4-9794-a7c0e10df55f

📥 Commits

Reviewing files that changed from the base of the PR and between b0bf0ae and 15a0231.

📒 Files selected for processing (5)
  • changelog.d/11673-fix-static-id-megamorphic-confirm.md
  • crates/perry-runtime/src/object/static_shapes.rs
  • crates/perry-runtime/src/object/static_shapes_tests.rs
  • crates/perry-runtime/src/string/mod.rs
  • crates/perry/tests/megamorphic_static_literal_confirm.rs

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Static shape seeding now mints eligible key names as string-pool atoms before building canonical key lists. Tests check megamorphic slot confirmation for seeded shapes and measure instruction counts for repeated literal reads.

Changes

Static Shape Key Atom Confirmation

Layer / File(s) Summary
Seed-time key atom creation
crates/perry-runtime/src/string/mod.rs, crates/perry-runtime/src/object/static_shapes.rs, changelog.d/11673-fix-static-id-megamorphic-confirm.md
The seeding path mints eligible names as string-pool atoms before building canonical key lists. The string module re-exports the maximum interned byte length used by the helper.
Key confirmation and performance tests
crates/perry-runtime/src/object/static_shapes_tests.rs, crates/perry/tests/megamorphic_static_literal_confirm.rs
Runtime tests check positional key confirmation for seeded and dynamically minted shapes. A Linux integration test measures instructions for repeated reads across sixteen literal shapes.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 15a02

The seeded-key change has no identified merge-blocking issue and is ready for normal merge checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 15a02

The change affects startup-time key identity within each runtime agent. The inspected production path uses compiler-generated keys, and no new external entrypoint or privilege was identified. Exposure through native callers is not fully established.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — On the inspected generated-code path, the added pool entries come from static key names in each agent rather than from a newly exposed user-input path. Deployed native ABI reachability was not established.

Trust Boundaries and Controls

  • observed — The exported seed function does not enforce the provenance of its raw pointer. Its compiler-owned-input assumption predates this change; the PR adds a bounded-eligibility atom-minting step behind that same ABI.

Resilience and Maintainability Implications

  • inferred — The inspected per-agent initialization and GC behavior support the intended same-agent pointer identity. Cross-agent sharing of a seeded shape was not established by the inspected source.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the runtime fix, the affected seeded static literal shapes, and the restored megamorphic confirmation behavior.
Description check ✅ Passed The description provides a clear summary, root cause, fix, performance results, tests, and verification details. It does not follow the template headings or include the checklist, but it contains the …
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 4 files. (1 skipped: 1 u…
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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
proggeramlug merged commit 2498f13 into main Sep 29, 2026
21 of 23 checks passed
@proggeramlug
proggeramlug deleted the fix-static-id-megamorphic-confirm branch September 29, 2026 16:29
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