Skip to content

the iquant Metal kq catch-up: kernels at llama.cpp speed, sidecars cache-pinned, tune boot hardened - #3919

Merged
borisbat merged 35 commits into
masterfrom
bbatkin/metal-kq-race
Sep 1, 2026
Merged

the iquant Metal kq catch-up: kernels at llama.cpp speed, sidecars cache-pinned, tune boot hardened#3919
borisbat merged 35 commits into
masterfrom
bbatkin/metal-kq-race

Conversation

@borisbat

@borisbat borisbat commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

ABI break: Program gains a moduleCacheDependencies field and the AST serializer moves to version 118 - external module repos must rebuild their shared_modules against this tree.

The nine iquant Metal kq kernels now run at or above llama.cpp speed on both Metal tiers: M5 mean 1.20x (125/144 cells at 1.0 or better), M4 mean 1.11x (133/144), and every iq2/iq3 format clears 1.0 on both boxes and both tiers. The win is the cvt compose - a float-expanded constant grid word with select-built signs replacing the integer decode chains - promoted across the gemv bases, the mvb batch arms, and the iq3 tensor stages, plus f4-slab gemv twins for iq3s, iq3xxs, and iq2xxs behind runtime crowns.

The races that mint those crowns got fidelity rules: different output buffers per dispatch (a shared output serializes on its hazard and once flipped a verdict by 17 points), a bounded burn phase before the first timed round, back-to-back dispatches. The iq2xxs f4 crown is not auto-raced at all - every isolated regime crowns a twin the served graph rejects - so a serving-confirm rail decides it on the real model: two manifests differing only in the crown, tg128 the verdict. The mechanism lives in ARCHITECTURE_MEASUREMENT sec. 2.21.

Three tune-boot traps are fixed. Tune sidecars are now content-hash-pinned into the module cache: a macro that reads a file at compile time registers it with the new add_module_cache_dependency builtin, and a re-minted sidecar invalidates the cached [tuned] stamps that were the silent failure. A sidecar perm outside the grid falls back loudly instead of failing the boot. Class default profiles ship: arm-i8mm (M4/M5 class) minted on M5 and committed; arm-neon re-minted fresh on M1 at this tip, 49 of 49 winners identical to the committed profile.

Where to look: the F4 template class and enc_kq_gemv in dasllama_metal_kernels.das; race_pair_ms in dasllama_metal_common.das; confirm_serving_crown in harness/tune_kernels.das; addModuleCacheDependency in module_builtin_ast.cpp and the record-header validation in ast_parse.cpp.

Validation, claims, ledger

Validation

  • Local model suites CI cannot run: the full dasLLAMA chain on M5 (kernels, model-free, decode arm1+arm10, prefill base+kq, support matrix including the new iq2xxs tensor-twin row, coverage) - 0 failed, 0 errors everywhere; an M4 broad-arm sanity sweep at this tip - 11/11 suites, 0 failed; the gemv/gemm kernel-unit suites on both boxes.
  • The coverage census cell reports SKIPPED on the default tier by design (partial-zoo honesty); its floors and the declared never-dispatched list reconciliation passed.
  • Woodpecker: two rounds, both findings verified and fixed in-branch (the Windows drive-relative dep path, the unbounded burn loops).
  • Preflight full tier: 22 gates green; the one red (the race lab on the metal-less lint lane) was fixed and re-validated on all three lint rails with the targeted gate.
  • arm-neon class profile: fresh M1 mint at the PR tip, 49/49 winners identical, file unchanged.

Claims - stated, not tested

  • Bind numbers and kargs word layout of race_gemv_f4_twin, race_kq_mulmm, and the race lab's time_arm_us were hand-verified against the class declarations, and independently re-verified in review. A break would race the wrong buffer and crown the wrong kernel.
  • The Windows arm of test_tuned's env spelling mirrors the proven env_cmd pattern; no Windows box was in this loop. A break would show as the stale-sidecar cell not seeing DAS_TUNE_MANIFEST on Windows CI.
  • Seven iquant mulmm crown families are witnessed by kernel-unit gates plus mint METAL_TWIN rows; the matrix suite's twin row covers iq2xxs end to end. A twin serving garbage would fail the unit gate's oracle bar.
  • The k2 gemv's rows/8 grid over-dispatches on a d not divisible by 8; every served d is an 8-multiple. A non-multiple shape would fail the kernel-unit gate at that shape.
  • The drive-relative Windows dep fix has no direct test; Windows CI exercises the new GetFullPathName path through every relative-path dep. A break would serve stale [tuned] stamps after a re-mint under a drive-relative manifest path.
  • The rewritten checklists took two audit rounds each; fresh cold re-reads after the final fixes were skipped by calibration, and the small src/builtin and utils/mcp rule edits were not re-audited.

