perf(codegen): representation-selection Phase 2 — specialized calling convention (bounded monomorphization) - #6905
Conversation
… convention (bounded monomorphization) Full-body specialized entries via the real compile_function parameterized on a call-site-derived rep tuple (Boxed/I32/F64/TaPtr), static guard-free Tier A dispatch for by-construction proofs, guarded Tier B for declaration-only proofs, proven-view checked element access, and the ctx-free spec_abi_sites pre-pass. Implements Phase 2 of docs/representation-selection-rfc.md. Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
…ss, module-const folding, routing fixes - masked-window region: compile-time-proven ta_i32 arrays (spec TaPtr params, literal-length views) skip the probes entirely; all-proven regions lower as ONE guard-free fast copy; proven-view element stores join the region as an admissible statement class (ctx-aware operand collect). - proven_view_access checked tier: guard-free bounds-checked loads/stores for storage-proven views; gated on undowngraded (scope+noalias) views. - top-level const numeric module bindings fold into compile_time_constants (proves P[BLOWFISH_NUM_ROUNDS+1] in-bounds). - PutValueSet element writes on statically-typed typed-array receivers route to index_set's typed arm. - clamp_detect: add missing was_plain_async gates (audit). - native_proof_buffer_views reassigned-read expectation updated for #6883's guarded checked read (was nightly-broken on main); asserts no unchecked form instead. Structural proof on enc_real.ts (real unrolled bcryptjs _encipher, untyped params): specialized entry post-O3 has ZERO js_dyn_index_get / js_dynamic_* / js_typed_array_* calls and zero kind guards; lr stores are native store i32; golden outputs byte-exact (lr0=-1623241632 lr1=-1640632493). Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
…adow-slot clear elision — enc_real beats Node - int_valued_ta_locals wrap-i32 extension: straight-line Add/Sub chains over exact operands (in-bounds-proven int-TA reads via constant lengths, incl. spec-ABI TaPtr param lens; literals; bitwise results; other candidates) carry the ToInt32 image in the canonical i32 slot; loop-carried additive writes and unproven operands stay rejected; hoisted-var undefined seeds admitted (ToInt32(undefined)=0 == slot seed). Fixpoint admission + additive-blessed observation walk with strict index positions. - let_stmt: re-declared canonical locals (hoisted var pairs) route the second init into the existing canonical slot. - FEAT_JSCVT: toint32_wrap emits @llvm.aarch64.fjcvtzs (spec-exact 1-instr ToInt32) on apple-arm64; PERRY_JSCVT flag, cache-keyed; tower kept portable. - shadow_slots_bound tracking: clears of never-bound slots elided; spec TaPtr params skip the redundant callee-side root bind (caller binding is rooted, storage non-movable). - file-size gate: buffer-view Let registration extracted to stmt/let_buffer_views.rs; bitand mask helper hosted in proven_view_access. Quiet-box protocol (min-of-9, cache-busted): enc_real.ts 126ms flag-on vs 714ms flag-off vs Node 134ms — byte-exact golden outputs; post-O3 spec entry: zero js_dyn/js_dynamic/js_typed_array calls, zero kind guards, pure i32 Feistel (fadd 0, add i32 33, xor i32 50, store i32 2). Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
The plain-array third act trips a PRE-EXISTING declared-type staleness bug on main (reads through a let-TA binding reassigned to a plain array return undefined — reproduced on a pre-Phase-2 build), unrelated to the spec-ABI routing property this test gates. Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
iOS/tvOS device targets can still cover A7-A11 chips (ARMv8.0-8.2, no JSCVT — fjcvtzs would be an illegal instruction); every Apple Silicon Mac is >= ARMv8.4. Generic aarch64 stays on the portable tower as before. Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
… class Review finding: the all-proven zero-probe region tier trusted storage_inline_proven alone, but hazard paths (closure capture, unknown-call escape, aliasing) signal distrust by clearing scope_idx / demoting alias — and this tier emits no runtime re-validation at all. Gate on allows_noalias + scope_idx like the sibling proven tiers (proven_view_for, local_is_proven_int_store_view). Currently inert (every stale-slot path is independently caught), but the flag was never designed to encode hazard-freedom; structural proof and enc_real golden outputs unchanged. Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
🚧 Files skipped from review as they are similar to previous changes (11)
📝 WalkthroughWalkthroughRepresentation-selection Phase 2 adds typed-array provenance analysis, bounded specialized ABI planning, static or guarded dispatch, specialized function emission, proven typed-array access lowering, wrap-i32 analysis, JSCVT support, cache-key updates, documentation, and regression tests. ChangesSpecialized ABI planning and dispatch
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
🚥 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.
Actionable comments posted: 5
🧹 Nitpick comments (6)
crates/perry-codegen/src/lower_call/func_ref.rs (1)
508-532: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOnly the typed-f64 arm is gated on
spec_result.is_none().The typed-i32/i1/string arms aren't, which reads as an oversight even though it's currently harmless (plan selection makes spec and typed-clone sets mutually exclusive, and the
if/else ifchain consumesspec_resultfirst). Either gate all four uniformly or drop the gate and rely on the chain — as written the asymmetry invites a future divergence.Also applies to: 586-588
🤖 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-codegen/src/lower_call/func_ref.rs` around lines 508 - 532, Make the dispatch conditions for the typed-i32, typed-i1, typed-string, and typed-f64 arms consistent in the call-lowering chain around spec_result. Either require spec_result.is_none() for all four typed arms or remove that gate from the f64 arm, preserving the existing priority of specialized-ABI dispatch and the mutually exclusive typed-clone selection.crates/perry-codegen/src/collectors/spec_abi_sites.rs (1)
117-125: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
local_is_reassignedre-walks the whole statement list on every call.Each invocation builds a fresh
ModuleScanand walks all statements, so the caller pattern incodegen/mod.rs(f.params.iter().map(|p| local_is_reassigned(&f.body, p.id))) is O(params × body). For wide signatures on large bodies this is avoidable: expose a "scan once, query many" form.♻️ Sketch: scan once per body
pub(crate) fn reassigned_locals(stmts: &[Stmt]) -> HashSet<u32> { let mut scan = ModuleScan::default(); walk_stmts(stmts, 0, &mut scan); scan.writes } pub(crate) fn local_is_reassigned(stmts: &[Stmt], id: u32) -> bool { reassigned_locals(stmts).contains(&id) }Callers that query several ids then use
reassigned_localsonce.🤖 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-codegen/src/collectors/spec_abi_sites.rs` around lines 117 - 125, Add a scan-once helper near local_is_reassigned, such as reassigned_locals, that walks the statement list once and returns the collected write IDs. Update the multi-parameter caller in codegen/mod.rs to reuse this set for all parameter queries, while retaining local_is_reassigned as a single-ID convenience wrapper if needed.crates/perry-codegen/src/collectors/spec_abi_sites/tests.rs (1)
261-305: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding two rejection cases.
Neither the non-numeric-kind veto (
spec_ta_kind_is_numeric, e.g. a BigInt64 kind) nor thePreallocateBoxesveto (boxed_prealloc) is exercised. Both are soundness gates onTaPtr, so a regression there would silently widen the specialization surface.🤖 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-codegen/src/collectors/spec_abi_sites/tests.rs` around lines 261 - 305, Add tests covering both TaPtr rejection gates: a non-numeric typed-array kind rejected by spec_ta_kind_is_numeric (such as BigInt64) and a binding with boxed_prealloc enabled. Assert each case is excluded from the specialized TaPtr results, while preserving the existing length and reassignment tests.crates/perry-codegen/src/expr/proven_view_access.rs (1)
104-121: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider mirroring
proven_view_for's closure-capture gate here.
proven_view_for(Line 150) rejects a receiver inctx.closure_capturesbecause it can be reassigned between proof and access; this sibling predicate omits that check. Today the actual lowering still goes throughproven_view_for, so no unsound code is emitted — butmasked_window_region's store admission and operand collect use only this predicate, so the two proofs drift. Adding the same gate keeps them aligned.🤖 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-codegen/src/expr/proven_view_access.rs` around lines 104 - 121, Update local_is_proven_int_store_view to reject IDs present in ctx.closure_captures, matching the closure-capture gate used by proven_view_for. Keep the existing storage, ownership, indexing, alias, scope, and element checks unchanged so masked_window_region admission and operand collection use the same proof criteria.crates/perry-codegen/src/stmt/let_buffer_views.rs (1)
38-47: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMagic-number duplication for the native-owned data offset.
The native-owned branch hardcodes GEP offset
"24"fordata_field, duplicatinginit.data_offset_bytes(also24, set in theNativeArenaViewarm below). If that constant ever changes, this literal won't follow along silently.♻️ Derive the offset from `init` instead of hardcoding it
let data_ptr = if init.native_owner_local_id.is_some() { - let data_field = blk.gep(I8, &handle_ptr, &[(I32, "24")]); + let data_field = blk.gep(I8, &handle_ptr, &[(I32, &init.data_offset_bytes.to_string())]); blk.load(PTR, &data_field)🤖 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-codegen/src/stmt/let_buffer_views.rs` around lines 38 - 47, Update the native-owned branch in the let-buffer view generation to derive the GEP offset from init.data_offset_bytes instead of hardcoding "24". Keep the existing handle_ptr/data_field loading behavior unchanged and reuse the same offset source already used by the non-native branch and NativeArenaView initialization.test-files/test_gap_specabi_reassign.ts (1)
14-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: track the noted pre-existing staleness bug.
The NOTE documents a real defect on
main(reads through a binding reassigned fromInt32Arrayto a plain array returnundefined) that this fixture deliberately steps around. Keeping it as a comment only means it can be lost.Want me to open an issue to track it?
🤖 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 `@test-files/test_gap_specabi_reassign.ts` around lines 14 - 17, Track the declared-type staleness defect described in the NOTE as a separate issue or TODO, preserving the existing test’s scope and workaround. Reference the reassigned binding scenario where an Int32Array binding changes to a plain array and reads incorrectly return undefined; do not alter this spec-ABI routing fixture.
🤖 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.
Inline comments:
In `@crates/perry-codegen/src/codegen/function.rs`:
- Around line 447-458: Update the TaPtr documentation in the parameter-binding
logic to match the actual arm’s behavior: it does not create a shadow-slot
GC-root binding, and its header-rooting rationale must reflect the existing
callee-root behavior. Also revise the hoisted data-pointer comment near the
related code so it no longer claims a nonexistent boxed-parameter shadow
binding; leave the implementation unchanged.
In `@crates/perry-codegen/src/codegen/spec_abi.rs`:
- Around line 178-187: Update the dominant-tuple selection in the order iterator
so equal counts retain the first-appearing tuple rather than the last; preserve
the existing count lookup and returned (tuple, count) shape, and correct the
adjacent tie-break comment to match the implemented behavior.
In `@crates/perry-codegen/src/collectors/int_valued_ta_locals.rs`:
- Around line 211-243: Restrict the Expr::LocalGet arm in
additive_write_admissible so pool membership alone is insufficient: admit a
local only when its originating write is proven in-bounds for int-kind
typed-array reads or otherwise established as undefined-free by the
additive-tree analysis, consistent with write_is_i32_producing_safe. Preserve
the existing literal, direct read, and arithmetic producer handling while
preventing unproven pooled locals from entering canonical additive chains.
In `@crates/perry-codegen/src/expr/proven_view_access.rs`:
- Around line 62-99: Restrict the additive `Expr::Binary` arm in
`index_is_exact_i32_shape` to locals whose slot representation is signed i32,
excluding `U32`. Keep the existing addend range and operand-shape checks
unchanged, and preserve the bare `LocalGet` behavior for canonical i32
representations.
---
Nitpick comments:
In `@crates/perry-codegen/src/collectors/spec_abi_sites.rs`:
- Around line 117-125: Add a scan-once helper near local_is_reassigned, such as
reassigned_locals, that walks the statement list once and returns the collected
write IDs. Update the multi-parameter caller in codegen/mod.rs to reuse this set
for all parameter queries, while retaining local_is_reassigned as a single-ID
convenience wrapper if needed.
In `@crates/perry-codegen/src/collectors/spec_abi_sites/tests.rs`:
- Around line 261-305: Add tests covering both TaPtr rejection gates: a
non-numeric typed-array kind rejected by spec_ta_kind_is_numeric (such as
BigInt64) and a binding with boxed_prealloc enabled. Assert each case is
excluded from the specialized TaPtr results, while preserving the existing
length and reassignment tests.
In `@crates/perry-codegen/src/expr/proven_view_access.rs`:
- Around line 104-121: Update local_is_proven_int_store_view to reject IDs
present in ctx.closure_captures, matching the closure-capture gate used by
proven_view_for. Keep the existing storage, ownership, indexing, alias, scope,
and element checks unchanged so masked_window_region admission and operand
collection use the same proof criteria.
In `@crates/perry-codegen/src/lower_call/func_ref.rs`:
- Around line 508-532: Make the dispatch conditions for the typed-i32, typed-i1,
typed-string, and typed-f64 arms consistent in the call-lowering chain around
spec_result. Either require spec_result.is_none() for all four typed arms or
remove that gate from the f64 arm, preserving the existing priority of
specialized-ABI dispatch and the mutually exclusive typed-clone selection.
In `@crates/perry-codegen/src/stmt/let_buffer_views.rs`:
- Around line 38-47: Update the native-owned branch in the let-buffer view
generation to derive the GEP offset from init.data_offset_bytes instead of
hardcoding "24". Keep the existing handle_ptr/data_field loading behavior
unchanged and reuse the same offset source already used by the non-native branch
and NativeArenaView initialization.
In `@test-files/test_gap_specabi_reassign.ts`:
- Around line 14-17: Track the declared-type staleness defect described in the
NOTE as a separate issue or TODO, preserving the existing test’s scope and
workaround. Reference the reassigned binding scenario where an Int32Array
binding changes to a plain array and reads incorrectly return undefined; do not
alter this spec-ABI routing fixture.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 3b23643c-78fe-4477-8a71-31d46bc3d460
📒 Files selected for processing (43)
changelog.d/6905-repsel-p2-specialized-abi.mdcrates/perry-codegen/src/block.rscrates/perry-codegen/src/codegen/closure.rscrates/perry-codegen/src/codegen/entry.rscrates/perry-codegen/src/codegen/function.rscrates/perry-codegen/src/codegen/helpers.rscrates/perry-codegen/src/codegen/method.rscrates/perry-codegen/src/codegen/mod.rscrates/perry-codegen/src/codegen/opts.rscrates/perry-codegen/src/codegen/spec_abi.rscrates/perry-codegen/src/codegen/typed_abi.rscrates/perry-codegen/src/collectors/clamp_detect.rscrates/perry-codegen/src/collectors/hir_facts.rscrates/perry-codegen/src/collectors/int_valued_ta_locals.rscrates/perry-codegen/src/collectors/int_valued_ta_locals/tests.rscrates/perry-codegen/src/collectors/mod.rscrates/perry-codegen/src/collectors/spec_abi_sites.rscrates/perry-codegen/src/collectors/spec_abi_sites/tests.rscrates/perry-codegen/src/expr/buffer_access.rscrates/perry-codegen/src/expr/buffer_views.rscrates/perry-codegen/src/expr/index_get.rscrates/perry-codegen/src/expr/index_set.rscrates/perry-codegen/src/expr/mod.rscrates/perry-codegen/src/expr/proven_view_access.rscrates/perry-codegen/src/expr/proxy_reflect.rscrates/perry-codegen/src/expr/range_facts.rscrates/perry-codegen/src/expr/shadow_slot.rscrates/perry-codegen/src/lower_call/func_ref.rscrates/perry-codegen/src/native_value/buffer.rscrates/perry-codegen/src/runtime_decls/strings.rscrates/perry-codegen/src/stmt/let_buffer_views.rscrates/perry-codegen/src/stmt/let_stmt.rscrates/perry-codegen/src/stmt/loops.rscrates/perry-codegen/src/stmt/masked_window_region.rscrates/perry-codegen/src/stmt/mod.rscrates/perry-codegen/tests/native_proof_buffer_views.rscrates/perry/src/commands/compile/object_cache.rscrates/perry/src/commands/compile/object_cache/object_cache_tests.rsdocs/representation-selection-rfc.mdtest-files/test_gap_specabi_polymorphic_coexist.tstest-files/test_gap_specabi_reassign.tstest-files/test_gap_specabi_recursion_escape.tstest-files/test_gap_specabi_view_detach.ts
…erand number-flow proof, first-appearance tie-break Review findings on #6905: - index_is_exact_i32_shape: the local+c additive arm now requires the SIGNED I32 slot rep — a U32 rep's value near 2^32 is a negative i32 bit pattern and + c can wrap UP through zero into a valid index while the true JS index must read undefined. Bare LocalGet reads keep both reps (unsigned compare is bit-correct there). - wrap-i32 additive operands now need a flow-sensitive provably-NUMBER proof at the write site (additive_flow_invalid_targets): pool membership alone admitted operands whose true value can be undefined, where ToInt32(undefined + 1) == 0 diverges from the wrapped image 0 + 1 == 1. Producers: literals, bitwise/imul/byte reads, IN-BOUNDS-proven int-TA reads, additive trees over numberish operands; control-flow meets are set intersections (claiming less is always sound); loop-carried states meet the pre-state. enc_real's accumulators stay eligible (their resets are in-bounds-proven reads); structural proof and goldens unchanged. - select_dominant_tuple: ties now keep the FIRST-appearing tuple as documented (max_by_key returned the last maximal element). - doc fixes: TaPtr param GC-rooting comments now describe the shipped caller-rooted design; stray CJK char removed; spec_result gating comment. - consistency/cheap: reassigned_locals scan-once helper (demotion loop), closure-capture gate on local_is_proven_int_store_view, GEP offset derived from BufferViewInit, BigInt64-kind + prealloc-binding rejection tests, reassign gap test references #6906. Claude-Session: https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
|
@coderabbitai Addressed in 0758867 — per-finding status: Actionable
Nitpicks
Re-verification (quiet M1 box + local): post- The |
|
Tip For best results, initiate chat on the files or code changes.
🐇 ✅ ✏️ Learnings added
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
Phase 2 of
docs/representation-selection-rfc.md: the specialized calling convention (bounded monomorphization). Statically-proven call sites call a full-body specialized entry with raw-typed args (typed-array header pointer, raw i32, raw f64) — dispatch chosen statically, no per-call guard.RFC acceptance (§7 protocol: quiet M1 box, min-of-9, cache-busted arms, IR proof first)
enc_real.ts— the REAL unrolled bcryptjs@3.0.3_encipher, untyped params, 2.1M calls, goldenlr0=-1623241632 lr1=-1640632493:PERRY_SPECIALIZED_ABIonByte-exact outputs on every arm. Structural proof (post-
opt -O3, specialized entry kept out-of-line): zerojs_dyn_index_get/js_dynamic_*/js_typed_array_*calls, zero kind/view guards (PERRY_TA_KIND_CACHE/PERRY_TA_VIEW_GUARDabsent), pure-i32 Feistel body —fadd 0,add i32 33,xor i32 50,load i32 87,store i32 2, bounds viallvm.assume-proven windows plus 4 inlineicmp ultchecks against entry-hoisted lengths. Remaining runtime calls: onejs_shadow_frame_push/poppair.enc.ts(looped variant): 800 ms on vs 988 ms off, byte-exact — its loop-carried additive accumulator is correctly rejected by the straight-line rule (Phase 2.5 backlog).slot.ts: output unchanged.What this adds (generalizing the existing typed_abi three-symbol scheme)
compile_functionparameterized on a rep tuple (Boxed/I32/F64/TaPtr{kind, const_len}); internal linkage,{public}__spec_<mangle>naming, emitted in the pre-public loop, mutually exclusive withi64_specializedand the typed_abi clone families. Return stays boxed.I32params bind straight into Phase 1 canonicalSlotRepslots (zero boundary conversions);TaPtrparams bind as PROVENBufferViewSlots (data pointer + length hoisted once at entry) so element accesses lower through the strong bare-load machinery — never the per-site guarded fast paths that lose on unrolled bodies.collectors/spec_abi_sites.rs, a ctx-free two-phase pre-pass:TaPtrfrom single-binding never-reassigned non-closure-referencednew Int32Array(<literal len | proven plain-array source>)bindings (view forms rejected),I32/F64from literals; dominant-tuple selection with callee-side demotion (reassigned / closure-referenced params stayBoxed).callto the specialized symbol, no diamond, no phi; a distinct-locals check justifies the entry's pairwise-noalias view slots. Tier B keeps the guarded-diamond shape for declaration-proven (Int32-typed) tuples. Indirect/closure/cross-module/spread paths are untouched and can only reach the public boxed symbol (enforced by thespec_abi_symbol_reachabilitysource ratchet test).storage_inline_proven+ constant length covering the window) lower as ONE guard-free fast copy: no probes, no plain/slow copies; proven-view element stores join the region as an admissible statement class.expr/proven_view_access.rs) — guard-free inlineicmp ultload/store for storage-proven views with dynamic exact-i32 indices (lr[off]), bit-exact with the runtime helpers (undefined OOB reads, silent OOB stores).int_valued_ta_localsextension: straight-lineAdd/Subchains over exact operands (in-bounds-proven int-TA reads via constant lengths, literals, bitwise results, sibling candidates) carry the ToInt32 image in the canonical i32 slot; loop-carried additive writes, unproven operands, and index-position reads stay rejected.toint32_wrapemits@llvm.aarch64.fjcvtzs(the ARMv8.3 single-instruction spec-exact ECMAScript ToInt32) on apple-arm64 targets; the portable shift/select tower is kept elsewhere.PERRY_JSCVTkill switch.clamp_detectwas missingwas_plain_asyncgates (added); top-levelconst <numeric literal>module bindings now fold intocompile_time_constants(TDZ/prealloc-flagged bindings excluded);PutValueSetelement writes on statically-typed typed-array receivers route toindex_set's typed arm; shadow-slot clears of never-bound slots elided; specTaPtrparams skip the redundant callee-side root bind (the caller's proven binding is rooted; typed-array storage is non-movable — verified ingc/types.rs:GC_TYPE_TYPED_ARRAY/GC_TYPE_BUFFERaremovable: false, and non-view typed arrays cannot be detached or resized).Budgets, flags, observability
SpecFnPlan::MAX_ENTRIES_PER_FUNCTION = 1, an explicit knob), module capPERRY_SPECIALIZED_ABI_MAX(default 64) withspec_budget_exceededrejection records.PERRY_SPECIALIZED_ABI(default on) — both flags plusPERRY_JSCVTkeyed into the object cache and its key test.typed_clone_rejection_recordsvocabulary (spec_abi_entry_decision+ new reasons); Tier A/B dispatches emitrecord_lowered_valuenotes (spec_call=static|guarded; tuple=...).binary-sizeCI job monitors monomorphization bloat — that job measures the compiler binary, not user output; §5.4/§8 now describe the empirical corpus measurement.Tests
PERRY_GC_FORCE_EVACUATE=1): polymorphic-site coexistence + OOB write through the specialized entry; view-formnew Int32Array(buffer)+buffer.transfer()detach (never takes the guard-free entry); reassignment (never provesTaPtr); recursion + escaped-as-value (const g = f) through the wrapper path.spec_abi_symbol_reachabilitysource ratchet.cargo test -p perry-codegen -p perry: green except 3native_proof_regressionswrite-PIC tests that fail identically on pristinemain(b0d789dab; pre-existing from the perf(codegen/runtime): #6812 — 3-way dynamic-key write IC (inline hit path, self-healing caches) #6895 write-IC landing — integration suites don't run per-PR). Thenative_proof_buffer_viewsreassigned-read expectation was updated for perf(codegen): inline checked-f64 typed-array-param reads in numeric context #6883's guarded checked read (also broken on pristine main) — it now asserts the property (no unchecked/proven form on a reassigned receiver) instead of one specific lowering.Notes / Phase 2.5 backlog
TaPtrbindings (module-global or same-body top-levellet/const). Unproven shapes (non-literal scalar exprs, cross-body bindings, call sites inside closures) keep the boxed path.TaPtrtuples key the specialization; differently-shaped sites fall back to the boxed entry (routing test a).let-TA binding to a PLAIN array makes reads through it returnundefined(reproduced on a pre-Phase-2 build); kept out of the reassign gap test and worth its own issue.https://claude.ai/code/session_01UGDwjukzhowLDJYsMPFwMv
Summary by CodeRabbit
New Features
Bug Fixes
Tests