Skip to content

Vulkan: the KHR coopmat kq tile rebuilt (word stage, f16 accumulators, 2x4 tiling), emission-time [unroll_full], the recurrent f16 feed decided per activation buffer - #3978

Merged
borisbat merged 12 commits into
masterfrom
bbatkin/khr-mmq-tile
Sep 9, 2026
Merged

Vulkan: the KHR coopmat kq tile rebuilt (word stage, f16 accumulators, 2x4 tiling), emission-time [unroll_full], the recurrent f16 feed decided per activation buffer#3978
borisbat merged 12 commits into
masterfrom
bbatkin/khr-mmq-tile

Conversation

@borisbat

@borisbat borisbat commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Behavior change: on a Vulkan device with KHR cooperative matrix and no NV_coopmat2 (DASLLAMA_COOPMAT=mm), K-quant prefill runs on a rebuilt tile, and a hybrid model's recurrent qkv/z GEMMs stay on it when its out plane is Q8_0.

Why. Our K-quant prefill tile on the KHR arm read 32.6 TFLOP/s on the 4B gate shape against llama.cpp's 43-46, and the board's KHR prefill rows sat at 0.59-0.85x of llama.cpp; a GPU profile put the gap in the load-store pipe and the register file, not the arithmetic.

What changes.

  • The SPIR-V emitter unrolls for [unroll_full] over literal bounds at emission and lays a fixed array of cooperative-matrix tiles out as one Function-storage array reached through constant chains.
  • Every kq format stages its 16-value run from one or two words of the quant plane (khr_stage16); the tile holds f16 accumulators, eight subgroups tiled two by four.
  • The recurrent head decides its f16 feed per activation buffer, not once per layer.
  • The tile probe's khrx sweep judges itself against the shipped class and the CPU oracle, the kernel census panics on a name nothing seeded, and the review round's document fixes ride along.

Observable behavior.

  • KHR kq tile, 4B gate / down / q shapes: 32.6 / 31.7 / 32.5 TFLOP/s -> 54.2 / 59.5 / 57.6.
  • Board KHR prefill, every K-quant row now above llama.cpp: 4B Q4_K_M 3051 -> 4764 t/s, 27B UD-IQ4_XS 395 -> 741, 9B UD-Q5_K_XL 767 -> 2531; decode unchanged.
  • A for [unroll_full] with a runtime bound, or a break, continue, return, while or second loop hint under it -> compile error.

Where to look. unroll_at_emission in modules/dasSpirv/spirv/spirv_emit.das; khr_stage16 and khr_tile in dasllama_vulkan_classes.das; pf_dnx6 / pf_dno6 in dasllama_vulkan_prefill.das.

Validation, claims, ledger