Not done

  • plans/dasllama_review_grooming.md - audit findings on rules this branch never touched, one grooming sitting per document, plus the recommended lint/gate candidates.
  • followup_metal sec. 4a (the elementwise _hx lane; measured knockout is +0.65% pp512) and sec. 5 (tall/db tensor stamps for the deep-K w2 column).
  • The tests/CLAUDE.md to tests/ARCHITECTURE.md rename (until then the tests checklist's section cites point at a CLAUDE.md).
  • k4/k5 f32-activation gemv pockets stay off-board: the served q8-activations path wins end to end.

🤖 Generated with Claude Code

https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw

borisbat and others added 30 commits September 1, 2026 08:28
…end-ops ladder patch

Rig 1 of the Metal kernel catch-up: races production decode GEMV and prefill
mul_mm of all twelve kq formats on synthetic planes (no model, no tuner),
dispatch-loop timing per QUIRK 22, whole-output dot-envelope oracle before any
timing, arms interleaved round-robin. --ref pins a llama.cpp test-backend-ops
row per (fmt, tier, shape); the patch replays the ladder onto the b10659
reference worktree (GGML_KQ_RACE_LADDER=1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… subscript-store claim, raced

Rewrites the production stamp's ta[IDX] = RHS threadgroup store into the pointer
form at lab runtime and races it. M5 verdict: FLAT (0.997-1.013x, oracle green
both arms) - the claim does not reproduce on this compiler; M1/M4 re-race owed
before any production spelling change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…eat llama.cpp on M5

MetalKqMulMmSplitTensorBase: the k6 tensor shell with the per-format decode as
ONE overridable stage16 (the MoE tensor family's scaffold shape). Each format's
base-template arm ports verbatim - va[] store retargeted to the twb tile. T
(f32 X) and TH (f16 panel) stamps per format; iq3s/iq3xxs stage their grids,
iq2* read the hoisted constant tables direct, exactly their base arms' choices.
Shared decode helpers (iq4_lut -> metal_common; grid tables + ksign7m public)
now serve both the kernels file and the prefill twins.

M5 lab race (dispatch-loop, oracle-green, vs llama.cpp b10659 tensor mul_mm):
k2 1.30x, iq2xxs 1.17-1.21x, iq2xs 1.19x, k3 1.13-1.19x, iq4nl 1.11-1.15x,
iq2s 1.12x, iq4xs 1.04-1.08x, iq3s/iq3xxs 0.97-0.98x - from 0.39-0.43x on the
base stamp (2.4-3.1x per format). Not yet wired: dispatch/PSO/site gates,
crowns, gemm-test tensor arms - the twins are lab-raced only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…ows, test arms

Per format: a kq_mulmm_<fmt> crown gates T/TH PSO compiles
(pf_compile_kq_iquant_tensor_twins), the pf_enc_kq_site_mm early-return arms
pick TH/T when crowned, release rows, and a race_kq_mulmm row in
metal_tensor_race (the fixture's k6 bool generalized to a scale-form enum:
k45 blocks / k6 split / k2 split / iq4nl d-plane) so the box mint crowns them.
test_metal_gemm_kernels gains T + halfx-TH cells for all nine (suite 2/2).

M5 e2e (lcpp_bench --ngl 99, crowns armed, debug-jit): iq4xs pp512
6364 -> 15147 t/s (0.85x lcpp, was 0.36x); iq2xxs 15165 (0.86x). tg unchanged,
sanity tokens unchanged. The ~0.85x residual is graph-level - lcpp's attention
also rides tensor on M5 while our attn_avmm race crowns simdgroup - ledgered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…l three at/above lcpp on M5

The q4_K lane map (8 lanes/superblock, one whole 32-block per thread, ib += 4)
with the f4 magnitude slab where it fits: iq2xxs d1f4 (8 KB slab) 1.22-1.56x
over prod -> 0.97-1.16x vs lcpp; iq2xs d1f4 (16 KB) 1.09-1.43x -> 0.95-1.14x;
iq2s d1 (constant reads - its slab would be 32 KB, lcpp skips staging it too)
1.12-1.30x -> 0.97-1.13x. Row-count knob raced: 2 rows/sg (the k4 choice) beats
4; the raw packed-word slab is flat (pre-EXPANSION is what pays, not staging).
M4 race owed before promotion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…f4 crown twin

iq2s/iq2xs/iq2xxs move to the q4_K lane map (8 lanes/superblock, one 32-block
per thread, 2 rows/simdgroup, ib += 4, dispatch rows/4) - interleaved wins on
BOTH boxes (M5 1.07-1.43x, M4 1.07-1.22x over the old 16-lane forms). iq2xs
additionally stages its grid as a 16 KB f4 magnitude slab (wins both boxes).
iq2xxs's 8 KB f4 slab is generation-split (M5 +12-22%, M4 -4-16%), so it ships
as MetalKqGemvIq2xxsF4 behind a NEW box-raced crown 'kq_gemv_iq2xxs_f4'
(race_gemv_iq2xxs_f4 in metal_tensor_race_decode; both call sites pick by the
compiled-under-crown PSO). gemv suite 2/2 incl. the new f4 twin cells; every
lab arm oracle-green. Lab: the promoted forms replace their candidate arms,
gdiv 4 for the trio.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…ock governor artifact

A sparse invocation's encoders start at sunk clocks (the CPU-side oracle gaps
between cells let the governor race to idle; a 50-dispatch encoder finishes
before it ramps) and read 2-3x slow - the dense full sweep never idles, which
is why the baseline was clean and every short rerun read worse. Not thermal:
no pmset event, and shorter runs degraded more. The burn spends ~150 ms of GPU
time on arm 0 before each cell's timing; a sparse k4 control returns to 1.03x.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…asks, prefolds, rows/8) on our planes; M5 1.27-1.44x over prod -> 1.16-1.20x vs lcpp

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…oxes win big

The k4 lane map with u16 shift-free masked compose (power-of-two prefolds, the
dmin fold on unshifted high nibbles), 4 rows/simdgroup x 2 sg, dispatch rows/8;
quant binding moves to the uint view. Interleaved: M5 1.27-1.44x over the old
k6-map form (-> 1.16-1.20x vs lcpp), M4 1.60-1.85x (-> 1.18-1.21x). gemv suite
2/2; lab k2 gdiv 8, candidate arm retired.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…EEP the crown (d1f4 0.85-0.99x of prod, d1 0.64-0.85x); M4 race owed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… become crowned twins

The generation split repeats the iq2xxs pattern in reverse: the shipped f4-slab
GEMVs win on M5 (1.26-1.36x vs lcpp) but lose to the d1 lane-map constant form
on M4-class (d1 1.18-1.26x over them). So the base is now the d1 form
(dispatch rows/4, no slab - lifts M4 iq3s 0.71->0.89x, iq3xxs 0.56->0.74x vs
lcpp) and the previous kernels ship verbatim as MetalKqGemvIq3sF4/Iq3xxsF4
behind box-raced crowns kq_gemv_iq3s_f4/iq3xxs_f4. race_gemv_iq2xxs_f4
generalizes to race_gemv_f4_twin (three crown rows); M5 races all three to the
twin (iq3s +27%, iq3xxs +46% over d1). Suite 2/2 with the twin cells; the
iq3xxs d1 walk also recorded the signed-strip trap (unsigned read = 10x-scale
garbage, oracle-caught).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… block

A single sparse dispatch reads idle-clock times and flipped the iq2xxs f4 crown
on M4 (the fixture ran at ~17 GB/s where warm kernels do ~90); the dense block
keeps the governor ramped so the race verdict matches the lab's interleaved one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…fn-up class)

The mid-size square fixture crowned the M4 iq2xxs f4 twin that the served
shapes reject in the lab's interleaved races; the crown must be minted on what
the graph actually dispatches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…t order pays the clock ramp the twin rides

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…settled by e2e

The same-y serialized chain crowned the M4 iq2xxs slab twin (+9% raced) that
serving rejects (tg128 189.6 -> 173.3, -8.6%, measured); alternating two y
buffers per side lets dispatches overlap like the real graph. Also the ptrstore
lab comment loses the quoted upstream aside.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…alid mint

Every isolated regime (dense, burned, overlapped ys, the served shape) crowns
the M4 twin that serving rejects (tg128 -8.6% measured); M5 serving confirms
its crown (+12.6%). A mixed-stream occupancy effect no kernel race sees - the
crown is minted by an e2e tg A/B per box (M5 box profile carries it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… format closes

One masked uint-wide qv per 4 elements replaces the per-element bit-compose in
the K45 tensor template's QH arm (both the single-tile and Db templates; the k4
non-QH path unchanged). Raced in the kq lab: 1.40-1.48x over the shipped twin
-> 1.13-1.16x vs llama.cpp on M5 (was 0.78-0.82). gemm suite 2/2; the promoted
twin equals the lab candidate, candidate retired.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… sw/sx chain - the whole family beats llama.cpp on M4-class

The lab decomposed llama.cpp's iq3xxs recipe one lever at a time on the d1 map:
threadgroup-staged grid words (flat), a staged ksigns table (+5%), their nr0=4
geometry (-4%) - and none of it was the gap. The gap was our compose: iq3s_sw/sx
spend ~20 integer ops + 4 sign-extending extracts per 4 elements, where the f4
twin's arithmetic (float-expand the grid word, multiply a select-built sign
vector) does it in half. Feeding that arithmetic straight from the constant
table - no slab, no barrier - wins everywhere the chain ran: iq3s, iq3xxs,
iq2s, iq2xxs, and iq2xs (which also drops its 16 KB threadgroup slab and its
tgmem dispatch row).

Race deltas vs the old bases at w13_1b (2048x8192): M4 +32/+41/+37/+43/+23%,
M5 +16/+24/+1/+26/+21% (iq3s/iq3xxs/iq2s/iq2xxs/iq2xs). Every iq2/iq3 format
now clears llama.cpp on M4-class at the gemv tier (1.03-1.45x); M4 iq2xxs
serving confirms e2e: tg128 189.6 -> 235.1 (+24%).

Crown court after the promotion (M5): iq3s_f4 +5.5% and iq3xxs_f4 +20.5% keep
their crowns; the iq2xxs_f4 e2e A/B re-ran at 519.8 vs 516.5 - the crown holds
by a thread and stays e2e-minted. gemv+gemm suites 2/2 on both boxes (via
DAS_TUNE_POLICY=reference: the committed m5.tune.json pins dot_q8kv=vec4_u2,
a perm the current grid no longer offers - pre-existing, owed to the box
re-mint deliverable). Lab probe classes retired after promotion; the stale
pre-d1 class comments and the crown racer's _r lint fixed in passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… tier

The gemv-promoted float-expand + sign-select compose lands in the ten
batch-decode kernels of the five grid formats - the B2/B4 templates and the
B8 X-panel kernels for iq3s, iq3xxs, iq2s, iq2xs, iq2xxs. Raced at B2 (the
most compose-sensitive width - the w vector amortizes over only two columns,
and the MTP-served one): M5 +11-19%, M4 +3-7%, all ten cells oracle-clean;
B4/B8 share the identical per-superblock compose and only amortize it wider.

The lab's new mvb tier races the 2-column batch kernels on the same
plane/oracle plumbing - gdiv 8 reuses the gemv dispatch shape, KqMvArgs rides
binding 5 as a 4-word buffer, and the 2-column oracle layout matches
y[col*ys+row] verbatim. No lcpp reference rows by design: their bs 2-8 runs
the GEMV once per column for these formats, so there is nothing to pin.

Also closes the fastmath-asymmetry ledger item: the k4 gemv raced
fastmath=false (llama.cpp's compile mode) vs our default on both boxes -
0.99-1.00x flat; the asymmetry neither hides the M4 k4 tail nor inflates
any win. Lab cvt twins retired after promotion; gemv suite (incl. the
B2/B4/B8 oracle cells) 2/2 on both boxes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…ub-1.0 M5 mm formats cross the line

The staged grid slab stays; only the per-16 compose changes - the integer
sw/sx sign-apply-and-extract chain becomes float-expand of the staged word
times a select-built +-dsc vector, four half stores from its lanes (the k5
byte-parallel precedent, iq3-shaped). Raced on M5 at w13_1b: iq3s
0.973 -> 1.014x lcpp, iq3xxs 0.968 -> 1.052x. With this every kq format's
crowned mm form clears llama.cpp on M5. gemm suite 2/2 on both boxes; lab
candidates retired after promotion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…laim is settled

The threadgroup A-tile pointer-store spelling raced dead flat against the
subscript form on M5 and M4 across every mm format; this compiler emits the
same code for both. The source-rewrite arm and its narration go; the verdict
lives in the arc record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…he q8q8 crown

The probe gate passed (correctness OK, 2.02x MAC ceiling over sdot4), and the
tile race buried it: best smmla seat ~21% behind NEON mr8_budget once the kg8
re-layout and memory traffic price in. No arm-i8mm defaults export; the smmla
kq-tile emitter idea moves from mac-leverage to a Graviton-raced candidate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… and crown-confirmed on real shapes

Three seams the M5 re-mint exposed, closed together:

1) Macro file dependencies in the module cache (core). A macro that reads a
file beyond the sources - the [tuned]/[tune_policy] sidecar read - now pins it
via add_module_cache_dependency (ast builtin): the record stores (path, size,
content hash) in its header and the reader re-validates before trusting the
payload, so a re-mint invalidates cached modules instead of serving stamps
minted against the old file (the empty-tune_status refusal, or worse, silently
stale winners). Content-hashed, not mtime'd: lcpp_bench rewrites its sidecar
byte-identically on exit, and mtime gating would repay the full parse every
run. Deps round-trip through deserialized records - the stream is rewritten
from parsedModules, and a kept record must not shed them. Serializer version
117 -> 118. llvm_tune's read_manifest registers the pin before its staleness
gate, so a sidecar that first APPEARS also invalidates. Gate test:
tests/module_cache/test_macro_dep_invalidate.das (cold / warm / byte-identical
rewrite stays cached / content change reparses), driven main-line because a
dastest child compiles outside the module cache.

2) A stale sidecar perm falls back instead of bricking the compile. The
manifest is measured data, not law: [tuned] on a sidecar-sourced perm the grid
no longer offers (dot_q8kv=vec4_u2 after the grid rework) stamps the fallback
with a loud line - the tuner that would re-race the entry can always run. An
explicit perm= pin keeps the hard refusal (cant_tuned_bad_pin.das); the
fallback arm is test_tuned's stale-sidecar cell via a spawned client.

