perf(runtime): an ordinary object's prototype is a fact of its shape; new F() allocates no ObjectMeta - #11815
Merged
Merged
Conversation
added 2 commits
October 3, 2026 16:11
A ShapeId already names its receivers' prototype identity (`proto_id`: a prototype serial, MIXED, UNIQUE, NULL, CLASS or PER_OBJECT), but every reader went back to the per-instance `ObjectMeta.prototype` for the object itself, so each `new F()` instance allocated a 152-byte meta record only to hold it. The way back from the identity to the object is now one word per prototype identity (object/shapes_prototype.rs): stable pages indexed by serial (plain and MIXED identities share it) and by UNIQUE number, owned by the agent's shape slab and published to a thread-local directory. The ShapeRecord stays 64 bytes. - Writer: the one prototype funnel (`object_set_static_prototype_impl`) computes the identity, writes its word, and moves the receiver to the shape with that identity. A class-default link on a meta-less function-constructor instance (synthetic class) allocates no meta record; any other receiver already has one (link flags, PER_OBJECT identity) and gets the same bits there. #10507's birth replay no longer allocates meta and validates the birth ShapeId's facts instead of pinning the id. - Readers: `shapes::object_prototype_word` (meta word if recorded, else the identity's word for a synthetic-class receiver, else nothing; null from the identity, born-null cells unchanged) behind `object_static_prototype`, `object_proto_id`, native_get, method_site, prop_plan, timer handles, the typed-feedback guard and the setter site. - GC: the identity word is a traced edge of its carriers, beside the keys edge, in the generic walk and the copying drain's plain-object plan; a full trace emits it once per identity. A minor also roots every word naming a young object; rewrite passes repair every word; a word whose prototype died is cleared by a dead-owner prune. Links shade the prototype for an incremental mark. Worker seeds skip identities with a word. - Fixes on the way: `Object.create(null) instanceof Object` was true; a class instance re-parented with setPrototypeOf stayed `instanceof` its class; `Object.create(fn) instanceof Function` was false; a re-parented `Object.create(null)` object kept a null-prototype shape identity. - Test repair: test_object_meta_null_prototype_survives_full_gc_on_live_owner never pushed a shadow frame, so its owner was not rooted and it read freed memory. `new F()` 1,898 -> 1,165 instructions per construction. Refs #10507
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing (31)
✨ 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 |
This was referenced Oct 3, 2026
proggeramlug
pushed a commit
that referenced
this pull request
Oct 3, 2026
… prototype With the prototype a fact of the shape (#11815), every reader of OBJECT_META_FLAG_PROTO_DIVERGED was a cache gate whose key already carries the prototype, so the per-instance flag only duplicated the ShapeId: - store plans (field_set_by_name, its tail, the proxy set path) are keyed by (class id, key, receiver prototype bits); - array-subclass dense layouts (array/subclass.rs, subclass_loop_guard.rs) are keyed by (class id, ShapeId) and validated by the receiver word, which carries the ShapeId; - the defineProperty key-add tail (keys_array.rs) replays ShapeId-keyed transition edges; - `x instanceof <class object>` (instanceof/dynamic_dispatch.rs) now asks whether the receiver has a recorded prototype instead. The flag, its funnel write, the class-object-template set/clear and its tests are deleted; bit 0 of ObjectMeta.flags is free. A runtime-wiring link on a function-constructor instance no longer allocates a meta record. Tests: `new F()` and `Object.create(F.prototype)` receivers share one shape; the dense array-subclass test now asserts the receiver moves to another ShapeId across a prototype override and its own element still reads the same (it asserted the old per-instance decline). Refs #10507
proggeramlug
pushed a commit
that referenced
this pull request
Oct 3, 2026
…s.json from #11815 Keeps main's PASS1_MARKED audit, appends the prototype-in-shape re-audit and pins gc/mod.rs at its current hash.
proggeramlug
pushed a commit
that referenced
this pull request
Oct 3, 2026
…s.json from #11815 Keeps main's PASS1_MARKED audit, appends the prototype-in-shape re-audit and pins gc/mod.rs at its current hash.
proggeramlug
pushed a commit
that referenced
this pull request
Oct 4, 2026
…ck to baseline for these files)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
protoshape phase 1: prototype in the shape (ad87de06e, on origin/main 6820067)
Bundle:
bundles/protoshape.bundle. Head:refs/heads/protoshape-phase1= ad87de06e. Prerequisite:6820067. Message: Refs #10507. Design:
protoshape/DESIGN.md.What it does
prototype. The words live in stable per-agent pages behind a thread-local directory. The
ShapeRecord stays 64 bytes.
new F()) carries no ObjectMeta. It reads its prototype fromits ShapeId.
words. Rewrite passes repair every word. A dead-owner prune clears a word whose prototype died.
Object.create(null) instanceof Object,Object.create(fn) instanceof Function, and thenull-prototype shape identity of a re-parented
Object.create(null)object.test_object_meta_null_prototype_survives_full_gc_on_live_ownernever pushed ashadow frame, so it passed on freed memory.
Verification on the rebased tree (vs main 6820067, qb6)
proto|class|instanceof|create|new|inherit|symbol|super: 240 tests.3527 and 10454.
test_gap_prototype_in_shape and test_gap_10507.
was re-audited and re-pinned for the gc/mod.rs scanner registration.
does not touch.
passed 2430/0 in the pre-rebase run. The rebased results land in
logs/v3.SUMMARYon qb6.Verification on the pre-rebase tree (main + #11787 + #11784, full batch)
and branch (regexp receiver rooting, pre-existing).
forced evacuation and the verifier.
proto_idfrom the shape key and the facts match):receivers_with_different_prototypes_never_share_a_shape,a_class_default_link_records_the_prototype_in_the_shape_only,a_null_prototype_is_a_shape_fact.fn 5 new new new new true true).Numbers (pre-rebase final batch; instructions:u medians, n=5)
new F()(fnctor new_fn_empty, per op)x.plus(i)(per op)new KE()class /ke instanceof KERefs #10507. Phase 2 (deleting the PROTO_DIVERGED / USER_PROTO_OVERRIDE / CLASS_EVALUATION_PROTO flags and moving the class-id shortcuts onto shapes) follows in a separate PR.