The Qwen family on Vulkan: the KHR kq tile, the OS-room auto arm, and the three fixes the walk found - #3972
Conversation
… on the box (27 GGUF files, 0.5B to 48 GB) measured against llama.cpp on the same card and the three silent declines fixed - the qwen2 q/k/v projection bias folds into the rope stage on the resident driver and rides the per-op attention chain's hooks, a hybrid's KV mirror binding cap sums per-layer rows instead of reading one byte past 4 GiB, and a BPE vocab without add_bos_token takes upstream's per-pre default; the resident plan's auto arm sizes against the room the OS reports on the adapter (a hand-bound vk_os_video_memory query, no VRAM pin needed) and device allocations carry no memory-priority tag (the 27B UD-IQ4_XS with 1.7 GB demoted decodes 4.2 -> 23.5 t/s); the grid GEMVs stage their codebooks from a device buffer (iq2s 84 -> 388 GB/s) and the codebook scale row is two words; the SPIR-V emitter lets a kernel call a [spirv_decode] method directly - a struct parameter spills to a Function local, a plane-element argument passes its index and the callee chains through the plane - with four fail-closed fixtures and a golden; the kq prefill tile gains a KHR cooperative-matrix arm for all thirteen formats (a device with KHR coopmat and no NV_coopmat2 prefilled K-quants at a third of llama.cpp's rate: 4B Q4_K_M 1564 -> 2979 t/s, the tile's two uint staging arrays its whole workgroup footprint after the 8 KB edge-store slab cost it one workgroup per SM); the twin kernels collapse onto templates and shared bases with a census of what stays apart; the serving census sweeps the format vehicles under every coopmat mode, so every format's sdot4, cm2 and KHR tile families dispatch under a census model; the deltanet conv kernel stages only the q/k region it norms (the whole-row slab overflowed at the 27B's 10240 conv width) and the add-rms row stash stays within a 32 KiB workgroup budget at 6144 floats; the ast-fuzz suite spawns the binary running it and the module-cache file's child-spawning cells skip on an AOT-consuming host; the harvest round lands every fact as an architecture section its code cites and every rule in the checklist it binds, the twenty-one audits' violations fixed in code or in the checklists themselves; the board (plans/qwen_arc_board.md) and ledger item 42 carry the numbers and where the KHR gap still sits Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The change set is very large and affects core GPU/Vulkan serving behavior, shader emission semantics, and many tests/docs, so it needs careful human validation (and the PR body should remove the prohibited Claude Code footer/link).
Pull request overview
This PR extends dasLLAMA’s Vulkan resident path and shader emission/test infrastructure to better serve the Qwen family on Vulkan (including KHR cooperative-matrix paths), while tightening or expanding test coverage and updating the related architecture/review documentation.
Changes:
- Add/extend SPIR-V emitter support for direct
[spirv_decode]method calls (including struct-parameter spill behavior) and cover it with new fixtures + golden/census/fail-closed tests. - Add Vulkan OS video-memory “room” query plumbing (
os_video_memory) plus an integration test, and route the value into dasLLAMA’s residency planning behavior. - Expand dasLLAMA model/kernel tests and supporting utilities for Qwen2/Qwen3.* + K-quant/iQ formats (including scale-row packing changes) and update associated checklists/docs.
Notable process issue: the PR description currently includes a claude.com/claude-code “Generated with” footer/link, which is explicitly disallowed by the repo’s PR hygiene guidance (skills/internal/make_pr.md).
File summaries
| File | Description |
|---|---|
| utils/internal/ast-fuzz/test_ast_fuzz.das | Use running binary path for fuzz |
| utils/dasllama-server/REVIEW.md | Checklist routing note for tests |
| tests/spirv/test_struct_param.das | New SPIR-V direct-call tests |
| tests/spirv/test_golden.das | Add new golden fixtures |
| tests/spirv/test_fail_closed.das | Add new fail-closed checks |
| tests/spirv/test_census.das | Add new opcode census inputs |
| tests/spirv/REVIEW.md | Strengthen SPIR-V test checklist |
| tests/spirv/_spirv_common.das | Add spdirect/spelem fixtures |
| tests/spirv/_gen_golden.das | Emit new golden outputs |
| tests/spirv/_fail_closed/_fc_decode_twoplanes.das | New fail-closed fixture |
| tests/spirv/_fail_closed/_fc_decode_mixed.das | New fail-closed fixture |
| tests/spirv/_fail_closed/_fc_decode_index64.das | New fail-closed fixture |
| tests/spirv/_fail_closed/_fc_decode_helper_copy.das | New fail-closed fixture |
| tests/msl/REVIEW.md | Reference shared emitter checklist |
| tests/module_cache/test_default_cache_path.das | Skip module-cache tests under AOT |
| skills/internal/make_pr.md | Clarify targeted gate reruns |
| plans/shader_emitter_followups.md | Document unroll-at-emission followup |
| plans/REVIEW.md | New plans folder checklist |
| modules/REVIEW_SHADER_EMITTERS.md | Rewrite shared emitter checklist text |
| modules/dasVulkan/utils/vulkan2rst.das | Group os_video_memory in docs |
| modules/dasVulkan/tests/REVIEW.md | Add integration-test numeric comparison rule |
| modules/dasVulkan/tests/integration/test_os_video_memory.das | New OS video-memory integration test |
| modules/dasVulkan/REVIEW.md | Route kernel bodies to shared checklist |
| modules/dasVulkan/daslib/vulkan_boost.das | Add public os_video_memory helper |
| modules/dasVulkan/ARCHITECTURE.md | Document OS video-memory mechanism |
| modules/dasUnitTest/unit_test.das.inc | Reformat embedded unit_test blob |
| modules/dasSpirv/REVIEW.md | Add OpVariable ordering rule |
| modules/dasSpirv/ARCHITECTURE.md | Document direct decode call lowering |
| modules/dasMetal/ARCHITECTURE.md | Note Vulkan-only direct decode calls |
| modules/dasLLVM/REVIEW.md | Checklist routing note for tests |
| modules/dasLLAMA/tests/test_vulkan_tier.das | Extend attention tests for bias path |
| modules/dasLLAMA/tests/test_tokenizer.das | Add BOS-default behavior test |
| modules/dasLLAMA/tests/test_kquant.das | Add iQ two-word scale-row gather test |
| modules/dasLLAMA/tests/test_kernel_coverage.das | Expand Vulkan serving census vehicles/modes |
| modules/dasLLAMA/tests/test_gpu_tier.das | Align gpu_want_auto with env export |
| modules/dasLLAMA/tests/test_gpu_serving_declines.das | Add decline/servability gates (bias, out plane, plan room) |
| modules/dasLLAMA/tests/test_gpu_resident_qwen2.das | New resident Qwen2 bias parity test |
| modules/dasLLAMA/tests/test_gpu_resident_hybrid.das | Adjust hybrid parity to mirror codec + tags |
| modules/dasLLAMA/tests/test_gguf_quant.das | Add ggml type sizing/name tests |
| modules/dasLLAMA/tests/run.das | Register new resident qwen2 test |
| modules/dasLLAMA/tests/REVIEW.md | Refine checklist update rules |
| modules/dasLLAMA/tests/REVIEW_KERNEL_CELLS.md | Clarify geometry/kargs update obligations |
| modules/dasLLAMA/tests/CLAUDE.md | Update suite census documentation |
| modules/dasLLAMA/tests/_vkd_toy.das | Add direct-decode dispatch fixture |
| modules/dasLLAMA/tests/_model_tier.das | Add mirror_kv_dtype helper |
| modules/dasLLAMA/REVIEW.md | Clarify measurement/logging rules |
| modules/dasLLAMA/REVIEW.das | Bump image layout stamp |
| modules/dasLLAMA/REVIEW_UPSTREAM.md | Retitle/clarify upstream naming policy |
| modules/dasLLAMA/REVIEW_PLACEMENT.md | Tighten placement rules for transforms/kernels |
| modules/dasLLAMA/REVIEW_MEASUREMENT.md | Clarify provenance and decision rules |
| modules/dasLLAMA/REVIEW_IMAGE.md | Clarify mint invariants (no double model) |
| modules/dasLLAMA/REVIEW_GPU.md | Tighten kernel/template and role rules |
| modules/dasLLAMA/REVIEW_GPU_VULKAN.md | Add Vulkan-specific rules (KHR, decode call form, grid staging) |
| modules/dasLLAMA/REVIEW_GPU_RACE.md | Clarify binding/order and timing rules |
| modules/dasLLAMA/README.md | Split Vulkan docs into GEMM companion |
| modules/dasLLAMA/performance/REVIEW.md | Clarify record-hygiene + recipe change rule |
| modules/dasLLAMA/performance/model_specs.das | Update Qwen3.5 Q4_K_M recipe |
| modules/dasLLAMA/HOW_TO_ADD_A_FORMAT.md | Update Vulkan iQ device-row + KHR arm guidance |
| modules/dasLLAMA/harness/REVIEW.md | Add vk_dispatch role silencing rule |
| modules/dasLLAMA/harness/_vk_probe_fixture.das | New shared Vulkan probe fixture |
| modules/dasLLAMA/followup_metal.md | Add Metal twins census plan section |
| modules/dasLLAMA/followup_general.md | Add image mint double-hold finding |
| modules/dasLLAMA/ENVIRONMENT.md | Flip vk_memprio default; clarify cm2 knobs |
| modules/dasLLAMA/dasllama/dasllama_vulkan_seams.das | Add bias placeholder binding in seam |
| modules/dasLLAMA/dasllama/dasllama_math_vulkan.das | Install new Vulkan resident seats |
| modules/dasLLAMA/dasllama/dasllama_layout.das | Pack iQ device scale rows (two-word) |
| modules/dasLLAMA/dasllama/dasllama_image.das | Serve streamed build if image save declines |
| modules/dasLLAMA/dasllama/dasllama_gguf.das | Add ggml sizing/type name helpers |
| modules/dasLLAMA/dasllama/dasllama_gemm_schema.das | Add kq_dev_ssb + two-word-row policy |
| modules/dasLLAMA/dasllama/dasllama_env.das | Change defaults/docs for Vulkan env knobs |
| modules/dasLLAMA/dasllama/dasllama_common.das | Add mb_delta formatting helper |
| modules/dasLLAMA/dasllama/dasllama_bpe.das | Add per-pre BOS default table |
| modules/dasLLAMA/benchmarks/REVIEW.md | Clarify race vs attribution rules |
| modules/dasLLAMA/ARCHITECTURE.md | Re-route Vulkan sections to new companion doc |
| modules/dasLLAMA/ARCHITECTURE_MEASUREMENT.md | Extend measurement axes descriptions |
| modules/dasLLAMA/ARCHITECTURE_GPU.md | Update Vulkan role table + seats list |
| modules/dasLLAMA/ARCHITECTURE_GPU_VULKAN_RESIDENCY.md | Update residency plan with OS-room sizing |
| modules/dasLLAMA/ARCHITECTURE_GPU_VULKAN_DECODE.md | Document bias + out-plane KQ behavior |
| modules/dasLLAMA/ARCHITECTURE_GPU_QUANT_PLANES.md | Note two-word device scale rows |
| modules/dasLLAMA/ARCHITECTURE_ENGINE.md | Document BOS default + conversion accounting |
Review details
- Files reviewed: 100/103 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
4c11103 to
50b489b
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It changes multiple high-impact GPU/runtime subsystems (Vulkan residency sizing, shader emission/test infrastructure, tokenizer defaults) and also includes a PR-body policy violation (Claude Code footer link) that should be removed before merge.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
tests/spirv/REVIEW.md:5
- Line 5 starts a new Markdown bullet ("- apply …") even though the preceding line doesn’t introduce a list item, which makes the checklist render oddly. This reads like it was meant to be a continuation of the previous sentence, similar to tests/msl/REVIEW.md.
- Files reviewed: 100/103 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
It touches core GPU residency/serving behavior and shader emission semantics across many files, which warrants careful human validation beyond an automated review.
Review details
- Files reviewed: 100/103 changed files
- Comments generated: 1
- Review effort level: Lite
Behavior change: the Vulkan resident plan's auto arm now sizes against the room the OS reports on the adapter, and device allocations carry no memory-priority tag - a model that needed a pin to arm may arm on its own, and one that used to spill to demoted memory now decodes at full rate.
Why. Every Qwen text model on the box (27 GGUF files, 0.5B to 48 GB) was measured one at a time against llama.cpp on the same card: three families were silently declined or derailed (the qwen2 projection bias, a hybrid KV mirror one byte past 4 GiB, a missing add_bos default), and on a KHR-only cooperative-matrix device every K-quant prefill ran at a third of llama.cpp's rate (221 against 677 t/s on the 27B).
What changes.
add_bos_tokenfollows llama.cpp's per-pre default.vk_os_video_memory; allocations drop the priority tag; the grid GEMVs stage codebooks from a device buffer; the codebook scale row is two words.[spirv_decode]method directly: a struct parameter spills to a local, a plane-element argument passes its index.Observable behavior.
<|endoftext|>derails -> correct output.DASLLAMA_COOPMAT=mm): 4B Q4_K_M prefill 1564 -> 3051 t/s (0.72x of llama.cpp's KHR arm, from 0.37x), 27B UD-IQ4_XS 221 -> 395 on the kq tile before the slab fix; its re-measure after the fix goes with the next PR, where the KHR kernels move again.Where to look.
khr_tileand the templates indasllama_vulkan_classes.das, the element-index rail inspirv_emit.das, the auto arm indasllama_gpu_resident.das, the bias rope kernels.Validation, claims, ledger
Validation
tests/watchdog/test_watchdog.das, 157 s alone on this box (its twenty-thousand-line chatty-child cell 92 s on the interpreter), untouched by this branch, and CI runs no such cap; the JIT lane 14138 of 14152 with the same file as its one red; the utils lane: every one of its thirteen suites green (the ast-fuzz suite for the first time on this layout - it spawned a hardcodedbin/daslang, fixed here to the running binary), yet therun_utils_testsrule reports exit -1 after the last suite's SUCCESS under msbuild alone - the same daspkg command exits 0 from Bash, from cmd.exe and through a pipe; CI runs the rule on Linux; the AOT lane skips locally by design (the DLL-flavor host pins the runtime), so the full AOT sweep below stands in for it.test_aot, ~1080 TUs): 13233 of 13250, the seventeen others twelve skips and five reds intests/module_cache/test_default_cache_path.das- its child-spawning cells run the host binary as the child, which under the AOT host runs no script and prints no cache marker; they now skip on--use-aotthrough the gate the file's other two cells already carried (after the rebuild: one cell passes, seven skip, none fail; the interpreter still runs all eight).DN_MAX_QKat the three capacity checks) and streams the v region through; and the add-rms row stash, doubled to 32 KB for the 27B's 5120, past what a 32 KiB workgroup-memory device holds with the reduce's partials - ruled not a target: the tier's floor is the 48 KiB every kernel here is sized against, stated once in the GEMM architecture doc, and the stash stays at 8192.DASLLAMA_COOPMAT=mm); the thirteen kernel cells hold 0 of 89600 off with their edge tiles, the hybrid parity file 10 of 10 on the KHR tile, the Vulkan serving census reaches every format's sdot4, cm2 and KHR classes through the 1B vehicles under every coopmat mode (the KHR classes 32 to 330 dispatches each; the census's idle warnings 98 -> 36, the rest the L tiles and the q8 mul_mm tiles the 40-token census prompts never pick, and the MoE and attention variants above the small tier).DASLLAMA_VK_SPV_DUMP, read with spirv-dis).out_f32test as!= 0; the other 110 are byte-identical.DASLLAMA_VK_KV32=1the same cells run on the f32 mirrors (the qwen2 file 3 of 3 there, the bias rope's f32 twin served).harness/_vk_probe_fixture.das); every other exact match is the tier's seat idiom (a one-line installer, getter orinvokeforwarder per seat, the same shape as its neighbours by design) or a shape coincidence across domains (a two-value predicate, a name table), left as they are.Claims - stated, not tested
vk_os_video_memoryanswers 0 and the plan falls back to the probed budget.Not done
plans/shader_emitter_followups.mditem 2).followup_metal.mdsec. 6) - a Mac session.