Skip to content

[Feature] Reduction-aware inlining - #43

Merged
katsujukou merged 6 commits into
mainfrom
feat/reduction-aware-inlining
Jun 17, 2026
Merged

katsujukou merged 6 commits into
mainfrom
feat/reduction-aware-inlining

Conversation

@katsujukou

@katsujukou katsujukou commented Jun 17, 2026 •

Copy link
Copy Markdown
Collaborator

What & why

Makes the NbE reducer (Semantics, ADR 0020) share work instead of recomputing it, and bounds the one case sharing can't help (genuine code-size blow-up). Together these let the optimized (opt-ON) self-compile of PursWasm.CLI.Main complete — it previously hung indefinitely at Optimize.Specialize.

The hang had two independent causes, both fixed here:

  1. NbE recomputation (exponential). eval re-evaluated each inline-set binding per use site (M1), and quote re-evaluated shared values per path (M2) — Θ(2^d) on a diamond-shaped inline DAG.
  2. Code-size blow-up at Specialize (module 276). Two sub-causes: the specialization dedup key was the show-serialized lambda body (multi-KB strings built + retained as Map keys), and NbE fully inlined the derived genericShow dictionary of the large IR ADTs into multi-million-node normal forms.

What's included

ADR 0035 — sharing the NbE reducer:

  • Layer A (b12f633) — memoize each inline-set binding's eval (Data.Lazy keyed by binding name). Kills the M1 re-eval. Byte-identical output.
  • Layer B (d1d26f8) — SShared constructor: evalVar tags inline unfolds; unShared strips it at every consumption site so it never blocks a redex; quote CSEs shared values into a hoisted let. Kills the M2 re-quote. Diamond DAGs now normalize in O(d).
  • Layer C lite (cc11c18) — DictElim.normalFormSizeCap: when a declaration's reduced form exceeds the cap, re-reduce with the inline context emptied (the binding stays an ordinary call — the --no-opt-correct shape). Bounds the genericShow-into-show blow-up. Plus Specialize.canonicalKey now hashes the canonical form (Serialize.Hash.hashString) and abstracts free vars in a single substMany pass instead of show-ing it per free var — removing the giant-string Map keys.

Tests:

  • Test.NbeStress — a synthetic diamond-DAG guard (depth-20 normalizes to linear size, fails loudly if the exponential returns) + two Layer-C cap guards (fallback above the cap; still inlines below it). Wired into Test.Unit.Compiler.

Docs:

  • ADR 0035 promoted to Accepted (Layers A+B+C-lite), ADR 0020 progress note, README index.
  • ADR 0037 (new, Accepted as a design record — not implemented): the per-module-codegen / separate-compilation direction explored this cycle (singleton GC rec-groups, boxed module boundary, .pmi/.pmo-as-ANF, wasm-merge link). Included here as the recorded rationale; no code change behind it.

Verification

  • Optimized self-compile of PursWasm.CLI.Main completes — writes a valid 8 MB index.wasm (previously hung at Optimize.Specialize).
  • Unit 162/162, e2e 150/150, bin 12/12 (incl. showShadow/intGenericShadow/examplesRun — the genericShow path).
  • Bench: no regression (some benches faster from Layer-B CSE sharing; rest within noise; baseline untouched). The Layer-C cap doesn't trigger on the bench corpus (no decl approaches the cap).
  • examples/metatheory compiles and runs.

Byte-equality is not required (the current compiler isn't a verified reference); the gate is test-suite + bench + examples no-regression.

Checklist

CI passing is enforced by the required ci-gate status check, not by a box here.
The items below are the human-judgment gates:

  • Docs updated — any behaviour/feature/representation change is reflected in docs/ and relevant ADR(s).
  • Tests — added/updated for this change; a bug fix includes a regression guard in the routinely-run lane (unit / e2e), not only a slow test:bin script.
  • Design changes have an ADR — added or updated, with its Status set (ADR-first).
  • No perf regression — for optimizer / runtime / lowering changes, benchmarks compared (node bench/run.mjs <dir>; do not overwrite bench/snapshots/baseline.json). N/A otherwise.
    - [ ] Runtime GC-type / ABI / canonicalization — changes to the value-type substrate or the host/runtime ABI keep cross-module type canonicalization intact. N/A otherwise.

@katsujukou katsujukou self-assigned this Jun 17, 2026
@katsujukou katsujukou added bug Something isn't working feature performance labels Jun 17, 2026
@katsujukou
katsujukou merged commit 6e45fb8 into main Jun 17, 2026
14 checks passed
@katsujukou
katsujukou deleted the feat/reduction-aware-inlining branch June 17, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working feature performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant