the iquant Metal kq catch-up: kernels at llama.cpp speed, sidecars cache-pinned, tune boot hardened - #3919
Conversation
…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
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
There was a problem hiding this comment.
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.
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
There was a problem hiding this comment.
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,
trySerializealready setsserializer_read->failed=true, but this code overwrites it withdepCount > 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.
…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
There was a problem hiding this comment.
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
wantbuffer (:=clone-init) to perturb a single value. For the mul_mm gateswantcan be tens of thousands of floats, so this adds unnecessary allocation/copy overhead to the test. Prefer temporarily modifyingwant[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
wantarray (:=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 perturbingwant[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
There was a problem hiding this comment.
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
depRelin 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 undertmp, and (2) running the child fromtmpso the relative env value resolves there. On POSIX, avoidMC_DEP_FILE=... cd ... && ...(the env only applies tocd) by wrapping thecd && ...sequence insh -cso the env prefix applies to the whole script.
ABI break:
Programgains amoduleCacheDependenciesfield 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_dependencybuiltin, 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_gemvindasllama_metal_kernels.das;race_pair_msindasllama_metal_common.das;confirm_serving_crowninharness/tune_kernels.das;addModuleCacheDependencyinmodule_builtin_ast.cppand the record-header validation inast_parse.cpp.Validation, claims, ledger
Validation
Claims - stated, not tested
race_gemv_f4_twin,race_kq_mulmm, and the race lab'stime_arm_uswere hand-verified against the class declarations, and independently re-verified in review. A break would race the wrong buffer and crown the wrong kernel.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 seeingDAS_TUNE_MANIFESTon Windows CI.GetFullPathNamepath through every relative-path dep. A break would serve stale[tuned]stamps after a re-mint under a drive-relative manifest path.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._hxlane; measured knockout is +0.65% pp512) and sec. 5 (tall/db tensor stamps for the deep-K w2 column).🤖 Generated with Claude Code
https://claude.ai/code/session_014mq9AKyoAwvD5T1cigTnRw