-
-
Notifications
You must be signed in to change notification settings - Fork 161
docs(codegen): record concat cache admission and workload evidence #9826
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| # Codegen mechanisms and workload evidence | ||
|
|
||
| An optimization's benchmark result does not establish that another program | ||
| uses the same generated path. Record the admission rule, the workload and | ||
| compiler snapshot, and evidence of emitted code before attributing a result | ||
| to that mechanism. | ||
|
|
||
| The [mechanism index](https://github.com/PerryTS/perry/blob/main/scripts/codegen_mechanisms.json) | ||
| starts with the per-site concat cache from | ||
| [#9514](https://github.com/PerryTS/perry/pull/9514), following the correction in | ||
| [#9824](https://github.com/PerryTS/perry/issues/9824#issuecomment-5554137476). | ||
| It is an evidence index, not a complete inventory or a new CI gate. Missing | ||
| entries mean unrecorded. Each entry names its lowering, runtime helper, | ||
| admission conditions, workload expectations, observations, and existing | ||
| regression tests. Refresh observations when the compiler, bundle, flags, or | ||
| proof changes; a historical negative is not a permanent property of an app. | ||
|
|
||
| ## Per-site concat cache | ||
|
|
||
| The [lowering](https://github.com/PerryTS/perry/blob/main/crates/perry-codegen/src/concat_site_cache.rs) | ||
| requires a string literal on the left in HIR and one of these right operands: | ||
|
|
||
| | Right operand | Admission proof | | ||
| | --- | --- | | ||
| | Compile-time integer | Value in `0..=255`, including supported constant arithmetic and integer-constant locals. | | ||
| | Loop-induction local | Proven interval with a nonnegative lower bound and upper bound at most 255. | | ||
| | `x % C` | Compile-time integer modulus `C` in `1..=256`; negative remainders fall back at runtime. | | ||
|
|
||
| The admission limit is **255**, but each site has only **32 slots**. At runtime, | ||
| an integral numeric value in `0..31` can hit a filled slot. The fill arm calls | ||
| `js_string_concat_site_value`; the plain arm handles values outside the table. | ||
| Ordered comparisons reject NaN and boxed non-numbers. An unproven site keeps | ||
| the ordinary fused helper and process-wide memo without this per-site diamond. | ||
| `PERRY_CONCAT_SITE_CACHE=0` disables the lowering **when compiling the program**. | ||
|
|
||
| The counted-loop shape in | ||
| [`bench_object_property.ts`](https://github.com/PerryTS/perry/blob/main/benchmarks/suite/bench_object_property.ts) | ||
| is admitted: `"field_" + j` has `j` in `0..19`. Fresh object compilation with | ||
| the codegen at `d36a1af0c` produced three tables and three fill call sites; | ||
| disabling the cache produced none. Both builds retained the ordinary helper. | ||
| This confirms applicability; it does not add a timing measurement to #9514. | ||
|
|
||
| The #9824 report found zero fill-helper executions in three runs of the | ||
| compiled `cli_2.1.112.js` bundle and no fill-helper symbol in its inspected | ||
| binary. Its roughly 8,600 concatenations per reply do not by themselves meet | ||
| the admission proof. The record therefore expects no emitted path for that | ||
| **reported snapshot**, not for every version of Claude Code. This is expected | ||
| workload selectivity, not evidence that the cache is broken. The rule also | ||
| admits constants and bounded remainders; it is not restricted to counted loops. | ||
|
|
||
| ## Recheck a workload | ||
|
|
||
| From the repository root, using the compiler whose behavior you want to audit: | ||
|
|
||
| ```sh | ||
| PERRY_NO_CACHE=1 PERRY_NO_AUTO_OPTIMIZE=1 PERRY_LLVM_KEEP_IR=1 PERRY_CONCAT_SITE_CACHE=1 \ | ||
| perry compile benchmarks/suite/bench_object_property.ts \ | ||
| --no-link --keep-intermediates -o /tmp/concat-on.o 2>/tmp/concat-on.log | ||
| PERRY_NO_CACHE=1 PERRY_NO_AUTO_OPTIMIZE=1 PERRY_LLVM_KEEP_IR=1 \ | ||
| PERRY_CONCAT_SITE_CACHE=0 \ | ||
| perry compile benchmarks/suite/bench_object_property.ts \ | ||
| --no-link --keep-intermediates -o /tmp/concat-off.o 2>/tmp/concat-off.log | ||
| nm -u /tmp/concat-on.o | rg 'js_string_concat_site_value' | ||
| nm -u /tmp/concat-off.o | rg 'js_string_concat_site_value' | ||
| ``` | ||
|
|
||
| The last command should have no match (exit 1). `PERRY_NO_CACHE` forces fresh | ||
| code generation; `--no-link` makes this an object-level check. For a module | ||
| graph, inspect every generated object and every retained IR path in the log. | ||
|
|
||
| Open the file named by each `kept LLVM IR:` log line. Count **call/invoke | ||
| instructions** to `@js_string_concat_site_value(` and definitions of | ||
| `@perry_concat_site_*` globals. A `declare` line is not a call site; searching | ||
| for the helper name alone gives a false positive. Confirm the disabled arm | ||
| still calls `js_string_concat_value_box` so the negative control is meaningful. | ||
|
|
||
| Keep these evidence levels separate: | ||
|
|
||
| - Retained pre-optimization IR shows whether codegen emitted the lowering. | ||
| - An object reference shows that a call survived compilation. Its absence | ||
| alone cannot distinguish non-emission from later dead-code elimination. | ||
| - Linked-binary symbol inspection must account for stripping and linkage. | ||
| - A counter at the helper entry measures fill-helper executions, not inline | ||
| cache hits. Zero executions alone does not prove the lowering was absent. | ||
|
|
||
| The [existing compiler tests](https://github.com/PerryTS/perry/blob/main/crates/perry/tests/concat_site_cache.rs) | ||
| pin positive and negative admission, the disable switch, and Node parity under | ||
| evacuation. The [runtime lifecycle test](https://github.com/PerryTS/perry/blob/main/crates/perry-runtime/src/gc/tests/concat_site.rs) | ||
| checks that collection rewrites a filled slot. These cover the public admitted | ||
| shape even when the application bundle legitimately has no eligible sites. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| { | ||
| "schema_version": 1, | ||
| "_comment": [ | ||
| "Workload applicability records for codegen mechanisms, starting with #9824.", | ||
| "This is an evidence index, not an exhaustive census or an automated admission gate.", | ||
| "An absent mechanism/workload is unrecorded, not evidence that a lowering never fires.", | ||
| "Update snapshot observations when the compiler, bundle, flags, or admission proof changes." | ||
| ], | ||
| "mechanisms": [ | ||
| { | ||
| "id": "concat_site_cache", | ||
| "introduced_by": "https://github.com/PerryTS/perry/pull/9514", | ||
| "codegen": "crates/perry-codegen/src/concat_site_cache.rs::try_lower_concat_site_cached", | ||
| "caller": "crates/perry-codegen/src/lower_string_concat.rs::coerce_concat_body", | ||
| "runtime": "crates/perry-runtime/src/string/concat_site.rs", | ||
| "fill_symbol": "js_string_concat_site_value", | ||
| "global_prefix": "perry_concat_site_", | ||
| "compile_time_disable": "PERRY_CONCAT_SITE_CACHE=0", | ||
| "admission": { | ||
| "left": "HIR Expr::String literal", | ||
| "right_alternatives": [ | ||
| "Compile-time integer in 0..=255, including supported constant arithmetic and integer-constant locals", | ||
| "LocalGet with a loop-induction interval whose lower bound is nonnegative and upper bound is at most 255", | ||
| "Remainder x % C with a compile-time integer C in 1..=256; negative remainders use the runtime plain arm" | ||
| ], | ||
| "maximum_proven_value": 255, | ||
| "cache_slots": 32, | ||
| "runtime_hit": "Integral numeric value in 0..31 with a filled slot; ordered comparisons reject NaN and boxed non-numbers" | ||
| }, | ||
| "workloads": [ | ||
| { | ||
| "id": "bench_object_property", | ||
| "source": "benchmarks/suite/bench_object_property.ts", | ||
| "expected_lowering": "emitted", | ||
| "why": "The field_ literal is concatenated with j bounded by FIELDS=20; the constant FIELDS-1 is admitted too.", | ||
| "observation": { | ||
| "date": "2026-09-05", | ||
| "codegen_revision": "d36a1af0c205ebdc7cf7f75b351ea34b5bc0fc0b", | ||
| "target": "aarch64-apple-darwin", | ||
| "method": "Fresh no-link compilation, retained pre-LLVM-optimization IR, and nm -u on the generated object", | ||
| "enabled": { "site_tables": 3, "fill_call_sites": 3, "object_fill_reference": true }, | ||
| "disabled": { "site_tables": 0, "fill_call_sites": 0, "object_fill_reference": false }, | ||
| "control": "Both arms retain calls to js_string_concat_value_box; these are code-presence observations, not execution counts or timing results." | ||
| } | ||
| }, | ||
| { | ||
| "id": "cc_parity_bundle", | ||
| "expected_lowering": "not_emitted_on_reported_snapshot", | ||
| "why": "The reported bundle has no retained sites meeting this admission proof; its concat volume alone does not imply eligibility.", | ||
|
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Align both evidence records with the available proof. The Claude snapshot shows no helper executions and no surviving linked-binary reference, but it does not distinguish non-emission from later elimination without retained pre-optimization IR.
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| "observation": { | ||
| "date": "2026-09-05", | ||
| "provenance": "reported in issue #9824; not a fresh measurement made by this record", | ||
| "source": "https://github.com/PerryTS/perry/issues/9824#issuecomment-5554137476", | ||
| "bundle": "cli_2.1.112.js", | ||
| "compiler_snapshot": "c7361c87c plus perf/for-in-deferred-shadow-set (#9823), as reported", | ||
| "workload": "offline mock API, one 400-character streamed reply, chunk 100", | ||
| "runtime_fill_calls": [0, 0, 0], | ||
| "linked_binary_fill_symbol_present": false, | ||
| "limit": "No artifact hash or retained IR is supplied here. Symbol absence proves no surviving reference in the inspected artifact; inspect pre-optimization IR to distinguish non-emission from later elimination. Re-audit another compiler or bundle." | ||
| } | ||
| } | ||
| ], | ||
| "regression_tests": [ | ||
| "crates/perry/tests/concat_site_cache.rs::site_cache_fires_and_matches_node_under_evacuation", | ||
| "crates/perry/tests/concat_site_cache.rs::admission_follows_the_proven_bound", | ||
| "crates/perry/tests/concat_site_cache.rs::kill_switch_restores_the_plain_helper_and_stays_correct", | ||
| "crates/perry-runtime/src/gc/tests/concat_site.rs::test_filled_slot_is_rewritten_by_a_copied_minor" | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Require a positive result for the enabled object.
The procedure only requires no match in
/tmp/concat-off.o. It does not require/tmp/concat-on.oto containjs_string_concat_site_value. A broken enabled build can therefore pass the documented negative check. Add an explicit positive assertion for the enabled artifact, then keep the disabled artifact as the negative control.🤖 Prompt for AI Agents