Validation

  • Local-only, on the RTX 5060 Ti (driver 616.56) against llama.cpp b10660's Vulkan build the same day.
  • Kernel cells (test_vulkan_kernels.das): 108 of 108; the thirteen per-format KHR arms each print <fmt> khr-tile batch: 0 of 89600 off the oracle, four-wide twin (k4, k5, q40, k2, iq4nl, iq4xs, k3, iq3s, iq2s, iq2xs, iq2xxs, iq3xxs, k6).
  • The SPIR-V of every other kernel is byte-identical before and after: a DASLLAMA_VK_SPV_DUMP of master and of this branch differ in the thirteen kq_batch_<fmt>_khr_spv kernels and dn_conv_cls_spv only.
  • Hybrid parity (test_gpu_resident_hybrid.das, nine cells - four on the Q8 carrier, two on the K-quant twin, two on the mixed twin, one model-free census cell; dastest counts 15): 15 of 15 under DASLLAMA_COOPMAT=mm, 15 of 15 on the cm2 default, 15 of 15 under sdot4. Every step's argmax and logits maxdiff sit inside the bar in the logs. The routing witness on the mixed twin reads 1 Q8_K requants over 18 recurrent layers, f16 feed admits kq true on both cells under mm, and 55 to 145 Q8_K requants over 18 recurrent layers, f16 feed admits kq false under sdot4.
  • The mixed twin Qwen3.5-0.8B-Q4_K_M-q8out.gguf was minted by its recipe row (llama-quantize --allow-requantize --tensor-type ssm_out=q8_0 Qwen3.5-0.8B-Q8_0.gguf Qwen3.5-0.8B-Q4_K_M-q8out.gguf Q4_K_M), 548172032 bytes; fetch_models.das -- -o q8out reports 1 ok, 0 fetched, 0 pending, 0 failed (exit 0), and the unscoped run lists the row as present (its exit 1 is other, unstocked entries).
  • The K-quant parity bar on the quant feed (sdot4) is 10%, the f16 feed's 6%: the mixed twin's two-window cell reads 0.084 of an 11.6 max logit at one decode step there, argmax unchanged.
  • Tile probe (harness/vk_gemm_probe.das -- khrx, exit 0): the ship, dec4, strip and wg128 copies read bit-exact vs khr on all three shapes; f32a reads 0.00514 / 0.00923 / 0.00525 relative, within the 2e-2 bound; the shipped class holds 0 of 768 sampled outputs off the cell bar against the k4 CPU oracle on every shape, the partial-window row included. khrprof:bogus exits 1 naming the eight arms.
  • Emitter suite (tests/spirv, 121 files): 402 of 402, the census cell with ufor, ufold and coopmat_arr in its roster; test_loops.das 15 of 15 and test_coopmat.das 6 of 6 with spirv-val from the local SDK (C:/VulkanSDK/1.4.357.0) - the suite's spirv-val not found locally; skipping line is absent from every log.
  • Board rows 14 and 22 are re-runs at this tip: lcpp_bench --for-debug-purposes -r 5 -p 512 -n 128 under DASLLAMA_COOPMAT=mm on 2026-09-08 (row 14 at 3a8465e2f before the rebase, the same sources); performance/records/*.json carries no UD-Q5_K_XL or Coder-30B row, so no record row was minted.
  • Per-PR suites at the tip 7a96ebae1, through the preflight gates with the worktree binary: --only dasllama-model-free [PASS] dasllama-model-free (1468.7s), --only dasllama-stocked [PASS] dasllama-stocked (no --exclude; the gate's duration print wrapped negative on a run past 35 minutes); the gate keeps the pass/fail verdict, not the runner's REPORT text. The full preflight ran once at 7a96ebae1: untracked, lint (64 files, both rails), hash-refs, review-md, md-ascii, ast-verify (29 files), ci-das (26 files) and compile-sweep (759 roots) PASS; format FAIL on one whitespace difference in harness/vk_gemm_probe.das, fixed by the formatter in the follow-up commit a62e1f3db and re-run green ([PASS] format). The lanes that red skipped ran one by one at a62e1f3db: docs PASS (sphinx-html 498 s), tests-cpp PASS, tests-interp 14285 of 14303 and tests-jit 14155 of 14171 with the same single failure in each - tests/watchdog/test_watchdog.das took 163 s against preflight's 60 s per-file cap under 48 workers, a file this diff does not touch (run alone it passes, 46 of 49 with 0 failures, in 165 s - the cap is what fails it, the ledgered per-file-allowance item); tests-aot 13226 of 13255 after building test_aot from a plain shell, the eleven failures all in tests/module_cache/test_descriptor_manifest.das - a file this branch does not touch, changed on master the same day by the commits this branch rebased onto; it passes 13 of 13 under the interpreter and fails only under the static test_aot.exe -use-aot runner, where the children it spawns print nothing (marker 'PROBE 42' MISSING, rc 0).
  • The .md stop's rulings batch (the last commit) and the rebase onto master 5a9c46d ran the fast tier minus the compile sweep: untracked, format, lint, hash-refs, review-md, md-ascii, ast-verify and ci-das PASS, three gates with nothing in reach; after the dragon fixes, lint / review-md / format / md-ascii / hash-refs PASS again, and the make-pr sync, stamp-reach and jit-smoke gates are green on the rebased tip. The full preflight was not re-run after the rebase; CI validates the tip.
  • CI on the tip: one red lane, build (windows, 32, Release), the interpreter sweep failing only master's tests/language/func_addr.das (the function-address test a function address answers the function in every tier; a module-qualified builtin call stops crashing JIT codegen #3974 merged the same day): the reinterpreted handle carries garbage in its high 32 bits on 32-bit, a path this diff does not touch (no change under src/, include/ or the interpreter), and a function address answers the function in every tier; a module-qualified builtin call stops crashing JIT codegen #3974's own 32-bit lane passed the same test, so the read is uninitialized memory. Filed as 32-bit Windows: reinterpret<uint64>(@@fn) reads garbage in the high 32 bits - tests/language/func_addr.das fails on the windows-32 Release lane #3979; the lane was re-run once.
  • The khrx arms' binding order (0 wq / 1 ws / 2 sched / 3 xf16 / 5 y) and the BatchArgs push-constant layout were verified by hand against KhrPxBase and kq_batch_k4_khr_cls.
  • The GPU was lost once during the board's KHR re-measurement (no TDR event, rebooted, cause unknown; another session was testing the watchdog and the server on the same box); the rows that followed were re-run after the reboot. The probe's first partial-window run lost the device once more: the bisect arms store whole tiles and read past a 300-token fixture, which is why they now sit out the partial row.

Claims - stated, not tested

  • The tile is measured on one NVIDIA card in KHR mode; a wave64 device (AMD) and a KHR-only card (Turing, Intel) run the same kernels but their rates are unmeasured - the real-hardware pass is ledgered under item 42.
  • The feed split is the rule the attention and FFN heads already apply, not a kernel race: one per-role profile shows both forms on one model under one instrument (qkv/z on the quant form at 11 TFLOP/s beside gate/up/down on the f16 form at 44).
  • Per-format word indexing in khr_stage16 is proven by the thirteen cells; the edge-tile store by the cells' edge tiles and the probe's partial row; f16 accumulation over the full k by the cell bars, the model bars and the cool-scales fixture (llama.cpp's own accumulator choice). The x6=false / o6=true combination (a K-quant out plane beside Q8_0 qkv/z) has no fixture; the two decisions are independent ifs and each side is exercised.
  • The emitter's fold guards (division by zero, a shift of 32 or more, INT_MIN / -1) are stated, not fixtured: the fixtures exercise the fold arms, not the refusals.
  • The routing witness's threshold assumes the attention and FFN heads contribute no Q8_K requants on the fixture (they ride the f16 feed there).

Not done

  • The external codex review (the woodpecker round) did not run: codex 0.153.3's execution runner fails to start a process on this box (timed out after 15000ms connecting runner pipe-in) in both the --base origin/master and the --commit form, on a quiet machine as under agent load, and its fallback review tool is blocked by the never-approve policy. No external round backs this PR.
  • Ledger item 42 is corrected in this change (on a KHR device llama.cpp runs its f16 coopmat tile, not the integer MMQ tile); the no-coopmat arm (DASLLAMA_COOPMAT=sdot4) against llama.cpp's coopmat-less arm, the wave64 twin of the KHR tile, and the real-hardware pass stay open under it.
  • The decode levers on the ledger (the attention floor, the fused residual add, the fence poll) and the qwen2moe items are not this arc's.
  • The tests/spirv census roster misses about 25 *_words fixtures, and the suite's skip line claims a CI check no lane makes in about a hundred files; the two files this change touches carry the corrected line, the rest and the roster go to the follow-up PR (the shader-emitter follow-up ledger, item 3). The roster rule itself lands here.
  • The MoE architectures that fit a 16 GB card and need a new arch are ledgered as followup 122, ruled for after the 0.6.4 release and after the Vulkan tier covers every carrier the module already serves (followup_vulkan 43).

borisbat and others added 11 commits September 8, 2026 20:47
… literal bounds emits its body once per copy with the induction variable an OpConstant, integer arithmetic on that constant folds so acc[t * 16 + c] chains a literal index (the register-block shape every driver's scalar-replacement pass promotes), break, continue and a runtime bound are refused by name, and [unroll] stays the driver's Unroll hint - the spelling the JIT (llvm.loop.unroll.full) and dasMetal (#pragma clang loop unroll(full)) already lower, so a kernel reads the same on every tier and no shipped SPIR-V kernel changes (the golden set is byte-identical); the visitor walks the body per copy through its own adapter and cuts the function section back over the walk it makes on its own; fixture ufor (zero OpLoopMerge, 23 constant-index chains into the block, the fold assertions), two fail-closed fixtures, dasSpirv ARCHITECTURE section 3.6, emitter ledger item 2

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ative matrix llama.cpp creates no integer MMQ pipeline - CREATE_MMQ sits only in the shader loader's two scalar arms, the q8_1 pipeline set is empty and every mat-mat falls to the f16 KHR-coopmat mul_mm.comp tile with the format decoded into shared memory - so the 4221 / 675 t/s KHR bars and the 43-46 TFLOP/s-eq per-shape rates are that tile's (128 threads, 128 x 128 x 32, four subgroups as 2 x 2 of 64 x 64 over one shared B stage, f16 accumulators, a 20-word stride and 22 KB of shared like ours, a direct store), and the road to parity is our KHR tile's subgroup tiling and accumulator width, not an integer tile; the MMQ tile is the coopmat-less arm's path and the sdot4 mode's lever, its rates on this card unmeasured

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… as a Function-storage OpTypeArray over the tile type and chains an element through the tile type, so an accumulator block written as `var acc : coopmatAcc_f16_16x16[16]` under for [unroll_full] reaches every element through a constant OpAccessChain (the register-block form a 2-D subgroup tiling holds, llama.cpp's sums[]); fixture coopmat_arr (one OpTypeArray, four MulAdds, twelve constant element chains, spirv-val clean at 1.6) and the constant-chain helper shared with the unroll fixture

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…es its 16-value run from one or two words of the quant plane (`khr_stage16`, the plane aliased as uint4 or uint on its binding, the scale words read once) in place of the four-wide decode callback's eight 16-bit lane loads, the eight subgroups tile the 128 x 128 step two by four (64 weights by 32 tokens each, four weight and two token fragments per 16-deep round, 48 fragment loads per workgroup per k step against 144) into f16 accumulators held as a `coopmatAcc_f16_16x16[8]` under `for [unroll_full]` and widened before the store - the shipped class reads 53.8 / 59.8 / 57.9 TFLOP/s on the 4B gate / down / q shapes against 32.6 / 31.7 / 32.5, the reference exe's KHR tile 43-46; Nsight GPU Trace on the old tile had the load-store pipe at 81% of peak from the decode's small loads and the register file at 99% (two workgroups per SM), and neither lever alone moved it (f16 accumulators 32.7, the tiling 35.5) until the stage went to words (37.6, then 54.0 with f16, 54.9 with the tiling); the thirteen kernel cells hold 0 of 89600 off, the hybrid parity file 10 of 10 on the KHR arm; the khrx probe resyncs to the shipped body with one lever moved back per arm (dec4, f32a, strip, wg128, nil) and its `khrprof:<arm>` mode submits one arm for a GPU profiler; the emitter's [unroll_full] bound accepts a folded module constant

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fill on the word-stage kq tile (3051 before, 1.13x of llama.cpp's KHR 4221), the 27B UD-IQ4_XS 741 under a 14000 MB pin (395 before, 1.10x of 675), decode unchanged; ledger item 42 records the Nsight reading behind the tile (the load-store pipe at 81% of peak from the decode's small loads, the register file at 99%), the order the three levers had to land in, and what stays open (the no-coopmat arm against the integer MMQ tile, the wave64 twin, the real-hardware pass); the format HOW_TO's KHR step and the Vulkan review rule name the per-format khr_stage16 a kq format ships

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…el at a time: every K-quant row now reads above llama.cpp's KHR arm on prefill - the 0.8B 16052 (1.09x), the 1.5B IQ3_XS 12058 (1.12x), the 4B Q5_K_M 4422 and Q6_K 4284 (1.10x), the 9B IQ4_XS 2560 (1.16x) and Q5_K_M 2525 (1.20x), the 27B i1-IQ3_S 737 and UD-Q3_K_XL 729 under the pin (1.13x, 1.08x), the MoE rows on the per-op tier 712 / 764 / 665 / 320 against the same-split offload (1.21x to 2.75x) - except the 9B UD-Q5_K_XL at 1788 against 2098 (0.85x), the one row under parity, its per-role profile owed; the Coder-30B run's decode read half its earlier figure and its re-run is owed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er layer: the deltanet qkv and z GEMMs read the block input and the out GEMM reads the scan's o rows, and one flag over all three planes sent a K-quant qkv/z pair to the sdot4 tile whenever the out plane was Q8_0 - the unsloth UD mixture's shape, a format the KHR arm's f16 feed does not admit - so the 9B UD-Q5_K_XL prefilled at 1788 t/s against llama.cpp's 2098 with its qkv at 72.9 ms and z at 35.1 ms per window (11 TFLOP/s, against 19.7 / 10.4 on the Q5_K_M file whose out plane is Q5_K); `pf_dnx6` (qkv, z) and `pf_dno6` (out) now decide apart, as the attention and FFN heads already did: qkv 19.7 ms, z 10.5, the board row 2531 / 56.66 (1.21x / 0.98x); the hybrid parity file gains the mixed twin (`Qwen3.5-0.8B-Q4_K_M-q8out.gguf`, the Q4_K_M recipe with `--tensor-type ssm_out=q8_0`, its row in the model-set table) and a routing witness through the coverage census - on a coopmat feed the prefill's Q8_K requants stay under one per recurrent layer (the KHR arm reads one, the classifier's final row), off it they reach one per layer (sdot4 reads 55 to 145, every count the route's) - with the old gate failing exactly the two mixed-twin cells; the census gains `vk_kernel_dispatches(name)`, which panics on a name no kernel seeded, because a class kernel counts under its SPIR-V name (`<name>_spv`) and a misspelt key read as a silent zero; the K-quant bar on the quant feed is 10% (the mixed twin's two-window cell reads 0.084 of an 11.6 max logit at one decode step under sdot4, argmax unchanged); the Coder-30B KHR row re-run after the reboot reads 721.8 / 64.23, so its halved decode was the box; the cm2 k4 tile rows beside the KHR tile's on the same shapes and submit (59.8 / 66.0 / 62.0 TFLOP/s, the KHR tile at 90%)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…itter's unroll bookkeeping is an `UnrollRewind` (the state the visitor's own walk of an unrolled body rewinds to) and the sentinel loop targets a named helper pair (`unrolled_loop_targets`, `is_unrolled_body`); the test helper that counts constant chains says which chains (`function_storage_chain_indices`, `dynamic_indices`); the hybrid parity test's feed predicate takes the documents' word (`f16_feed_admits_kq`) and the prefill's Q8_K x route its twin's vocabulary (`xq8k` beside `x6`); dasSpirv's section 3.6 carries the facts the comments stated - a compiler-folded bound counts as a literal, an error in an unrolled body is reported once, a fixed array of tiles is one Function-storage OpTypeArray reached through constant chains - cited from `literal_int_of`, `literal_range_of`, `collect_locals`, `visitExprFor` and `declare_local_var`; the Vulkan architecture doc gains section 2.2ac for the probe arms' shared set layout (the `nil` arm's unread plane, the case the dummy-bind rule sends there), cited from the arm's kernel, and the probe base body cites the GEMM doc's tile section; the measurement doc's khrx paragraph tightens back to its four lines; forty-one narrating comments go, the per-format plane-word maps and the sanctioned suppressions stay

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…- a loop hint beside [unroll_full] is refused by name, a `return` inside an unrolled body is refused like `break`, the errors a later copy raises survive the per-copy trim (dedupe by text, not by count), the discarded pre-walk's decorations are rewound with its instructions, and the induction-variable folds fire only inside an unrolled body - with the `ufold` fixture (a module-constant bound; unary -, ~ and every binary integer op over the induction constant fold to constants, no integer op survives) and four fail-closed fixtures (continue, return, while, a hint pair); the tile probe's khrx sweep judges itself - every compared arm within 2e-2 of the shipped class (the copies read bit-exact), the shipped class against the k4 CPU oracle on six sampled corners at the kernel cell's bar, an unknown `khrprof:<arm>` refused by name, the process exit non-zero on any miss or on a run with no result row - and gains a partial-window row (the gate shape at 300 tokens: 45.0 TFLOP/s against 54.2 at 512, 57.6 per computed row, so the edge store costs nothing beyond the padded rows; the whole-tile bisect arms sit that row out, since they stage and store 128-row tiles and a 300-row fixture lost the device once); the kernel coverage census is read by kernel name through `vk_kernel_coverage_of`, which panics on a name nothing seeded (the hybrid file's routing witness had read a silent zero off a misspelt key), and the hybrid parity file names its planes (`HybridPlanes`), its two K-quant bars (6% on the f16 feed, 10% on the quant feed) and its census cell; the review documents' own defects fixed in the same batch (the recipe-row trigger, the result-row and gate definitions, the width rule split into fixed widths and one-whole-one-partial windows, the KHR per-arm evidence rule and the khr_stage16 residue rule, the slot-owning @ROLE scope, the reference-build reading/proposal split, the census and version rules of the emitter suite, the records-write rule moved to performance/); the architecture docs groomed - the KHR passage of the GEMM doc in four heads with the per-lever figures moved to ledger item 42, the recurrent block of the Vulkan prefill in its own section 2.2ad with its kernels' citations repointed, the runtime doc's activation-group bullet and its pre-existing imperatives in plain present tense, the measurement doc's kernel-race sections in a companion, the dasSpirv doc's cooperative-matrix sections in ARCHITECTURE_COOPMAT.md - and the tests folder's guide rewritten at fixture boundaries; verified on the RTX 5060 Ti: the kernel file 108 of 108 with the thirteen KHR cells at 0 of 89600 off, the hybrid file 15 of 15 under mm, cm2 and sdot4 (the mixed twin's witness 1 requant over 18 recurrent layers on the f16 feed, 55 to 145 off it), the emitter suite 402 of 402, the khrx probe exit 0 and the recipe row present to fetch_models

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng, the formatter's own output - no code change

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… route fires on a file added under dasllama/ or a def, require or module global added or moved there, and the charter duty on a file added, a def moved between files, or a change to what a file owns - a diff that edits one def's body has no charter edit to land, so "edits" left both triggers; the measurement checklist's figure rules bind the text a change adds in the repo or its PR body, not its commit messages, which no review step reads; the duty to refresh last_known_good_sidecar.json on every DASLLAMA_RELEASE bump is gone from performance/REVIEW.md (a bump made on the PC box could never satisfy a duty that needs the M5 mint, and the architecture doc already states that a stale copy serves fallbacks); the dasSpirv verification procedure the archivist took out of its architecture doc lives in skills/internal/tests_in_repo.md as the emitter-suite section - the dastest command, what green means, the disassembly diff and the real-driver gate - with the dasSpirv checklist's local spirv-val rule and the root CLAUDE.md skill row pointing there; skills/review_md.md gains the case of a checklist whose folder has no rationale document (a folder of transient documents, each deleted when its work lands, so no rule there can cite a reason - it writes no Architecture-doc slot) and marks its repo-internal examples repo-only, since the skill ships and the leak gate keeps every REVIEW*.md out of the bundle; the tests checklist's two half-automated rules keep only the half test_run_suites.das cannot decide, with "weakening that gate is a defect" as the residue; the shader-emitter checklist's planned-work pointer into plans/ and the residency doc's plans/ reference are gone (plans/ is cross-box development scratch, retired when its work lands, and nothing refers to it); the hot-path rule's four-way split loses its two ambiguities - the region entry is the outermost function the runtime re-enters per token, per frame or per prefill quantum, and a load, stage, bake or convert loop carries no [hot_path] and none of the [no_alloc] / [no_env] / [no_io] contracts while a [cold_path] may sit on it; the measurement checklist's route-without-a-record-leg duty, stated once in the board-cell rule and again in the re-mint rule, is one rule; the ledgers: item 121 of followup_general.md carries its ruling date, item 122 records the MoE architectures whose files fully fit a 16 GB card (deepseek2 first, cohere2moe second, about sixteen in all) as work for after the 0.6.4 release and after the Vulkan tier covers every carrier the module already serves, which is item 43 of followup_vulkan.md (the existing families and the vision, audio and TTS towers, the known gaps by item number, the fully-resident MoE chain as row 20's shape), and the shader-emitter ledger's item 3 holds the census roster's twenty-five unrostered fixtures and the suite's skip line that claims a CI check no lane makes, both for the follow-up PR; the shared-expert rail's comment said its knob was opt-in where the code and ENVIRONMENT.md say it follows the tier. Verified: the preflight fast tier minus the compile sweep at this tree - untracked, format, lint, hash-refs, review-md, md-ascii, ast-verify and ci-das PASS, review-md-tests, cpp-syntax and dasgen with nothing in reach - and one fresh dragon re-read per changed rule document (the dasLLAMA, measurement, tests and dasSpirv checklists and the review_md skill), every blocking finding applied, the wording notes left as they are

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 9, 2026 03:49

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.

🔵 Needs a closer look

It combines performance-sensitive Vulkan behavior changes with broad test and architecture/review-document refactoring, and the reviewable surface area is too large for a safe automated sign-off.

Pull request overview

This PR improves the Vulkan path for cooperative-matrix (KHR) K-quant prefill by introducing emission-time full unrolling for constant-bounded loops and by refining Vulkan hybrid recurrent feed decisions, with expanded SPIR-V emitter fixtures and updated architecture/review documentation to lock the new invariants in.

Changes:

  • Add [unroll_full] emission-time unrolling coverage (including fail-closed fixtures) and new golden/census entries for the SPIR-V test suite.
  • Update dasLLAMA Vulkan hybrid execution to decide recurrent f16 feed per activation buffer and expose kernel-coverage-by-name for routing witnesses/tests.
  • Split/extend architecture + review docs (dasSpirv coopmat/unroll companion; dasLLAMA measurement kernel-race companion) and update cross-references.
File summaries
File Description
tests/spirv/test_loops.das Adds [unroll_full] SPIR-V shape tests
tests/spirv/test_golden.das Adds golden disassembly check for ufor
tests/spirv/test_fail_closed.das Adds [unroll_full] rejection fixtures + “once” check
tests/spirv/test_coopmat.das Adds coopmat tile-array unroll fixture
tests/spirv/test_census.das Rosters new fixtures in opcode census
tests/spirv/REVIEW.md Tightens fixture-roster + validation rules
tests/spirv/_spirv_common.das Adds ufor/ufold/coopmat_arr fixtures + helpers
tests/spirv/_golden/ufor.txt New golden disassembly for ufor
tests/spirv/_gen_golden.das Emits ufor golden output
tests/spirv/_fail_closed/_fc_unroll_while.das New fail-closed: unroll on while
tests/spirv/_fail_closed/_fc_unroll_runtime.das New fail-closed: runtime bounds
tests/spirv/_fail_closed/_fc_unroll_return.das New fail-closed: return in unrolled body
tests/spirv/_fail_closed/_fc_unroll_hint_pair.das New fail-closed: hint pair beside [unroll_full]
tests/spirv/_fail_closed/_fc_unroll_continue.das New fail-closed: continue in unrolled body
tests/spirv/_fail_closed/_fc_unroll_break.das New fail-closed: break in unrolled body
skills/review_md.md Clarifies review-checklist opening patterns
skills/internal/tests_in_repo.md Documents tests/spirv suite expectations
plans/vulkan_hybrid_ladder.md Updates plan reference pointer
plans/shader_emitter_followups.md Updates follow-up plan with [unroll_full] details
plans/REVIEW.md Refines plans/ routing to measurement/upstream checklists
modules/REVIEW_SHADER_EMITTERS.md Updates shader-emitter review rules + doc list
modules/dasSpirv/spirv/spirv_dis.das Moves [arch] citation to new coopmat doc
modules/dasSpirv/spirv/spirv_builtins.das Moves [arch] citation to new coopmat doc
modules/dasSpirv/spirv/spirv_builder.das Moves [arch] citation to new coopmat doc
modules/dasSpirv/REVIEW.md Updates dasSpirv checklist (fixtures, device cells, spirv-val)
modules/dasSpirv/ARCHITECTURE_COOPMAT.md New: coopmat + emission-time unrolling architecture companion
modules/dasMetal/ARCHITECTURE.md Updates reference to dasSpirv coopmat doc
modules/dasLLAMA/tests/test_gpu_resident_hybrid.das Adds mixed twin, feed witness, and kernel-census-by-name test
modules/dasLLAMA/tests/REVIEW.md Refines dasLLAMA tests checklist wording
modules/dasLLAMA/tests/REVIEW_KERNEL_CELLS.md Tightens kernel-cell expectations + precision rule
modules/dasLLAMA/REVIEW.md Clarifies routing rules and doc-upkeep expectations
modules/dasLLAMA/REVIEW_UPSTREAM.md Refines upstream naming/provenance guidance
modules/dasLLAMA/REVIEW_MEASUREMENT.md Refines measurement provenance + board/record rules
modules/dasLLAMA/REVIEW_GPU.md Refines GPU checklist wording + constraints
modules/dasLLAMA/REVIEW_GPU_VULKAN.md Adds KHR-tile specific checklist rules
modules/dasLLAMA/REVIEW_GPU_RACE.md Refines race checklist wording + conditions
modules/dasLLAMA/README.md Notes KHR kq tile coverage in Vulkan GEMM doc
modules/dasLLAMA/performance/REVIEW.md Refines performance-artifact review rules
modules/dasLLAMA/performance/model_specs.das Adds mixed twin model recipe row
modules/dasLLAMA/HOW_TO_ADD_A_FORMAT.md Updates KHR instantiation procedure (khr_stage16)
modules/dasLLAMA/harness/tune_kernels.das Updates [arch] citation for race fidelity
modules/dasLLAMA/harness/REVIEW.md Refines harness checklist + probe-axis rule
modules/dasLLAMA/harness/gen_tune_probe.das Updates [arch] citation for gemv seat
modules/dasLLAMA/benchmarks/REVIEW.md Updates benchmark checklist + measurement docs
modules/dasLLAMA/benchmarks/matmul/kq_kernel_bench.das Updates [arch] citations for fixture rules
modules/dasLLAMA/benchmarks/matmul/bench_metal_kq_race.das Updates [arch] citation for race fidelity
modules/dasLLAMA/ARCHITECTURE.md Updates companion-doc index entries
modules/dasLLAMA/ARCHITECTURE_MEASUREMENT_KERNEL_RACE.md New: measurement companion for kernel races/benches
modules/dasLLAMA/ARCHITECTURE_GPU.md Updates kernel-race doc reference
modules/dasLLAMA/ARCHITECTURE_GPU_VULKAN.md Adds recurrent-block + probe-layout sections
modules/dasLLAMA/ARCHITECTURE_GPU_VULKAN_RESIDENCY.md Updates referenced section list + measurement command
modules/dasLLAMA/ARCHITECTURE_GPU_VULKAN_DECODE.md Updates referenced section list
modules/dasLLAMA/ARCHITECTURE_GPU_QUANT_PLANES.md Updates kernel-race doc reference
modules/dasLLAMA/ARCHITECTURE_CPU_KERNELS.md Updates gemv seat doc reference
modules/dasLLAMA/followup_general.md Adds follow-up item about splitting tests CLAUDE/arch
modules/dasLLAMA/dasllama/dasllama_vulkan_prefill.das Splits recurrent feed decisions; exports pf_f16_feed
modules/dasLLAMA/dasllama/dasllama_vulkan_common.das Adds vk_kernel_coverage_of
modules/dasLLAMA/dasllama/dasllama_metal_kernels.das Updates [arch] citation for race fidelity
modules/dasLLAMA/dasllama/dasllama_gpu_resident.das Clarifies shared-expert comment text
CLAUDE.md Expands tests skill row to mention tests/spirv spirv-val run
Review details
  • Files reviewed: 70/70 changed files
  • Comments generated: 2
  • Review effort level: Lite

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

Comment thread skills/internal/tests_in_repo.md Outdated
Comment thread tests/spirv/test_coopmat.das Outdated
…change touches - tests/spirv/test_coopmat.das and test_loops.das - says where the run belongs (a box with the Vulkan SDK on PATH) instead of claiming a CI check no lane makes, and the emitter-suite section of the tests skill states the requirement alone, without restating the dasSpirv checklist's PR duty. Verified: both files green with spirv-val from the local SDK (6 of 6, 15 of 15); the lint, review-md and format gates PASS

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 9, 2026 04:03

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.

🔵 Needs a closer look

It changes performance-critical GPU/emitter behavior and adds broad checklist/doc/test enforcement, which warrants final human review despite strong fixture coverage.

Review details
  • Files reviewed: 70/71 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@borisbat
borisbat merged commit 536d9b1 into master Sep 9, 2026
46 of 48 checks passed
@borisbat
borisbat deleted the bbatkin/khr-mmq-tile branch September 9, 2026 05:19
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