kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 8199c01966dbfe93f649fe2d817329d1aa426b42
parent 508a6344ac3365fcf0fe738853fa8ab690f54776
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 14 Jun 2026 05:58:09 -0700

doc(plan): FRONTEND-SHAPE §D — commit to Path B (lean hot-path representation), no layer fusion

The §4.3 tiers measured ~1.2% combined; Path A (precompute/cache) spiked marginal
on both levers (per-token loc, classify-once types). Conclusion: the cost is
operation-count + dispatch structure, not per-item work. Decision recorded: halve
instructions only via a lean wholesale hot-path representation (lean token,
int-bitmask type on a lean value slot, bypass the general CG type/value-stack
machinery), keeping lex|pp|parse|CG|emit as distinct streaming modules — a
representation pivot, not control-flow fusion. Next step is a minimal end-to-end
lean prototype to validate the compounding hypothesis before building the lane.

Diffstat:
Mdoc/plan/FRONTEND-SHAPE.md | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+), 0 deletions(-)

diff --git a/doc/plan/FRONTEND-SHAPE.md b/doc/plan/FRONTEND-SHAPE.md @@ -14,6 +14,101 @@ whose headline finding reframes the work: **most of §4.3 is already landed.** T one genuine-but-bounded structural change. Each tier below is independently landable and individually gated. +> **Superseded for the instruction goal (2026-06-14).** The §4.3 tiers below were +> built and measured (three parallel worktree branches, all byte-identical) — they +> are real but **marginal**: Tier 1 (1A+1B) −0.70% on `-c`, Tier 2 −0.49%, Tier 3 +> instruction-neutral; ≈ **−1.2% combined**. They remain worthwhile *cleanup*, but +> they do not move the real number. The committed direction for the actual target — +> **halving `-c` instructions (within 1.5× of tcc)** — is **§D** immediately below. +> Treat §0 and the Tier plan as the landed/landable cleanup record beneath it. + +--- + +## D. Direction (2026-06-14): halving instructions — Path B, no layer fusion + +The goal is no longer "reach §4.3's shape" (mostly landed, and marginal). It is +**halve `-c` retired instructions** — 1.91 B → ~0.95 B, i.e. within 1.5× of tcc +(0.665 B). This section records what governs that, what was ruled out, and the +committed bet. + +### D.1 The arithmetic that governs everything + +| phase | kit `-c` | share | vs tcc | +|---|--:|--:|--:| +| **lex + pp** | **~0.93 B** | **49%** | ~2.8× | +| parse + sema + types + CG-drive | ~0.71 B | 37% | ~3.5× | +| native emit + objwrite | ~0.27 B | 14% | ~7× | +| **total** | **1.91 B** | | **2.87×** | + +**kit's preprocessor alone (0.93 B) is 1.4× tcc's entire compile (0.665 B).** So +the target is unreachable by improving parse/types/codegen alone — even zeroing +emit leaves 1.64 B (2.5×). Halving the total means **roughly halving every phase**; +the cost is **uniform ~2.9× with no single dominant hot function** (the doc's +long-standing observation). That uniformity is the whole problem: there is nothing +to "crush," only a pervasive per-token / per-operation tax to remove everywhere. + +### D.2 What was ruled out (measured, not assumed) + +- **Micro-opts / seam-preserving copy elimination — exhausted.** The three §4.3 + tiers move `-c` ~1.2% combined. This *is* the data point: the incremental surface + is spent. +- **Path A (precompute / cache) — DEAD, spiked marginal.** Two validation spikes: + *lever 1* — kill per-token `SrcLoc`, recover location lazily — **marginal**; + *lever 3* — classify-once type facts (kill the `unalias`/`pred_bits`/`class`/ + `size` re-derivation), carried inline — **marginal**. The spikes prove the cost + is **not the per-item work** (which caching removes) but the **operation count + and the dispatch structure itself**. Making each operation cheaper is futile when + the number of operations is the tax. +- **Layer fusion — ruled out by decision** (see D.3). Collapsing lex→pp→parse→emit + into one mega-pass would buy the uniform inner-loop win, but at the cost of the + architecture's legibility and reuse (the shared cpp backing `cc -E`/`cpp`, the + separable parser, the CgTarget seam + seven backends). Not pursued. + +### D.3 The committed bet: Path B = a lean hot-path *representation*, not control fusion + +**Decision: Path B only, and explicitly without layer fusion.** Path B is a +**representation pivot, not a control-flow pivot.** + +- **Lean wholesale representation.** The C `-O0` hot path stops paying for + generality: a **lean token** (no per-token heft/loc-stamp), an **int-bitmask + type** carried directly on a **lean value slot**, bypassing the general CG type + universe and the public `ApiSValue` value-stack API. The wager is that the lean + representation's savings **compound when adopted wholesale** — the inner loop gets + uniformly cheaper across *all* phases at once — in a way the incremental spikes + (a lean *cache layered on top of* the heavy objects) structurally could not show. +- **No layer fusion.** The pipeline stays factored: **lexer | preprocessor | parser + | CG | emit remain distinct streaming modules**, boundaries intact. The shared + cpp still backs `cc -E`/`cpp`; the parser stays separable; the `CgTarget` seam and + the seven backends stay. We do **not** merge the stages' control flow. **The win + comes from the data that flows, not from merging the loops.** + +In one line: **replace the heavy general data objects with lean ones on the hot +path; leave the modules that pass them around exactly as separated as they are.** + +### D.4 The load-bearing risk — validate before building the full lane + +The spikes were marginal; Path B bets that **wholesale adoption compounds where +incremental did not.** That bet is **unproven**, and Path B is expensive (a parallel +lean lane plus a fast/slow split to gate). So the next step is **not** to build the +lane — it is to **build a minimal end-to-end lean prototype** (a reduced-grammar +path carrying lean token + int-type + lean value slot from bytes to emitted bytes) +and measure whether the compounding actually appears on a representative slice. If +it does not, Path B is also marginal and the honest conclusion is that **~2× is the +floor for a modular C compiler** and we stop there. This validation gates the spend. + +### D.5 Open design questions for the lean lane (after D.4 validates) + +- **Inter-layer handoff without fusion:** how separate modules pass the lean + representation cheaply (a shared cursor over a lean token buffer? a leaner pull?) + — killing the per-token call/copy tax *without* collapsing the loops. +- **Int-bitmask type on the hot path:** the encoding, and how it **falls back** to + the full `Type` / CG-type universe for what a bitmask can't carry (aggregates, + bitfields, VLAs, `_Atomic`, wide/`__int128`). +- **Lean value slot:** what the parser drives that still resolves to the **kept** + `CgTarget` seam for emission (the seam is cheap — indirection was never the cost). +- **Fast/slow split + gate:** which constructs take the lean lane vs fall back, and + how the gate proves the two lanes emit **byte-identical** output. + --- ## 0. Verified current state (what §4.3 already bought)