Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 29 additions & 24 deletions modules/REVIEW_SHADER_EMITTERS.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Shared emitter rules - dasSpirv and dasMetal
# Shader Emitters Code Review Checklist

**Read `REVIEW_COMMON.md` (repo root) first - its contract binds this checklist. A change
under either module, or to any kernel body or fixture either emitter compiles, applies this
list with that folder's own.** Architecture docs: `dasMetal/ARCHITECTURE.md` and
`dasSpirv/ARCHITECTURE.md`.
**Read `REVIEW_COMMON.md` (repo root) first - its contract binds this checklist.** Architecture
docs: `dasMetal/ARCHITECTURE.md`, `dasSpirv/ARCHITECTURE.md`.

**Routed here by another checklist: a diff under either emitter module, or to any kernel body
or fixture either emitter compiles, applies this list together with its own folder's.**

**Never put anything that cannot run on the CPU into a kernel body or into a function a kernel
calls - keep both in ordinary das.** The CPU run of the same body is what the tests compare
against.

**A diff that adds or changes an emitter builtin ships a CPU body that returns what the emitted
form returns, argument for argument.**
**A diff that adds or changes an emitter builtin - a declaration in
`daslib/shader_lingua_franca.das` or an emitter's builtin table - ships a CPU body that returns
what the emitted form returns, argument for argument.**

**Never let a construct the emitter cannot lower produce a kernel or a crash - the emitter
reports a compile error that names the construct.**
Expand All @@ -19,35 +21,38 @@ reports a compile error that names the construct.**
constant.** A shape constant is any value that fixes the kernel's tiling: a tile row count, a
tile column count, a cooperating-simdgroup count, a staged chunk depth.

**Never pass a matmul reduction width known only at run time into a kernel that accumulates
through the emitter's matrix or tensor ops any way but through the emitter's runtime-extent
descriptor - `dynamic_extent` on Metal, and on SPIR-V a `tensorLayout2D` or `tensorLayout2DPad`
whose dimension `tensorLayoutSetDimension` sets.** The reduction width is the K dimension - the
length of the loop the kernel accumulates over; it does not fix tiling, so it is not a shape
constant. A hand-written reduction loop takes K as a plain uniform.
**Never pass a matmul reduction width known only at run time into a kernel that loads its
operands with the emitter's tensor-load ops (`coopmatLoadTensor*`, `coopmatLoadTensorDecode`)
any way but through the emitter's runtime-extent descriptor - `dynamic_extent` on Metal, and on
SPIR-V a `tensorLayout2D` or `tensorLayout2DPad` whose dimension `tensorLayoutSetDimension`
sets.** The reduction width is the K dimension - the length of the loop the kernel accumulates
over; it does not fix tiling, so it is not a shape constant. A loop that stages its own tiles
and loads fragments with `coopmatLoad` takes K as a plain uniform.

**A diff that makes a kernel need a shape value known only at run time ships a specialization
path, or records in the emitter's architecture doc - `dasMetal/ARCHITECTURE.md` for a Metal
kernel, `dasSpirv/ARCHITECTURE.md` for a SPIR-V kernel - that the kernel cannot have one.** A
specialization path is one compiled variant per constant shape.
**A diff that makes a kernel need a shape constant known only at run time ships a
specialization path, or records in the emitter's architecture doc - `dasMetal/ARCHITECTURE.md`
for a Metal kernel, `dasSpirv/ARCHITECTURE.md` for a SPIR-V kernel - that the kernel cannot
have one.** A specialization path is one compiled variant per constant shape.

**Never check a claim about emitted shape against the das source - check it in the emitted
words or text.** Emitted shape is the structure of the emitted kernel - its signature, its
parameter attributes, its statement forms - and its stamped shape values (tile, grid,
threadgroup sizes).

**A diff that adds a kernel-model capability to one emitter adds it to the other, or records
the asymmetry in the shared ledger (`dasMetal/ARCHITECTURE.md`).** A kernel-model
capability is anything that changes how a kernel is written or how its body is lowered.
the asymmetry in the shared ledger (`dasMetal/ARCHITECTURE.md`).** A kernel-model capability
is anything a kernel author can write differently because of it - an annotation, a call form,
a declaration the emitter now accepts or refuses; a change to lowering alone is the folder's
own checklist's.

**A diff that puts a `daslib/shader_lingua_franca` declaration into a kernel body or fixture an
emitter compiles, where that emitter does not handle it, ships, in the same change, either
that emitter's lowering of the declaration or a test showing the emitter rejects the
declaration by name.** A declaration in that module is available to both emitters.

