Skip to content

perf: function-valued stores served by the store IC with a body check (ConstFn key-add) - #11798

Merged
proggeramlug merged 4 commits into
mainfrom
perf-constfn-function-stores
Oct 3, 2026
Merged

proggeramlug merged 4 commits into
mainfrom
perf-constfn-function-stores

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Function-valued stores (this.k = F, o.cb = fn) no longer miss the store IC on every store. Stacked on #11787; retarget to main after it merges.

Instructions per op

Row base this PR
new C() storing this.k = C 5,013 2,189
this.cb = fn, 16 closures of one body 5,085 2,267
o.cb = fn overwrite 2,026 202
#10507 decimal row 5,003 2,387
controls (number key-add, class decimal, empty ctor) – unchanged

A function-valued key-add now costs about 156 instructions more than a number key-add, down from about 2,985.

How it works

  • The store site gets a fifth word holding its one ConstFn body, set the first time a function store is cached.
  • A cached function store is served inline only when the value is a closure of that body: one GC-header kind load, one info compare and one capture-flag test. Anything else takes the slow path, which deprecates the lane as before.
  • The runtime key-add memo applies the same check.
  • The new guard bits are in perry-abi and pinned by the layout test.

tsc and Zod (n=5; output matches node)

  • tsc: −0.07%, full collections 81/81.
  • Zod: +0.08%, full collections 0/0.
  • RSS is equal within run-to-run spread.

Verification

  • runtime 4827/0; codegen lib 1913/0; native_proof_regressions 291/0.
  • fmt, file size, gc_call_effects and gc-root-dominance (40/40 seeded caught) pass.
  • Gap A/B on 229 tests: 0 regressions.
  • Sabotage:
    • the runtime memo skipping the body check, and a site claiming any body, each turn a unit test red;
    • the emitted check ignoring the body is caught only by the codegen IR test assert_constfn_body_compare, because no current JS program can observe it.

Limits: one body per site, so a site alternating two bodies still misses for the second. Arrows that capture this take the slow path. Class-field stores were not measured.

Ralph Küpper added 4 commits October 3, 2026 10:55
…ord (#10507)

`new F()` and `x instanceof F` on a plain `function` went through every
exotic-callee probe (builtins by code pointer, bound, proxy, native-module
exports), three hash tables keyed by address (FUNCTION_CLASS_IDS,
CLASS_PROTOTYPE_OBJECTS, the own-symbol table) and two shape interns per
construction.

- perry-abi: FN_COMPILED_BODY marks every info perry-codegen renders, so an
  ordinary compiled function is a fact of its body, decided once.
- Construction reads F.prototype through F's ShapeId (a per-agent ShapeId to
  slot cache) and replays the birth record kept on the prototype object
  (ObjectMeta::instance_birth: class id and birth ShapeId, minted by the first
  ordinary construction, cleared when that class's registered prototype
  moves), then enters the body directly.
- instanceof with an ordinary compiled F is one shape compare when x's ShapeId
  names F.prototype, else OrdinaryHasInstance's prototype walk for an object
  of no compiled class: an object created before F.prototype was reassigned
  is no longer an instance. A bound function answers for its target.
- A method call whose name a class also declares sends receivers of no such
  class to the method site (direct call of an inherited F.prototype method)
  instead of the own-property probe and the by-name dispatcher.

Instructions per op (issue repro): new F 5,048 -> 1,898, instanceof
2,809 -> 425, decimal.js-shaped x.plus(i) 15,848 -> 5,003. tsc -1.43%,
Zod -0.13%, outputs equal to node.
A ConstFn lane (a slot holding a closure of one static body) was refused by
both halves of the static-key store site: the key-add memo and the
existing-key word. Every function-valued store therefore took the runtime
miss (~2,800 instructions per key-add on the #10507 decimal row).

The site gains a fifth word naming its one ConstFn body (claimed once by the
primary agent, never changed). A key-add memo whose successor's lane is
ConstFn carries a guard flag (bit 14), and an existing-key word or way for a
ConstFn slot carries bit 62. The emitted hit then admits only a closure of
the site body: POINTER tag above the handle band, GcHeader kind CLOSURE and
not forwarded, ClosureHeader::info equal to the site word, and not a
rebindable `this` clone. Any other value takes the miss, whose checked funnel
deprecates the lane. The runtime memo (packed_add_try) applies the same
admission.

ABI: perry-abi PACKED_SET_SITE_WORDS / PACKED_SET_CONSTFN_INFO_WORD and the
two flag bits, pinned by packed_set_site_layout_matches_codegen.
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 23918ab1-69ee-4c78-a0af-9d4f69aad93e
📥 Commits

Reviewing files that changed from the base of the PR and between ea38090 and 88eed19.

📒 Files selected for processing (29)
  • changelog.d/11787-function-constructors.md
  • changelog.d/11798-constfn-function-stores.md
  • crates/perry-abi/src/lib.rs
  • crates/perry-codegen/src/expr/put_value_store_ic.rs
  • crates/perry-codegen/src/expr/write_pic_barrier_tests.rs
  • crates/perry-codegen/src/fn_info.rs
  • crates/perry-codegen/src/lower_call/property_get/dynamic_dispatch.rs
  • crates/perry-codegen/tests/native_proof_regressions.rs
  • crates/perry-runtime/src/closure/dispatch.rs
  • crates/perry-runtime/src/closure/dispatch/value_call.rs
  • crates/perry-runtime/src/closure/mod.rs
  • crates/perry-runtime/src/closure/shape.rs
  • crates/perry-runtime/src/object/alloc_basic.rs
  • crates/perry-runtime/src/object/class_registry.rs
  • crates/perry-runtime/src/object/class_registry/construct.rs
  • crates/perry-runtime/src/object/class_registry/construct/compiled_function.rs
  • crates/perry-runtime/src/object/class_registry/construct/compiled_function_tests.rs
  • crates/perry-runtime/src/object/class_registry/state.rs
  • crates/perry-runtime/src/object/instanceof/dynamic_dispatch.rs
  • crates/perry-runtime/src/object/meta_accessors.rs
  • crates/perry-runtime/src/object/meta_record.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/proxy/put_value/cached_constfn_tests.rs
  • crates/perry-runtime/src/proxy/put_value/packed_add.rs
  • crates/perry-runtime/src/proxy/put_value/packed_add_tests.rs
  • crates/perry-runtime/src/proxy/put_value/packed_set.rs
  • test-files/_helpers/constfn_store_worker.ts
  • test-files/test_gap_10507_function_constructors.ts
  • test-files/test_gap_constfn_function_stores.ts
 ____________________________________________________________________________________________________
< Don't live with broken windows. Fix bad designs, wrong decisions, and poor code when you see them. >
 ----------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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 added the run-extended-tests Opt PR into compile-smoke/parity/doc-tests/drizzle-mysql-smoke label Oct 3, 2026
Base automatically changed from perf-10507-function-constructors to main October 3, 2026 12:02
@proggeramlug
proggeramlug merged commit b6357f6 into main Oct 3, 2026
23 of 45 checks passed
@proggeramlug
proggeramlug deleted the perf-constfn-function-stores branch October 3, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-extended-tests Opt PR into compile-smoke/parity/doc-tests/drizzle-mysql-smoke

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant