diag(runtime): receiver-representation ledger — per-family headerless-receiver counters and a typed native-result classification replacing NR_PTR (371 rows, 0 unknown) - #9973
Conversation
Instrument headerless receiver producers and dynamic receiver funnels behind the opt-in PERRY_RECEIVER_REPR_DIAG sink. Add explicit native return storage kinds while retaining the existing pointer-boxing lowering, plus checked provider inventory and fail-capable fixtures. Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
Record the producer map, corrected executable-row census, sabotage proofs, gate results, and the perrymaster measurement request for campaign PR 1. Claude-Session: https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used all 8 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (54)
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 |
#9969 makes run_lint_gates.sh derive every command of every run: step (80 now, up from 67). Two of the newly-run gates were red on this train: - addr_class_inventory rejected #9973's two GcHeader casts in hot_diag/receiver_repr.rs. Both are allowlisted with reasons rather than converted: the block is a #[cfg(debug_assertions)] trust-the-tag audit that compares the ownership-derived header against a direct byte-offset read and counts disagreements. Routing the raw side through try_read_gc_header would validate the address first and return None for exactly the implausible cases the audit exists to catch, so the canonical predicate cannot stand in there. - RUSTFLAGS="-D warnings" cargo check --workspace --all-targets rejected #9861's doc comment on a thread_local! macro invocation, which cannot carry one. Moved inside the macro onto the static it describes. This is a warning, not an error, so it only fails under -D warnings — which the driver never replayed before #9969.
|
Landed on The three non-passing gates are accounted for: public-baseline is pre-existing on |
First PR of the receiver-representation campaign (design:
cc-perf-campaign/DESIGN_receiver_representation.mdin the campaign repo). Measurement and metadata only — no JS-visible representation, release-path classifier, wrapper, or native-result boxing changes. Written by codex (0.154.0-alpha.3); all gates run locally.Why
On the best cc bundle the receiver family —
try_read_tracked_gc_header2.46 % self,js_native_call_method_at_site2.90,dispatch_primitive1.79, plus the Buffer/typed-array registry residue — is ≈ 9.9 % of main-thread samples. The audit (REPORT_trust_the_tag.md) showed the direct-header read cannot replace the ownership check while seven headerless POINTER_TAG families exist (integer registry ids, AsyncHook/AsyncResource boxes, leaked global symbols, external buffers, SAB backing, the null stub, bare managed addresses). Before any of them is migrated, the campaign needs (a) counts per family on a real cc reply and (b) an executable classification of every native pointer result so a wrapper migration cannot miss a producer.What changes
PERRY_RECEIVER_REPR_DIAG=<sink>(hot_diag sink, SIGKILL-safe snapshot): one line[receiver-repr-diag] constructed common= fetch= zlib= proxy= timer= text= tui= async_hook= async_resource= symbol_global= external_buffer= sab= null_stub=; observed_old …; observed_wrapped …; bare_managed= invalid_pointer_zero= direct_mismatch=. Producers bump their family at the constructors (common/fetch/zlib registries, proxy, timers, text, TUI, async hooks/resources, global symbols, external buffer, SAB, null stub — file:line in the campaign report); the five dynamic-receiver funnels (gc_pointer_and_type_from_value,dispatch_primitive,object_static_prototype, the field tail,ic_miss) classify only while armed. Every guard is one relaxed atomic load when off;direct_mismatch's header probe isdebug_assertions-only.NR_PTRresult kind is replaced byNR_GCPTR(131 rows),NR_NULLABLE_GCPTR(2),NR_HANDLE_ID(221),NR_FOREIGN_PTR(4: AsyncHook/AsyncResource backing),NR_JS_VALUE(13) — 371 executable declarations (the audit's "372" counted two prose comments and missed one helper row), all materialised through the samenative_handlebranch as before (byte-identical lowering).scripts/native_result_ledger.tsvrecords 322 runtime symbols with class, provider source and Rust return type (the design's 81 unresolved rows: 81/81 resolved);scripts/native_result_ledger.pyfails on any remainingNR_PTR, any table/provider class disagreement, or a stale provider entry, with a--self-test; a path-filtered CI workflow runs it.Tests (named; sabotage performed)
receiver_repr_family_fixtures_move_constructed_and_observed_old(each family's constructed > 0, observed_old > 0, observed_wrapped == 0),receiver_repr_every_family_producer_keeps_its_constructed_bump(pins the bump-site count per producer file; sabotage: one family token changed →left: 1, right: 2),receiver_repr_unarmed_funnels_never_enter_classification(test-only entry counter stays zero with the sink off; sabotage: drop a guard → nonzero),native_result_ledger.py --self-test(a plantedNR_PTRrow and a planted class disagreement are both rejected).Gates (local, macOS arm64)
Runtime release lib suite 3,262 passed / 0 failed; codegen tests 1,450 passed;
cargo build --release -p perry-runtime --features wasm-hostandcargo build --release -p perryok; root-holder and thread-local inventories ok; rustfmt, diff-check, file-size ok.Next
perrymaster runs one 3300-char cc reply with the diag on the best bundle (stage RL): the ledger line prices PR 2 (wrapper creations per reply) and names which headerless families actually reach a dynamic receiver on cc. Per the design, the campaign's early kill (a forced-invariant synthetic A/B) runs before any wrapper migration.
Measured (perrymaster RL, 2026-09-08; main6 bundle relinked against this runtime and stdlib, one 3300-char cc reply,
PERRY_RECEIVER_REPR_DIAG=stderr)constructed common=11 fetch=20 zlib=0 proxy=0 timer=137 text=15 tui=0 async_hook=0 async_resource=0 symbol_global=55 external_buffer=0 sab=0 null_stub=0; observed_old common=4974 fetch=63 timer=4966 text=4949 symbol_global=148 (others 0); observed_wrapped all 0; bare_managed=0; invalid_pointer_zero=0; direct_mismatch=0. Exactly five families reach a dynamic receiver on cc (timers, global symbols, fetch handles, text handles, the common pool); eight never do. 238 constructions per reply price the next PR's wrappers as negligible. The ~4.9 kobserved_oldcounts shared by common/timer/text are the same small-integer payloads claimed by each overlapping range, as the report predicts.https://claude.ai/code/session_011dhBmdn4vGgNibjo3oZqTo