**A `?:`, `&&`, or `||` in a `[spirv_kernel]` or `[compute_shader]` body, or in any `def` that
body calls, never indexes a global - a module global, a `@workgroup` array, or a
`self.<member>` resource - in an operand the condition can skip; read the value into a local
above the operator instead, and when the condition was the only thing keeping the index in
range, clamp the index.** The emitter lowers such an operand as a branch
(`dasSpirv/ARCHITECTURE.md`, "Operand laziness follows the language").
body calls, whose skippable operand subscripts a global-rooted array - a module global, a
`@workgroup` array, or a `self.<member>` resource - is written as the language reads it: never
rewritten around the emitter with an if/else, a clamp or an unconditional read.** The emitter
lowers such an operand as a branch (`dasSpirv/ARCHITECTURE.md`, "Operand laziness follows the
language"); a branchless form where it matters waits for `select` (`plans/shader_emitter_followups.md`).
10 changes: 6 additions & 4 deletions modules/dasLLAMA/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ re-transcoding `$LCPP/src/unicode-data.cpp`).
- `ARCHITECTURE_GPU_PREFILL.md` - sec.2.2c-2.2i, 2.2u-2.2v, 2.2aa: the Metal prefill driver's
GEMM form ladder, dev-W knee map, attention slab, MoE bucket rail, chunked submission, the
f16 twin dual-store, the last-layer FFN tail, and the dense-KQ tensor mul_mm scaffold.
- `ARCHITECTURE_GPU_VULKAN.md` - sec.2.2j-2.2m, 2.2p-2.2q: the Vulkan resident driver's kernels
and encode chains - the prefill window chain, the cm2 decode lanes and tile pick, the
class-pipeline build seat, the Q8 requant byte store, and the MoE expert chain on the cm2
tiles.
- `ARCHITECTURE_GPU_VULKAN.md` - sec.2.2j, 2.2p, 2.2ab: the Vulkan resident driver's prefill
chain and byte stores - the prefill window chain, the Q8 requant byte store, and the decode
GEMV family's grid codebook buffer.
- `ARCHITECTURE_GPU_VULKAN_GEMM.md` - sec.2.2k-2.2m, 2.2q: the cooperative-matrix tiles the
Vulkan tier's GEMMs run on - the cm2 decode lanes, the tile pick and the coopmat mode ladder,
the class-pipeline build seat, and the MoE expert chain on those tiles.
- `ARCHITECTURE_GPU_VULKAN_RESIDENCY.md` - sec.2.2n-2.2o: what a model has to fit on the card
before the driver runs - the residency plan, and the GPU-slot marks swap that lets one slot
serve many models.
Expand Down
16 changes: 14 additions & 2 deletions modules/dasLLAMA/ARCHITECTURE_ENGINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,12 @@ stay the reviewer's. A mis-numbered arm dispatches, reads the wrong buffer, and
share no state; a third merge algorithm gets a fourth file. Two sanctioned family-name tests
live here rather than in `dasllama_pretok`: the `pre`-name selector inside `bpe_encode`, and the
gemma-4 newline-run split in `bpe_encode_spm_space`; `load_bpe_tokenizer_gguf`'s per-family
metadata defaults are the third and last.
metadata defaults are the third and last. A GGUF without `tokenizer.ggml.add_bos_token` takes
upstream's per-pre default (`bpe_pre_adds_bos`): the pres on its list - the llama3 family,
tekken, chameleon, a few smaller ones - prepend BOS, every other pre - qwen2, qwen35, gpt-2,
gpt-4o among them - prepends none. The
default matters: a Qwen hybrid fed a leading `<|endoftext|>` degenerates for the whole
generation, and the unsloth Qwen3.8 conversions omit the key.
- **`dasllama_pretok.das`** - the pre-tokenizer: one hand-compiled split function per family
(llama3/qwen2/qwen35, gpt-2, gpt-4o, tekken), selected by the BPE `pre` name. Regex-port growth
lands here, never in the merge engine - the two change for different reasons (new model family
Expand All @@ -152,7 +157,14 @@ stay the reviewer's. A mis-numbered arm dispatches, reads the wrong buffer, and
detection, the eager and streamed conversion ladders, and the load entry points. It owns nothing the
forward path touches at run time - a loaded `Model` is the whole handoff - and it requires
`dasllama_common` back for `Model`/`Session`, so the transformer umbrella requires it `public` and
breaks the cycle. That re-export is what keeps every consumer on the facade.
breaks the cycle. That re-export is what keeps every consumer on the facade. Every big-weight
conversion is accounted as it runs (`conv_account`, on both the eager and the streamed ladder),
and a load that converts ends with the report: the file's bytes, then each (disk type ->
conversion kind) as bytes on disk against bytes served with the delta, biggest overhead first,
and the count of tags the row-length rule demoted to the q8 rail (each demotion logged by
name as it is decided). The delta column is where a served form costs more than the file - a
decoded scale row, an f32 copy of a quantized token table - and it is read as a number, not
inferred from image sizes.

- **`dasllama_image.das`** - the prepared-model `.dlim` rail, and it is ONE rail
(`ARCHITECTURE_IMAGE.md` sec.2.1). Nothing
Expand Down
16 changes: 10 additions & 6 deletions modules/dasLLAMA/ARCHITECTURE_GPU.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ that a question answered for one backend has an obvious address in the other. Th