3) The undecidable crown is minted on real shapes. kq_gemv_iq2xxs_f4 - the
crown every isolated kernel race gets wrong (M4-class: +9% raced, -8.6%
served) - joins the mint through confirm_serving_crown in tune_kernels: two
temp manifests differing only in runtime.metal_tensor membership, lcpp_bench
tg128 per arm under DAS_TUNE_MANIFEST, margin 1.005, best-of-3. First live
run on M5: 521.66 vs 517.21 tok/s - crowned, matching the hand e2e verdict.
No vehicle on the box = no crown (base is the safe side) plus a provision
hint. This also retires the absorb-drop hazard: the crown is re-decided by
every mint rather than preserved by hand.

dasVulkan's shared_module rebuilt against the new Program layout (the vintage
guard flagged the drift); in-tree shared modules rebuild with the tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
The producer-fused _hx family already banks most of the fusion rung (rms_hx,
add_rms_bhx, swiglu/geglu_hx, qk_rope_hx); the act_cvt knockout measures ALL
remaining converts at +0.65% pp512 on M5. What's left: the attention-out _hx
(the one live dense-path cvt, ~0.3% M5 / more on M1-class, with the q_gated
sigmul ordering caveat), the per-model cvt arms, and the real gap - f16
activation planes end-to-end, its own arc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…ind the race rig, two renames dissolve their comments

