Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions benchmarks/repsel_census/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,21 @@ Six proven, two applied. A promotion goes unconsumed three ways, and every one
of them is recorded at the site where the proof is dropped:

1. **`module_init_context`** (#7109) — `codegen/entry.rs` sets
`repsel_context_allows_canonical_i32: false` for module-init and
program-entry bodies, and `FnCtx::ptr_shape_receiver_fact` returns `None` for
the whole body when that flag is clear. Every access site falls back to the
guarded diamond.
2. **`async_body` / `generator_body`** (#6328) — the same flag, cleared for a
different reason.
`repsel_context_allows_ptr_shape: false` for module-init and program-entry
bodies, and `FnCtx::ptr_shape_receiver_fact` returns `None` for the whole
body when that flag is clear. Every access site falls back to the guarded
diamond.

That flag used to be `repsel_context_allows_canonical_i32`, shared with a
different representation. #7109 lifted the entry-body exclusion for
canonical i32/u32/Str and split the flag rather than dragging `Ptr<Shape>`
along with it: #6991 is an open rooting bug for a compiled receiver held
across the `globalThis`-population collection, which runs around module
init. So this row is unchanged, and it is now the only representation the
rule names.
2. **`async_body` / `generator_body`** (#6328) — the canonical flag, cleared for
a different reason. `Ptr<Shape>` reads the same rule name through its own
flag.
3. **`scalar_replaced`** (#7115) — `collectors/escape_news.rs` deleted the
object outright. This one is the *better* outcome, not a defect; it is listed
because "scalar-replaced" and "promoted but wasted" used to render
Expand Down Expand Up @@ -140,9 +149,11 @@ Three separate mechanisms, in increasing order of paranoia:
candidates" names nothing — and the two produce an identical census table.
When #7104 landed, **8 of the 18 real workloads were in the second state**,
every one because its hot loop is at module top level and
`codegen/entry.rs` excludes module-init contexts from canonical selection
before any per-value rule runs (#7109). Nothing in the census could have
told the difference; the follow-up records those as denials so it can.
`codegen/entry.rs` excluded module-init contexts from canonical selection
before any per-value rule ran (#7109). Nothing in the census could have told
the difference; the follow-up recorded those as denials so it could, and
#7109 then removed the exclusion — the same values are now selections, and
`canonical-i32` went from promoting in 2 of 18 real workloads to 17 of 18.

Only `suite_01_startup` is allowlisted: it is a lone `console.log`, with no
bindings for any analysis to consider.
Expand Down Expand Up @@ -181,6 +192,12 @@ local in a loop moves it to the parallel-shadow model, adding a bounds check to
`fixture_int_valued_ta` moves its locals to the ordinary integer-local path.
Each file says which edits would silently take it to zero.

`fixture_module_init_canonical.ts` has one extra rule of its own: **it must
never grow a function, method or closure.** Its whole claim is that every count
it reports came from the module-init `FnCtx`; moving one loop into a helper
would make it a duplicate of `fixture_canonical_slots.ts` and leave the entry
context untested again.

If a fixture legitimately stops exercising its representation, change the
fixture *and* say so in the PR. Lowering `LIVENESS_FLOORS` instead is how this
gate would end up unable to fail.
Expand Down
70 changes: 48 additions & 22 deletions benchmarks/repsel_census/baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 1,
"canonical-i32": 3,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 1,
Expand All @@ -144,7 +144,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 2,
"canonical-i32": 3,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand Down Expand Up @@ -183,7 +183,33 @@
"int-valued-ta": 0,
"spec-abi": 4
},
"unconsumed_mechanisms": {}
"unconsumed_mechanisms": {},
"consumption_sites": {}
},
{
"name": "fixture_module_init_canonical",
"role": "liveness",
"source": "benchmarks/repsel_census/fixtures/fixture_module_init_canonical.ts",
"floors": {
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 1,
"canonical-i32": 3,
"canonical-u32": 1,
"canonical-str": 1,
"int-valued-ta": 0,
"spec-abi-entry": 0,
"spec-abi-taptr-slot": 0
},
"candidates": {
"ptr-shape": 0,
"ptr-numarray": 1,
"canonical-slot": 5,
"int-valued-ta": 0,
"spec-abi": 0
},
"unconsumed_mechanisms": {},
"consumption_sites": {}
},
{
"name": "batch",
Expand All @@ -193,7 +219,7 @@
"ptr-shape": 2,
"ptr-shape-consumed": 1,
"ptr-numarray": 0,
"canonical-i32": 3,
"canonical-i32": 5,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand Down Expand Up @@ -249,7 +275,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -274,7 +300,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 1,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -299,7 +325,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 1,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -324,7 +350,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 1,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -349,7 +375,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -374,7 +400,7 @@
"ptr-shape": 1,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -401,9 +427,9 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"canonical-str": 1,
"int-valued-ta": 0,
"spec-abi-entry": 0,
"spec-abi-taptr-slot": 0
Expand All @@ -426,7 +452,7 @@
"ptr-shape": 1,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand Down Expand Up @@ -457,7 +483,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 1,
"canonical-i32": 0,
"canonical-i32": 3,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -482,7 +508,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 3,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -507,7 +533,7 @@
"ptr-shape": 1,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -534,7 +560,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -559,7 +585,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 2,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -584,7 +610,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 0,
"canonical-i32": 6,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -609,7 +635,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 0,
"canonical-i32": 3,
"canonical-i32": 5,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -634,7 +660,7 @@
"ptr-shape": 0,
"ptr-shape-consumed": 0,
"ptr-numarray": 1,
"canonical-i32": 0,
"canonical-i32": 4,
"canonical-u32": 0,
"canonical-str": 0,
"int-valued-ta": 0,
Expand All @@ -652,5 +678,5 @@
"consumption_sites": {}
}
],
"generated_at": "2026-07-31T05:53:44.935156Z"
"generated_at": "2026-07-31T06:45:01.246943Z"
}
50 changes: 50 additions & 0 deletions benchmarks/repsel_census/fixtures/fixture_module_init_canonical.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Liveness fixture for canonical selection in a MODULE-INIT body (#7109).
//
// `fixture_canonical_slots.ts` proves the three canonical reps are alive in
// function bodies. This one proves the same thing for the program-entry body,
// and it is deliberately the ONLY program in the corpus that can: it declares
// no function, no method and no closure, so every promotion it reports had to
// come from `codegen/entry.rs`'s FnCtx.
//
// Before #7109 both entry contexts hard-coded
// `repsel_context_allows_canonical_{i32,str}: false`, so this file's counts
// were 0/0/0 no matter what the per-value rules said. That is what makes the
// floors below falsifiable: revert the entry.rs gate and this fixture goes to
// zero on all three keys while every function-body fixture stays green.
//
// Requirements mirrored from the function-body fixture: no closure may capture
// a candidate (there are no closures), and no candidate may be a module global
// (nothing here is exported or read from a function, so nothing is globalized
// into `@perry_global_*`).

// Canonical i32: an index-used loop counter and the index-used bound it is
// compared against. `data[i]` is what makes both index-used; a counter that
// never reaches an array index is rejected by `not_index_used_or_bounded`.
const LIMIT = 64;
const data: number[] = [];
for (let i = 0; i < LIMIT; i++) {
data[i] = i * 3;
}

let checksum = 0;
for (let i = 0; i < LIMIT; i++) {
checksum = checksum + data[i];
}

// Canonical u32: every write is a top-level `>>> 0`, so the value stays
// observable as unsigned above 2^31 and the u32 bit pattern round-trips.
let mixed = (0x9e3779b9 ^ LIMIT) >>> 0;
for (let s = 0; s < 8; s++) {
mixed = (mixed ^ (mixed << 13)) >>> 0;
mixed = (mixed ^ (mixed >>> 17)) >>> 0;
}

// Canonical Str: a string local whose every write is a string — Phase 3a's
// motivating `+=` self-append shape, which is what `benchmarks/suite/
// 08_string_concat.ts` is and why it promoted nothing before #7109.
let text = "seed";
for (let t = 0; t < LIMIT; t++) {
text = text + "x";
}

console.log("module_init_canonical:" + checksum + ":" + mixed + ":" + text.length);
Loading
Loading