- Deleted fmha.py (CuTeDSL slow path), FmhaKernel, Python KV merge - Deleted fmha_sm100.cuh, fmha_sm100_tc.cuh, fmha_sm100_launch.cu, fmha_epilogue_sm100.cuh - Moved fmha_qk_verify.cuh to tests/unit/qk_verify_kernel.cuh - Deleted decode_sparse.py, decode_swa.py, kernels/decode/ - Deleted 46 test_d*.py probes, test_smem_*, test_cotiled_*, test_tmem_*, test_smem_p_*, test_ultra_minimal, test_fmha_pv16, test_working_softmax_maybe - Deleted root scratch: debug_linear.py, test_mapping.py, run_router_tests.py - Moved archive/ to archived_plans/code_archive/ - Rewrote production.py: single fast path via 6-warp multi-tile kernel - Added STATUS.md, audit_attention_live.md - Moved NEXT_PRIORITIES*.md to archived_plans/
401 lines
20 KiB
Markdown
401 lines
20 KiB
Markdown
# ROADMAP — Stage E and the path to a runnable model
|
||
|
||
**Method.** Every state claim below was verified against the source in this
|
||
revision. Agent status doc and agent assessment were both read, then ignored
|
||
where they contradict the code. The doctrine rules from prior issue docs apply:
|
||
DSL wall → raw CUDA C++ (not Python); raw CUDA ≠ scalar math; print, don't
|
||
guess; integration over exploration; falsifiable gates only.
|
||
|
||
---
|
||
|
||
## TL;DR — where we actually are
|
||
|
||
**The FMHA kernel itself is in genuinely good shape.** `fmha_6warp_tma_multirow_multitile.cuh`
|
||
is one file, properly Blackwell-native (`tcgen05.mma` SS, UMMA descriptors,
|
||
TMEM, `cp.async.bulk.tensor.2d` + `mbarrier.arrive.expect_tx`, in-kernel
|
||
multi-tile rescale via SMEM accumulator, multi-row softmax). The TMA hang from
|
||
P4 was correctly diagnosed (missing `expect_tx`) and resolved. The C-API +
|
||
ctypes shim works. **P3, P4, P5, P6, P7, P8 numerics are done.**
|
||
|
||
**The agent's "Stage E" assessment is correct in spirit but wrong on specifics.**
|
||
Where it says "AttentionSubBlock has `NotImplementedError` stubs" — it doesn't.
|
||
The layer is structurally complete and *imports the right things*. The real
|
||
problem is one layer down: it calls cache methods like `gather_compressed_kv()`,
|
||
`gather_swa_kv()`, and properties like `cache.num_query_heads` / `cache.head_dim`
|
||
**that don't exist on `LayerCacheHandle`**. The handle exposes `read_*_view()`
|
||
returning paged FP8 buffers; nothing materializes them into the BF16 dense
|
||
tensors `dsv4_attention` consumes. That's the integration gap, not stubs.
|
||
|
||
**Three structural shortcuts still live in production code.** They are not
|
||
performance-fatal yet but they will be at scale:
|
||
1. Two `torch.cuda.synchronize()` calls remain on the kernel-launch path
|
||
(`fmha_multitile_op.py:130`, `production.py:279`). The second is in the still-resident
|
||
"slow path." The first is unconditional, even on the fast path.
|
||
2. The slow CuTeDSL+Python-merge path is **not deleted**, only relabeled
|
||
"slow path." 471 lines of code, 48 unit tests bound to its `FmhaKernel` symbol,
|
||
still imported at `production.py:53`.
|
||
3. The batched call shape (4D q) does a **Python for-loop over batch items**
|
||
at `production.py:380` instead of folding batch into the grid.
|
||
|
||
**Cleanup is non-trivial.** ~22 dead/stale code paths live alongside the
|
||
working ones. Some are old CuTeDSL drafts (`ops/decode_sparse.py`,
|
||
`ops/decode_swa.py`), some are pre-consolidation Stage D probes
|
||
(`fmha_sm100.cuh`, `fmha_sm100_tc.cuh`, `fmha_sm100_launch.cu`,
|
||
`fmha_qk_verify.cuh`, `fmha_epilogue_sm100.cuh`), some are misplaced root files
|
||
(`debug_linear.py`, `test_mapping.py`, `tests/working_softmax_maybe.py`), and 46
|
||
of 177 unit tests are pinned to the deprecated `FmhaKernel`. Left in place,
|
||
this is the primary path by which an agent will drift back into the wrong
|
||
codebase the next time it context-switches.
|
||
|
||
---
|
||
|
||
# PART 1 — CLEANUP (do this first, in this order)
|
||
|
||
Cleanup is gated *before* Stage E work because the next stage's failure mode is
|
||
"agent picks the wrong file to extend." Right now `dsv4/kernels/attention/` has
|
||
14 files; only 6 are live. That ratio is the diagnostic.
|
||
|
||
## C1. Confirm what's live with `grep`, not by reading status docs
|
||
|
||
Before deleting anything: produce `audit_attention_live.md` listing each file
|
||
under `dsv4/kernels/attention/` and `dsv4/ops/` with its **outbound reference
|
||
count from non-archive code** (production, layers, model, integration tests).
|
||
Anything with 0 refs is a deletion candidate; anything with >0 refs gets
|
||
inspected to confirm the reference is live, not a stale import.
|
||
|
||
**Falsifiable gate:** the audit table goes into `archived_plans/audit_attention_live.md`,
|
||
file count column adds up to the actual directory listing. Mention deletion
|
||
candidates by name with the ref count next to them.
|
||
|
||
## C2. Delete the dead CuTeDSL FMHA scaffolding
|
||
|
||
These have **zero external refs** outside their own `#include` chain and
|
||
self-references:
|
||
|
||
| File | Status | Action |
|
||
|---|---|---|
|
||
| `dsv4/kernels/attention/fmha_sm100.cuh` | Phase-1 scalar reference, header says "WORKING cos 0.999999" | Delete. The 6-warp kernel supersedes it. |
|
||
| `dsv4/kernels/attention/fmha_sm100_tc.cuh` | Earlier tensor-core fork before consolidation. | Delete. The 6-warp kernel is its successor. |
|
||
| `dsv4/kernels/attention/fmha_sm100_launch.cu` | Header says "COMPILES but doesn't run via torch.utils.cpp_extension" | Delete. Replaced by `fmha_multitile_capi.cu` + ctypes. |
|
||
| `dsv4/kernels/attention/fmha_qk_verify.cuh` | Header says "QK GEMM verification" — one test references it. | Move to `tests/unit/qk_verify_kernel.cuh` (it's a test fixture, not a library kernel). |
|
||
| `dsv4/kernels/attention/fmha_epilogue_sm100.cuh` | Phase-2 epilogue prototype; the multi-tile kernel already does in-kernel rescale via SMEM and doesn't use this. | Delete after confirming it's not `#include`'d by anything live. Audit table from C1 answers this. |
|
||
| `dsv4/ops/decode_sparse.py` | Old CuTeDSL CSA decode draft, not called from anywhere. | Delete. Path is now `layers/attention.py:_forward_csa` → `kernels/attention.sparse_fmha_with_swa` → `kernels/attention/production.py`. |
|
||
| `dsv4/ops/decode_swa.py` | Old CuTeDSL SWA decode draft, same story. | Delete. |
|
||
| `dsv4/kernels/decode/` | Two files: `__init__.py` (empty) and `_NOTES_fp8_bf16.md` (scratch). | Delete the whole directory. |
|
||
|
||
**Falsifiable gate:** after C2, `find dsv4/kernels/attention -name '*.cuh' -o -name '*.cu' | wc -l`
|
||
returns the value in `audit_attention_live.md`'s live count. No reference
|
||
warnings on `python -c "import dsv4"`.
|
||
|
||
## C3. Decide the fate of the CuTeDSL slow path (`fmha.py`)
|
||
|
||
`fmha.py` is 645 lines and is the entire pre-P3-P8 CuTeDSL path. It is currently
|
||
imported by `production.py:53` (`from dsv4.kernels.attention.fmha import FmhaKernel`)
|
||
and used by the `else` branch starting at `production.py:410` ("SLOW PATH"). It
|
||
is also imported by **48 unit tests**.
|
||
|
||
**Two options. Pick one, document it, do not maintain both.**
|
||
|
||
- **Option A (preferred, matches doctrine).** Delete `fmha.py`, delete the
|
||
SLOW PATH branch from `production.py`, delete the 48 `test_d*.py` unit tests
|
||
bound to `FmhaKernel`. The 6-warp multi-tile kernel covers
|
||
`hd∈{64,128,256,512}` × `T=1` decode × any `n_segments`. That's the production
|
||
surface. Everything else is exploration that already paid off — it does not
|
||
need to ship.
|
||
- **Option B (fallback, only if Option A is blocked).** Move `fmha.py` and the
|
||
48 tests to `archived_plans/cutedsl_legacy/`. Delete the SLOW PATH from
|
||
`production.py` regardless. The kernel and tests stay buildable for reference
|
||
but are no longer in the active import graph.
|
||
|
||
**Failure mode to refuse:** "keep it as fallback in case the 6-warp kernel has
|
||
a regression." If there's a regression, you fix the 6-warp kernel — that's the
|
||
production kernel. Carrying a parallel implementation guarantees both rot.
|
||
|
||
**Falsifiable gate:** `grep -r "FmhaKernel" dsv4/ tests/ scripts/ | wc -l` returns
|
||
0 (Option A) or only matches inside `archived_plans/` (Option B). The
|
||
SLOW PATH branch is gone from `production.py` either way.
|
||
|
||
## C4. Empty the `archive/` subdirectory the same way
|
||
|
||
`dsv4/kernels/attention/archive/` currently holds 5 backups
|
||
(`fmha_backup_pre_epilog.py`, `fmha_backup_v2.py`, `fmha_smem_acc.py`,
|
||
`fmha_6warp_tma_driver_api.cuh`, `fmha_tma_driver_api.cuh`). These were correct
|
||
to *not* delete during P8 consolidation. Now: move them out of `dsv4/`
|
||
entirely into `archived_plans/code_archive/` and delete `dsv4/kernels/attention/archive/`.
|
||
Active code directories should contain only active code. `archived_plans/` is
|
||
the right place for everything that isn't.
|
||
|
||
**Falsifiable gate:** `find dsv4 -name 'archive' -type d | wc -l` returns 0.
|
||
|
||
## C5. Move the root-level scratch files
|
||
|
||
Three files live at the repo root and don't belong there:
|
||
|
||
- `debug_linear.py` (58 lines, computes expected O for a debug pattern — Stage D
|
||
probe). → `tests/unit/archive/` or delete if no longer useful.
|
||
- `test_mapping.py` (49 lines, SMEM-P coordinate mapping check — Stage D probe). → same.
|
||
- `run_router_tests.py` (hardcoded path `/root/dsv4-nvfp4-workspace/kernel`). →
|
||
Either generalize the path and move to `scripts/`, or delete. Hardcoded
|
||
absolute paths in tracked files are bug magnets.
|
||
- `tests/working_softmax_maybe.py` (496 lines, name speaks for itself). → If
|
||
it's a draft, archive it. If it's superseded, delete it. The doctrine is
|
||
"code or archive, never `maybe`."
|
||
|
||
**Falsifiable gate:** repo root has no `.py` files except generated/build
|
||
artifacts. `tests/` contains no `*_maybe.py` or `working_*.py`.
|
||
|
||
## C6. Prune the unit test D-probe explosion
|
||
|
||
46 `test_d*.py` files were Stage D debugging probes. Many are bound to
|
||
`FmhaKernel` and will be deleted in C3 if you pick Option A. The remainder that
|
||
test components still in use (`FmhaTmaMultiRowMultiTile`, the indexer, the
|
||
compressor, mHC, MoE) should either be promoted to named integration tests
|
||
(`test_fmha_decode.py`, `test_csa_indexer.py`, etc.) or archived.
|
||
|
||
**Falsifiable gate:** `ls tests/unit/test_d*.py | wc -l` returns ≤ 5
|
||
(only the ones genuinely testing currently-live behavior, with descriptive names).
|
||
|
||
## C7. Status doc consolidation
|
||
|
||
After C1–C6, write **one** new top-level `STATUS.md` describing the **post-cleanup**
|
||
state of the production path, and delete `NEXT_PRIORITIES.md` (this doc replaces it).
|
||
`archived_plans/` already holds the historical paper trail; do not add to it
|
||
during cleanup.
|
||
|
||
**Falsifiable gate:** repo root has exactly two living `.md` files: `README.md`
|
||
and `STATUS.md` (plus this `ROADMAP.md`). `archived_plans/` holds the history,
|
||
unchanged.
|
||
|
||
---
|
||
|
||
# PART 2 — STAGE E PRIORITY ORDER (post-cleanup)
|
||
|
||
The agent is right that Stage E = "make the model run end-to-end." The order
|
||
below is sequenced so each priority unblocks the next, and so the
|
||
performance-decisive moves come *after* end-to-end correctness, not before.
|
||
|
||
## E1. Wire `LayerCacheHandle` to expose what `AttentionSubBlock` calls
|
||
|
||
This is the load-bearing missing piece. `dsv4/kernels/attention/__init__.py`
|
||
calls four things that don't exist on `dsv4/cache/handle.py`:
|
||
|
||
- `cache.gather_compressed_kv(selected_indices) → (k_compressed, v_compressed)` BF16 dense
|
||
- `cache.gather_all_compressed_kv() → (k_compressed, v_compressed)` BF16 dense (HCA, no top-k)
|
||
- `cache.gather_swa_kv() → (k_swa, v_swa)` BF16 dense
|
||
- `cache.num_query_heads`, `cache.head_dim` (properties)
|
||
|
||
The handle already exposes the right *raw* state via
|
||
`read_classical_view()` (FP8 entries + RoPE BF16 + paged block_table) and
|
||
`read_swa_view()` (FP8 entries + RoPE BF16 + positions). The work is the
|
||
gather+dequantize+concat path.
|
||
|
||
**Constraints (doctrine):**
|
||
|
||
- Each gather is a **single fused kernel**, raw CUDA C++ if CuTeDSL can't do
|
||
block-table-strided FP8→BF16 dequant+gather in one pass. Not a Python loop
|
||
over selected indices. Not five eager torch ops.
|
||
- The kernel uses TMA + warp-level dequant, not scalar loads. The MoE GEMM
|
||
already proves out the FP8 dequant pattern on Blackwell — reuse it.
|
||
- The dequant decodes E4M3 with the inverse-scale stored alongside. **Print
|
||
the actual layout of `paged.entries_fp8` + `paged.inv_scale` before writing
|
||
the kernel** — do not assume the layout matches an MoE convention.
|
||
|
||
**Definition of done:**
|
||
1. The four methods exist on `LayerCacheHandle`, implemented in
|
||
`dsv4/kernels/cache/gather.{cu,py}`.
|
||
2. `python -c "from dsv4.layers.attention import AttentionSubBlock; ..."` runs
|
||
a forward pass on one layer without AttributeError, NotImplementedError,
|
||
or shape mismatch.
|
||
3. Numerical parity vs a pure-Python reference (FP32 dequant + dense FMHA over
|
||
the gathered tensors): `cos ≥ 0.9995`. FP8 dequant loses a little, that's
|
||
expected.
|
||
4. **Launch count for one CSA layer** measured with Nsight: gather is ≤ 3 launches
|
||
(compressed gather, SWA gather, concat). Not 30, not 5+5+cat.
|
||
|
||
**Failure modes to refuse:**
|
||
- A Python gather loop over the block table. The block table is on GPU; the
|
||
gather is on GPU. No host trips.
|
||
- BF16 materialization "for now" without FP8 dequant. The paged cache stores
|
||
FP8 because that's the design — bypassing it loses 2x KV memory and the
|
||
whole point of the cache structure.
|
||
|
||
## E2. End-to-end smoke test through one full layer
|
||
|
||
After E1, both halves of `dsv4/model/layer.py` (`mhc_attn` → `attn.forward` →
|
||
`mhc_ffn` → `ffn.forward`) should execute without error. The test is the
|
||
forcing function.
|
||
|
||
**Definition of done:**
|
||
1. `tests/e2e/test_one_layer.py` runs `DSV4Layer.forward(X, token_ids, cache)`
|
||
on synthetic inputs with cached weights loaded from `loader/`, returns a
|
||
tensor of the right shape and dtype, no errors.
|
||
2. The same test with `cache` containing actual paged FP8 entries (not zeros)
|
||
produces output with `cos ≥ 0.99` against a Python reference of the same
|
||
layer (compressor + indexer + FMHA + mHC, all in eager PyTorch).
|
||
3. Test exists for all three attention types (CSA, HCA, SWA) by toggling the
|
||
layer's `attn_type`.
|
||
|
||
**Failure mode to refuse:** wrapping the test in a try/except to "make it
|
||
pass." The cos threshold is the gate.
|
||
|
||
## E3. Top-level `model/dsv4.py` (currently a 2-line TODO)
|
||
|
||
`dsv4/model/dsv4.py` is literally:
|
||
|
||
```python
|
||
"""Full DSV4 model."""
|
||
# TODO: Phase 1
|
||
```
|
||
|
||
Build the actual model class: embedding → N×DSV4Layer → final norm → prediction
|
||
head. Use `layer_schedule.py` to alternate CSA/HCA per the V4 spec
|
||
(Pro: layers 0–1 HCA, then interleaved; Flash: layers 0–1 SWA, then
|
||
interleaved).
|
||
|
||
**Definition of done:**
|
||
1. `DSV4Model.forward(token_ids, cache_manager) → logits` works on a single
|
||
token decode.
|
||
2. Layer schedule matches the V4 paper (verify against `config.py`).
|
||
3. MTP module is wired but optional (off by default; doctrine note in code
|
||
that MTP fine-tuning is a separate workstream).
|
||
4. End-to-end test: `tests/e2e/test_decode.py` generates one token from a
|
||
3-layer toy config, succeeds, cos ≥ 0.99 vs Python reference.
|
||
|
||
## E4. Delete the two remaining `torch.cuda.synchronize()` calls on the fast path
|
||
|
||
After C3 the slow-path sync at `production.py:279` is gone with the slow path.
|
||
The other sync is at `fmha_multitile_op.py:130`:
|
||
|
||
```python
|
||
# Synchronize to catch async errors
|
||
torch.cuda.synchronize()
|
||
return o, lse
|
||
```
|
||
|
||
This is on the *fast path*. It's there to catch async kernel errors, but the
|
||
correct way to do that is to check the return code from the C-API launch
|
||
function (which `fmha_multitile_decode_launch` already returns) plus
|
||
`cudaPeekAtLastError`, not a full device sync.
|
||
|
||
**Definition of done:**
|
||
1. The sync is removed from `fmha_multitile_op.py`.
|
||
2. Error checking moves to a `cudaPeekAtLastError` call + the existing return
|
||
code check.
|
||
3. Nsight measurement on a 4-layer decode: zero `cudaDeviceSynchronize` calls
|
||
between layer 0 entry and final logits.
|
||
|
||
## E5. Fold the batch loop into the kernel grid
|
||
|
||
`production.py:380–388` is a Python for-loop over batch items. Each iteration
|
||
calls back into `dsv4_attention` on a 3D slice. For batch=8 and 61 layers,
|
||
that's 488 launches per decoded step instead of 61.
|
||
|
||
The multi-tile kernel's grid is currently `(1, n_h, 1)`. It needs to become
|
||
`(1, n_h, batch)` with `blockIdx.z` used to index batch. The kernel already
|
||
has `_batch_stride` params (read off `FmhaTmaMultiRowMultiTileParams`); they
|
||
just aren't wired to a grid dim.
|
||
|
||
**Definition of done:**
|
||
1. `production.py:380–388` for-loop deleted; the batched path calls the
|
||
multi-tile kernel once with `gridDim.z = batch`.
|
||
2. Numerical parity: `cos ≥ 0.999998` vs the deleted Python loop.
|
||
3. Launch count for batch=8 decode: 1 per layer, not 8.
|
||
|
||
## E6. NVFP4-1.2: FP4 output fusion for FMHA → `wo_a`
|
||
|
||
Right now the FMHA writes BF16 to GMEM, then `wo_a` re-quantizes it to FP4.
|
||
That's a redundant memory pass on the attention output. The epilogue already
|
||
has the one-way TMEM→regs→SMEM→GMEM shape — adding an FP4 pack with amax in
|
||
registers, gated by a template param, is the same pattern the MoE epilogue
|
||
already uses for NVFP4-1.1.
|
||
|
||
**Constraints (doctrine):**
|
||
- The FP4 pack is **part of the same single launch**. No second kernel for
|
||
quantize. Per-128-element block amax reduces via warp shuffle in registers.
|
||
- The amax → scale → E2M1 LUT path matches `dsv4/ops/quantize.py` byte for byte.
|
||
Print the LUT from the Python side; assert equality in a unit test. The
|
||
indexer LUT bug we already fixed is the cautionary tale.
|
||
- This is gated on E4 (`epilogue_op` slot exists in the multi-tile kernel —
|
||
let's verify it does before starting E6).
|
||
|
||
**Definition of done:**
|
||
1. `fmha_6warp_tma_multirow_multitile_kernel` accepts a template arg `Epilogue`
|
||
(default = identity); a `FP4Epilogue` specialization packs E2M1 + emits SF
|
||
blocks in the layout `wo_a` expects.
|
||
2. `tests/unit/test_fp4_epilogue_parity.py`: BF16 reference vs FP4 fused,
|
||
`cos ≥ 0.999` after dequant.
|
||
3. End-to-end CSA layer: BF16 output → FP4 output replaces one full BF16 R/W
|
||
pass through (T, n_h * hd) memory. Measured bandwidth drop with Nsight.
|
||
|
||
## E7. Stage F: Lightning indexer FP4 tensor-core scoring
|
||
|
||
Currently `indexer_score_topk.cu` uses scalar FP32 cores after the LUT dequant.
|
||
The paper §5.2.1 is explicit that this path runs in FP4 on tensor cores
|
||
("multiplied entirely in FP4 ... 99.7% recall"). The LUT bug fix made the
|
||
scalar path *correct*; this priority makes it *fast*, the way V4 actually
|
||
specifies it.
|
||
|
||
**Constraints (doctrine):**
|
||
- `tcgen05.mma` with `mxf4nvf4` kind. Keys and queries both FP4. Scales E4M3
|
||
in shared mem, hardware decodes. No scalar fallback in the live path.
|
||
- Recall vs the FP32 oracle ≥ 99.7% per paper. The recall test from the LUT
|
||
fix is the same test; the bar is the same.
|
||
- The top-k selector goes warp-level (`__shfl_xor` ballot) at the same time —
|
||
the current serial single-thread heap merge at top_k=1024 is its own latency
|
||
source on Pro decode.
|
||
|
||
**Definition of done:**
|
||
1. `indexer_score_topk.cu` has a `tcgen05`-MMA FP4 path, gated by a config
|
||
flag, with the scalar path archived (not deleted, since the FP4 path needs
|
||
the oracle).
|
||
2. Recall@k ≥ 99.7% for k ∈ {512, 1024} at compressed_blocks ∈ {2k, 8k, 32k}.
|
||
3. End-to-end CSA layer latency drops measurably (Nsight).
|
||
|
||
## E8. Multi-CTA grid for prefill (Priority 4 from the original ROADMAP)
|
||
|
||
Decode is single-CTA per head and that's correct. Prefill (T > 1) wants
|
||
multi-CTA to parallelize over M. The one-way epilogue from E6 is the
|
||
prerequisite — once it lands, the grid expansion is wiring.
|
||
|
||
**Definition of done:**
|
||
1. `gridDim.x = ceil_div(M, M_TILE)` for `T > 1` paths.
|
||
2. Prefill latency at T=128 with M-tile=64: 2-CTA grid, measured speedup ≥ 1.5×
|
||
over single-CTA.
|
||
3. Decode path unchanged (single CTA, no regression).
|
||
|
||
## E9. CUDA graph capture (Stage E item from the agent's notes)
|
||
|
||
Once E5 lands and the decode hot path is sync-free, graph capture is
|
||
straightforward. This is the biggest single perf win on autoregressive decode
|
||
because it eliminates host launch overhead entirely.
|
||
|
||
**Definition of done:**
|
||
1. `DSV4Model.decode_step(token, cache)` is `cudaGraph`-capturable.
|
||
2. Replay latency vs uncaptured: ≥ 2× faster on a 3-layer toy at batch=1.
|
||
3. Memory: graph capture doesn't `torch.zeros` on the hot path (E1 already
|
||
forced this for gather; verify it holds for compressor + indexer + FMHA).
|
||
|
||
---
|
||
|
||
# DOCTRINE — applies to every item above
|
||
|
||
1. **DSL wall → raw CUDA C++, not Python.** The C-API + ctypes pattern in
|
||
`fmha_multitile_op.py` is the correct shape for new kernels.
|
||
2. **Raw CUDA ≠ scalar math.** Every kernel above is `tcgen05` / UMMA / TMA /
|
||
warp reductions. No "temporary scalar dot product" without a labeled
|
||
replacement target.
|
||
3. **Print, don't guess.** Two places this round specifically:
|
||
- E1 gather kernels: print the FP8 cache layout + inv-scale layout before
|
||
writing the dequant.
|
||
- E6 FP4 pack: print the LUT from `quantize.py` and assert in a test that
|
||
the kernel produces byte-identical packed nibbles.
|
||
4. **Integration over exploration.** Do not create
|
||
`fmha_6warp_tma_multirow_multitile_v2.cuh`. Extend the chosen file or stop
|
||
and replan. Variant proliferation is the agent's primary failure mode in
|
||
this repo.
|
||
5. **Falsifiable gates only.** Every "done" above has a number or a binary
|
||
check. "Looks fine," "milestone complete," and "Stage X done" without a
|
||
number are read as "not done."
|
||
6. **The slow path is not a fallback.** Once C3 deletes the CuTeDSL+Python-merge
|
||
path, it stays deleted. If the 6-warp kernel breaks, you fix the 6-warp
|
||
kernel — not resurrect 471 lines of dead code. |