The diff's working comments settle per the harvest contract. Three FACT sets
land as anchored architecture sections with LINT026-proven citations: the
kernel-race fidelity conditions and the serving-confirm crown
(ARCHITECTURE_MEASUREMENT.md 2.21, five citers), the iquant GEMV grid read
and its per-box f4-slab twin (ARCHITECTURE_GPU.md 2.2z), and the dense-KQ
tensor mul_mm scaffold (ARCHITECTURE_GPU_PREFILL.md 2.2aa, two citers). The
dasLLVM side lands the tune-sidecar cache-pin mechanism (ARCHITECTURE.md 5)
with its citer and a DAS_ARCH_EXTRACT_SPECS row so the citation survives in
an installed SDK. Two rules join REVIEW_GPU.md: race chains bind distinct
output buffers, and a race burns the clock governor before its first timed
round.

Two renames replace comments outright: race_shapes becomes a named tuple
(name/n/d), and the race fixture's scale-form legend becomes the
KqRaceScaleForm enum. The locked-view argv contract hoists to
get_command_line_arguments' own doc in daslib/builtin.das. A pre-existing
k6 doc line orphaned by an insertion rehomes onto pf_enc_kq_site_mm; a
stale mvb_arms comment and a comment that mis-stated the dep tuple are
fixed; build_and_debug.md's module-cache invalidation claim catches up with
the content-hash pin. followup_metal gains the missing tall/db tensor-stamp
ledger row, tied to the arc matrix's deep-K w2 column. The arm-i8mm class
defaults profile ships from the fresh full M5 mint (49 kernels, the i8mm
seats raced-and-covered), serving every M2+ Apple box including m4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… three new tests, one detwinned kernel