| role | holds | must not hold |
|---|---|---|
| the kernel home<br>`dasllama_metal_kernels`, `dasllama_vulkan_classes` | kernel source, the derived-access/PSO census | device state, engine types |
| `dasllama_<gpu>_common`<br>`dasllama_metal_common`, `dasllama_vulkan_common` | device state, buffer/command plumbing, hazard + capture rail, profiler, shared quant-decode helpers (`iq4_lut`) | driver policy |
| the kernel home<br>`dasllama_metal_kernels`, `dasllama_vulkan_classes` | kernel source, the kernel-side quant-decode helpers and codebook tables, the derived-access/PSO census; on Vulkan the one device buffer kernel data fills (`kq_grid_dev`, the grid codebooks) | device state other than `kq_grid_dev`, engine types |
| `dasllama_<gpu>_common`<br>`dasllama_metal_common`, `dasllama_vulkan_common` | device state, buffer/command plumbing, hazard + capture rail, profiler, host-side quant-decode helpers (Metal's `iq4_lut`) | driver policy |
| `dasllama_<gpu>_decode`<br>`dasllama_metal_decode`, `dasllama_vulkan_decode` | the resident token-step driver + decode-time arms | kernel bodies |
| `dasllama_<gpu>_prefill`<br>`dasllama_metal_prefill`, `dasllama_vulkan_prefill` | the batched prefill driver + batch arms | kernel bodies |
| `dasllama_<gpu>_shapes`<br>`dasllama_metal_shapes` | PORTABLE servability gates - no GPU C++ require, so any box can bake | device calls |
Expand Down Expand Up @@ -88,7 +88,10 @@ that a question answered for one backend has an obvious address in the other. Th
`set_moe_gpu_attn_dec_hooks`, the decode FFN tail `set_moe_gpu_ffn_tail_hooks`, the deltanet
decode step's state seams `set_moe_gpu_dn_state_hooks` (flush, invalidate, release), the
whole-token span `set_moe_gpu_span_dec_hook` - the span rides common's decode override
registry as `vulkan_moe_span`, selected by the MoE placement and declining per token). The
registry as `vulkan_moe_span`, selected by the MoE placement and declining per token - the
resident driver's q/k/v projection-bias seat `install_moe_gpu_resident_bias`, the OS
video-memory seat `install_moe_gpu_os_memory` the residency plan sizes against, and the
weight-bytes seat `install_rdec_note_weight_bytes` the decode warm-up guard reads). The
installs are one-way: a test that arms the tier installs the seats and never restores them,
because no uninstall exists and none is needed - a seat serves whatever model loads next; Metal
deliberately does not, because UMA makes residency moot there and Metal
Expand Down Expand Up @@ -223,7 +226,6 @@ its PORT/PORTH/PORTC arms and `benchmarks/attn/lcpp_flash_dk72.metal` are the or
external reference the shipped `MetalTowerFlash` was decided against, and the DIAL arm races
the production dialect against them - the bisect seat when the flash regresses.


- **Per-simdgroup register-fragment matmul2d (16x32x16, device -> `vec<T,8>` fragments):**
1.7-2.0x slower for weight GEMMs, vectorized loads and deep n-blocking included. The
fragment architecture pays for attention's streaming operand reuse (one resident Q against
Expand Down Expand Up @@ -258,8 +260,10 @@ consecutive staging runs, relaxed_precision always - are `REVIEW_GPU.md` rules a
`modules/dasMetal/REVIEW.das` descriptor gate; this section keeps only the refuted shapes
and why they lose.

Sections 2.2j-2.2m and 2.2p-2.2q, the Vulkan resident driver, are `ARCHITECTURE_GPU_VULKAN.md`;
its 2.2n-2.2o - the residency plan and the marks swap - are `ARCHITECTURE_GPU_VULKAN_RESIDENCY.md`.
Sections 2.2j, 2.2p and 2.2ab, the Vulkan resident driver's prefill chain and byte stores, are
`ARCHITECTURE_GPU_VULKAN.md`; its 2.2k-2.2m and 2.2q - the cooperative-matrix GEMM tiles - are
`ARCHITECTURE_GPU_VULKAN_GEMM.md`; its 2.2n-2.2o - the residency plan and the marks swap - are
`ARCHITECTURE_GPU_VULKAN_RESIDENCY.md`.

### 2.2w The tower attention routes {#tower-attn-routes}

Expand Down
5 changes: 4 additions & 1 deletion modules/dasLLAMA/ARCHITECTURE_GPU_QUANT_PLANES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ a region's bind offset must be a multiple of 512 elements - the `(off/256)*2` d-
4-byte aligned only then - which is what `metal_blob_off_ok` and `moe_site_ok` check. iq4nl is
the exception: it reuses q40's 16-byte plane of eight f16 d per superblock, binds once, and
ignores `doff`. The Vulkan tier does not use this form - it binds the decoded 20-byte row as five
uints per superblock.
uints per superblock, except for iq4xs, iq3s, iq3xxs and iq2xxs, whose device row is two words
(`kq_dev_ssb`): f16 d with the format's native high bits above it, then eight 4-bit sub-scale
fields - iq4xs's `scales_h` and `scales_l` verbatim, the others' `s` nibbles - which the
gather packs from the plane's decoded bytes and `iq4xs_sc` / `grid_sc` decode in the kernels.

### 2.2z The iquant GEMV grid read and its f4-slab twin {#metal-iquant-gemv-grid}

Expand Down
Loading
Loading