kit

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

commit f2841b12dee3f171dfab2888e0ebc619060a50b3
parent 4fa49d55d7bc1ccb2918c81505c9d0f26002e18c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 11 Jun 2026 14:53:47 -0700

docs(perf): 16B Tok is a compile regression, not neutral — dead end

Built + merged the 16B-Tok foundation onto HEAD (byte-identical, gate
clean) and measured it on a quiet machine (best-of-15): it SPLITS —
pp-macro -E +3.3% but type-decl -7.1%, body-size -3.4%, fn-count -2.8%.
The earlier 'neutral' was load-noise. The compile path is loc-heavy, so
every token pays the LocId indirection on both ends (lexer interns a 12B
store, parser resolves via a cross-TU call), outweighing the smaller-Tok
copy saving; -E wins only by passing tokens through without resolving.
The planned lexer inline removes only the intern call overhead, not the
store/resolve work that IS the regression, and the clean inline would
require exposing libkit's loc-table to lang/cpp (boundary violation
anyway). Tok is shared by -c and -E and -c dominates -> net loss.
Recorded as a dead end.

Diffstat:
Mdoc/plan/PERF.md | 27++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/doc/plan/PERF.md b/doc/plan/PERF.md @@ -199,15 +199,24 @@ design). Ranked by (impact × confidence) / risk: reloc/ABI handling and variable-length x86 encoding). Worth it only as a deliberate big bet on the dominant bucket. (Spike + numbers persisted under `build/perf-nonbid-analysis/codegen-copypatch-spike.md`.) -2. **16-byte `Tok` (register return)** — *implemented and byte-identical* (a - lossless 32-bit `LocId` side-table replaces the inline 12 B `SrcLoc`, so - diagnostics stay exact and it passes the full byte-id gate), and it does - achieve the `x0:x1` register-return ABI (no x8 sret). But it measured - **neutral**: the two per-token `kit_loc_intern` calls it adds cost about as - much as the sret copy they remove. To flip it positive, **inline the loc-table - append into the lexer's hot loop** (publish the append cursor so a - static-inline fast path does one bounds-compare + store, out-of-line only on - grow). The clean 16 B `Tok` foundation is the prerequisite and is done. +2. **16-byte `Tok` (register return)** — *implemented, byte-identical, and + rejected on measurement.* The lossless 32-bit `LocId` side-table makes `Tok` + 16 B (exact diagnostics, passes the full byte-id gate) and achieves the + `x0:x1` register-return ABI. But on a **quiet** machine (best-of-15; the + earlier "neutral" was load-noise) it **splits**: pp-macro `-E` **+3.3 %** but + type-decl **−7.1 %**, body-size **−3.4 %**, fn-count **−2.8 %**. Reason: the + compile path is loc-heavy (a loc per statement + `.loc` on every AST node), so + each token pays the `LocId` indirection on *both* ends — the lexer interns + each loc (a 12 B store into the table) and the parser resolves it (a table + lookup + a cross-TU `kit_loc_resolve`) — which costs more than the smaller-`Tok` + copy saves; `-E` wins only because the pp passes tokens through without + resolving. The planned "inline the loc append into the lexer" does **not** fix + this: it removes only the intern *call overhead*, not the per-token store work + or the parser-side resolve cost, which are the regression. And the clean inline + is blocked anyway — it would require exposing libkit's loc-table layout to the + `lang/cpp` frontend, a boundary violation. `Tok` is one type shared by `-c` and + `-E`, so it is all-or-nothing, and `-c` dominates → net loss. **Dead end; do + not revive.** 3. **Keyword classify-once** — *partly landed.* The real residual the `KwMap` missed was `parse_decl_specs` re-deciding keyword-ness ~27×/token (26 `is_kw` + a trailing `ident_kw`); a single alias-aware `classify_kw()` at the loop top