Code, from the seven checklist audits and the TDD round: the 21 crown-gated
kernel classes join the census blind-spot ledger; the twelve crown names join
the support matrix's CROWNED witness plus a skip-aware tensor-twins/iq2xxs row
on the arc vehicle (a local requant no fetch provisions - absence skips, never
reds); both shared kernel gates gain a poisoned-expectation control that must
RED; the Metal-4 capability decline becomes a real skip; the clock-governor
burn moves into race_pair_ms so all eight race callers inherit it; the iq2xxs
GEMV pair collapses into one template with an F4 stamp axis (oracle-proven);
DASLLAMA_CONFIRM_IQ2XXS becomes a declared, announcing knob; plus the
clock-control marker, the iq4_lut charter row, figure provenance in
followup_metal 4, and the falsified arm-i8mm ledger line rewritten.

Tests, closing the TDD round's top gaps: test_tuned gains the wiring cell -
a [tuned] client under -module-cache with a real tune sidecar must reparse
when the sidecar's content changes, through the production read_manifest pin;
test_macro_dep_invalidate gains the absent-then-present phase through a
RELATIVE dep path (the first-mint case the architecture doc calls the one
that matters, plus the cwd-absolutize arm); cant_tuned_bad_fallback pins the
fallback-not-in-grid refusal.

Checklists, per their own audits: the two race rules this PR added rebuilt
property-scoped with the serialized-rig exception ledgered; homeless rules
moved to folders whose walks surface them (census to REVIEW_GPU, bench-list
and harness routing to the dasLLAMA root, ref-pin merged into benchmarks,
consent to REVIEW_EXCHANGE, kept-comments to utils/mcp); the macro-file-pin
duty guards the new cache mechanism; the bit-exact discriminator becomes
bit-identity; the third-party-walls rule splits into its ban and its scratch
property; seven weakening rules collapse into one; and a dozen wording
repairs across tests, dasLLVM, daslib, src/builtin and review_md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…debt gets a ledger

Five rule documents audited as documents, wave by wave. REVIEW_GPU_RACE.md - new this
arc - takes every verdict: the routing trigger widens to kernel-side diffs (a diff that
changes what an arm mirrors never touched an arm, so three rules could not fire from the
route that existed), knockout and kargs defined in place, the selects-alone carve-out cut,
the burn phase named for what it is, the A/B-lab port duty split from the empty-lab
deletion, every dangling referent spelled. REVIEW_GPU.md's routing line widens in step -
the duplicated trigger pair is what the sync gap cost. REVIEW_EXCHANGE.md's moved consent
rule gets its path re-based (the ../ survived the move). performance/REVIEW.md sheds its
two wrap artifacts, daslib/REVIEW.md its three plus the PERF023 doubled locator and the
emit-gloss restatement. harness/REVIEW.md lands at its minimal form: the four-way ledger
routing and the one rule the folder owns.

Findings on rules this branch never touched go to plans/dasllama_review_grooming.md -
one grooming sitting per document, plus the RECOMMEND LINT candidates each dragon named
(the bind-number walk extension, the burn-phase counting cell, the consent-notice
byte-identity check, the stamp-reach glob widening, three utils/lint census cells).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…tops double-burning

The review-md auditor read the new REVIEW_GPU_RACE.md against the diff it binds and found
the diff clean but the rules leaky. Five repairs: the mis-numbering criterion now carries
the gate's real property (bound at a number the class does not declare for that field - the
old wording missed an undeclared number outright); the crown rules' trigger is decidable
(mints a runtime crown or a tune-sidecar row - the serialized per-format rig is sanctioned
in ARCHITECTURE_MEASUREMENT.md sec.2.21 as the instrument's own probe shape); the
port-deletion duty is keyed on the ported variant's own code, so a lab row that dispatches
the shipped class's generated source stops reading as a deletion target; the invisible-arm
PR attestation widens to kargs word layout (the hand-mirrored KqMvArgs words were exactly
the unattested half); the scaffolding rule states the shared-by-two-sites property instead
of enumerating three names. The harness benches-last trigger widens to runtime mutation of
the list.

One code fix the audit surfaced: race_gemv_f4_twin kept its hand-rolled 150ms base-only
burn from before race_pair_ms grew a burn phase - the phase burns both arms, so the local
loop was a slower, one-sided duplicate. Deleted; decode parity re-run green at this tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…C++-side, so the description is handmade

Sphinx clean, zero warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
…dency unabsolutized

C:foo is drive-relative, not absolute - the second-character-colon test stored it raw, and
a validating reader on a different per-drive cwd then stats a different file, or the same
wrong one on both sides, and the dep never invalidates. Windows now resolves through
normalizeFileName (GetFullPathName underneath, which owns the per-drive cwd rules); POSIX
keeps the plain cwd-join.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
borisbat and others added 2 commits September 1, 2026 10:31
A device whose GPU timestamps read 0.0 for short dispatches would spin the ~150ms burn
budget forever - race_pair_ms at every tensor race, the kq lab at every cell. The budget
condition gains a 2048-round bound; on a healthy device the loop exits on budget long
before the cap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
… go generic

The lab's ?das_metal requires were already conditional, but five fully-typed functions
(the three arm builders, the oracle, main_apple) resolved unconditionally, so the linux
lint mirror - where dasMetal's generated msl globals and _metal_kernel_common don't exist -
failed the compile. Untyped-parameter generics instantiate only from call sites, and every
call traces to main's das_metal static_if, so the metal-less lane never resolves them: the
same shape bench_metal_kq_mm_lab already uses. Targeted lint gate green on all three rails;
metal smoke cell unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
Copilot AI lite review requested due to automatic review settings September 1, 2026 18:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens and extends the module-cache invalidation mechanism to account for macro-consumed file inputs (notably tune sidecars), while also advancing the dasLLAMA Metal KQ kernel/race/tune infrastructure and its validation coverage (including new iquant crowns and serving-confirm logic). It introduces an ABI/serialization version bump to support dependency metadata round-tripping through cached AST records.

Changes:

  • Add compile-time “macro file dependency” pinning (add_module_cache_dependency) and serialize/validate those dependencies in the module-cache stream (serializer v118, Program::moduleCacheDependencies).
  • Update dasLLAMA Metal iquant kernel/twin/race harness and tests (new f4-slab GEMV twins, tensor twin coverage, serving-confirm crown logic, and checklist/doc routing).
  • Add targeted tests/fixtures to prove content-hash invalidation behavior (module-cache + tuned-sidecar cases) and document related review/build rules.

Reviewed changes

Copilot reviewed 50 out of 50 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
utils/mcp/REVIEW.md Adds a checklist rule about keeping formatter “kept comment” pinning tests strong.
tests/module_cache/test_macro_dep_invalidate.das New module-cache test to ensure macro-registered file deps invalidate by content, not mtime.
tests/module_cache/_fixtures/mc_dep_used.das Fixture module required into cache to carry the dependency probe.
tests/module_cache/_fixtures/mc_dep_mod.das Fixture function macro that registers MC_DEP_FILE via add_module_cache_dependency.
tests/module_cache/_fixtures/mc_dep_drv.das Driver program that prints probe output for the cache invalidation test.
src/builtin/REVIEW.md Clarifies checklist wording around Inline-module bind registration rules.
src/builtin/module_builtin_ast.cpp Implements and exposes add_module_cache_dependency builtin (with path absolutization).
src/ast/ast_parse.cpp Adds dependency header read/validate + writes deps into the module-cache record header; adds file stat+hash helper.
skills/review_md.md Updates REVIEW.md authoring guidance for “Planned work” pointers into larger ledgers.
skills/internal/build_and_debug.md Updates module-cache documentation to reflect new invalidation inputs (mtime+size + content-hash deps).
plans/dasllama_review_grooming.md Adds backlog plan for additional REVIEW.md grooming follow-ups found during dragon pass.
modules/dasLLVM/tests/tuned_stale_perm_client.das New test client script for stale-sidecar perm fallback behavior.
modules/dasLLVM/tests/test_tuned.das Adds tests for stale-sidecar perm fallback and for sidecar-driven module-cache invalidation.
modules/dasLLVM/tests/cant_tuned_bad_pin.das Negative test: explicit perm= outside grid must still hard-fail compilation.
modules/dasLLVM/tests/cant_tuned_bad_fallback.das Negative test: broken fallback= chain must still hard-fail compilation.
modules/dasLLVM/REVIEW.md Updates checklist wording; adds rule that compile-time file reads must pin deps with add_module_cache_dependency.
modules/dasLLVM/daslib/llvm_tune.das Pins tune sidecar as module-cache dependency before staleness checks.
modules/dasLLVM/CMakeLists.txt Registers arch-extract specs for dasLLVM daslib [arch] citations in bundles.
modules/dasLLVM/ARCHITECTURE.md Documents tune sidecar as a module-cache dependency and the validation mechanism.
modules/dasLLAMA/tests/test_metal_support_matrix.das Makes an iquant tensor-twin carrier row optional and extends crowned kernel list.
modules/dasLLAMA/tests/test_metal_gemv_kernels.das Extends GEMV kernel-unit gates for new f4-slab twin variants + adds poison controls.
modules/dasLLAMA/tests/test_metal_gemm_kernels.das Extends GEMM kernel-unit gates, adds iquant tensor twin coverage, converts certain skips to test-level skips, adds poison controls.
modules/dasLLAMA/tests/test_kernel_coverage.das Updates never-dispatched census list to include new crown-gated/tensor twin classes.
modules/dasLLAMA/tests/REVIEW.md Refactors checklist rules (contract-pinning gates, geometry vs kargs updates, control requirements, etc.).
modules/dasLLAMA/REVIEW.md Expands routing rules (GPU race code now also routed to GPU checklist) and clarifies reference-build naming rules.
modules/dasLLAMA/REVIEW_GPU.md Adds routing to new REVIEW_GPU_RACE.md and refines multiple GPU review rules.
modules/dasLLAMA/REVIEW_GPU_RACE.md New checklist governing GPU race/knockout/hand-binding arms and fidelity requirements.
modules/dasLLAMA/REVIEW_EXCHANGE.md Adds rule to keep exchange consent notice wording synced across all checked-in copies.
modules/dasLLAMA/performance/REVIEW.md Simplifies and refocuses performance checklist rules; removes consent-copy rule moved elsewhere.
modules/dasLLAMA/performance/defaults/arm-i8mm.tune-defaults.json Adds shipped class default profile for arm-i8mm.
modules/dasLLAMA/harness/tune_kernels.das Adds serving-confirm crown logic and wiring for metal tensor crown confirmation.
modules/dasLLAMA/harness/REVIEW.md New harness checklist including “benches list order” invariants.
modules/dasLLAMA/harness/backend_ops_kq_race.patch Patch file documenting/encoding a reference-tool perf-ladder extension for validation.
modules/dasLLAMA/followup_metal.md Updates metal follow-up ledger with new findings and new planned sections.
modules/dasLLAMA/dasllama/dasllama_tune.das Makes stale sidecar perms fall back to a safe fallback perm, while preserving hard failure for explicit perm=.
modules/dasLLAMA/dasllama/dasllama_metal_common.das Adds shared iquant LUT helper + strengthens race_pair_ms burn phase (bounded loop).
modules/dasLLAMA/dasllama/dasllama_env.das Adds harness env knob for IQ2_XXS confirm vehicle path.
modules/dasLLAMA/benchmarks/REVIEW.md Updates benchmark checklist wording and tightens race instrument requirements.
modules/dasLLAMA/benchmarks/matmul/bench_metal_kq_race.das New Metal KQ race rig for per-format synthetic benchmarking vs a pinned reference table.
modules/dasLLAMA/ARCHITECTURE.md Updates architecture doc index to reflect new/expanded GPU sections.
modules/dasLLAMA/ARCHITECTURE_MEASUREMENT.md Documents race fidelity requirements and the serving-confirm crown mechanism.
modules/dasLLAMA/ARCHITECTURE_GPU.md Documents iquant GEMV grid read + f4-slab twin mechanism and shared helper placement.
modules/dasLLAMA/ARCHITECTURE_GPU_PREFILL.md Documents dense-KQ tensor mul_mm scaffold design and stamp strategy.
include/daScript/ast/ast.h ABI change: adds Program::moduleCacheDependencies + declares statAndHashFileDependency.
include/daScript/ast/ast_serializer.h Bumps AST serializer version to 118 for new per-record dependency header content.
doc/source/stdlib/handmade/function-ast-add_module_cache_dependency-0x6bab73893c4ca81f.rst Adds stdlib documentation entry for add_module_cache_dependency.
daslib/REVIEW.md Refines daslib checklist wording and consolidates/clarifies lint-related pinning rules.
daslib/builtin.das Adds doc comment clarifying that get_command_line_arguments returns a locked view.
Suppressed comments (1)

modules/dasLLVM/tests/test_tuned.das:121

  • Same quoting issue for DAS_TUNE_MANIFEST here: unquoted temp paths with spaces will break the env prefix and make the module-cache invalidation test flaky (especially on Windows CI).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread modules/dasLLAMA/harness/tune_kernels.das Outdated
Comment thread modules/dasLLVM/tests/test_tuned.das
Comment thread src/ast/ast_parse.cpp
Comment thread src/builtin/module_builtin_ast.cpp
Comment thread tests/module_cache/test_macro_dep_invalidate.das
Copilot caught the confirm rail writing its manifest and capture file at fixed names into
das_root - an interrupted run strands debris in what can be an installed tree, and two
concurrent tuner runs would mint a crown against each other's manifest. Both files now come
from create_temp_file_result. The child-env spellings pick up the quoted house form
(set "VAR=..."&& / single-quoted POSIX prefix) here and at the two test spawn sites.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
Copilot AI review requested due to automatic review settings September 1, 2026 18:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 3 comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/ast/ast_parse.cpp:679

  • On record-header deserialize failure, trySerialize already sets serializer_read->failed=true, but this code overwrites it with depCount > SER_MAX_MACRO_DEPS. That can clear the failure flag and prevent cache writeback/self-healing, causing the same broken cache record to be re-read and fail every run.

Comment thread modules/dasLLAMA/harness/tune_kernels.das Outdated
Comment thread src/ast/ast_parse.cpp Outdated
Comment thread modules/dasLLAMA/harness/tune_kernels.das
…s up, the seed spells ull

A hung confirm child (driver stall, bad vehicle) no longer wedges an unattended mint -
popen_timeout at a 30-minute budget, nonzero exit announced. The fwrite-fail path removes
its temp manifest. FNV64A_SEED takes the ull suffix - the ul spelling was legal (a decimal
literal past unsigned long promotes to unsigned long long) but reads as 32-bit on LLP64.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
Copilot AI review requested due to automatic review settings September 1, 2026 18:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

modules/dasLLAMA/tests/test_metal_gemm_kernels.das:155

  • The poison-control block deep-clones the entire want buffer (:= clone-init) to perturb a single value. For the mul_mm gates want can be tens of thousands of floats, so this adds unnecessary allocation/copy overhead to the test. Prefer temporarily modifying want[0], running the mismatch, then restoring it.
        //! the control: the same compare must RED on a poisoned expectation (one element off by
        //! an env-scaled-plus-absolute delta) - proves the bar can fail for this arm
        var wantp := want
        wantp[0] += env[0] * 0.1 + 1.0
        t |> success(buf_mismatch_env(by, wantp, env, 2e-4, "{tag} CONTROL(expected-red)") != 0, "{tag}: poison control reds")
        delete wantp

modules/dasLLAMA/tests/test_metal_gemv_kernels.das:104

  • The poison-control block deep-clones the full want array (:= clone-init) just to perturb one element. This adds avoidable allocation and O(n) copy per gate (and this test iterates many gates). You can get the same control without cloning by temporarily perturbing want[0], running the mismatch, then restoring the original value.
        //! the control: the same compare must RED on a poisoned expectation (one element off by
        //! an env-scaled-plus-absolute delta) - proves the bar can fail for this arm
        var wantp := want
        wantp[0] += env[0] * 0.1 + 1.0
        t |> success(buf_mismatch_env(by, wantp, env, 2e-4, "{tag} CONTROL(expected-red)") != 0, "{tag}: poison control reds")
        delete wantp

The serving-confirm vehicle override was declared in dasllama_env.das without re-running
gen_env_doc; the env-registry lane rightly red on the stale doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw
Copilot AI review requested due to automatic review settings September 1, 2026 19:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

tests/module_cache/test_macro_dep_invalidate.das:93

  • The relative-dependency probe writes/removes depRel in the process CWD (typically the repo root). That can dirty the working tree and leaves debris if the test is interrupted. You can keep the dependency relative while keeping the scratch file under the already-created temp directory by (1) creating/removing the file at an absolute path under tmp, and (2) running the child from tmp so the relative env value resolves there. On POSIX, avoid MC_DEP_FILE=... cd ... && ... (the env only applies to cd) by wrapping the cd && ... sequence in sh -c so the env prefix applies to the whole script.

@borisbat
borisbat merged commit c0ca624 into master Sep 1, 2026
38 checks passed
@borisbat
borisbat deleted the bbatkin/metal-kq-race branch September 1, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants