spark_model/layers/qwen3_ssm/
mod.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! Qwen3-Next SSM (Gated Delta Net) layer implementing TransformerLayer.
4//!
5//! Corrected pipeline matching the HuggingFace reference implementation:
6//!   1. QKVZ projection (interleaved output)
7//!   2. Deinterleave QKVZ → sequential [Q | K | V | Z]
8//!   3. BA projection (interleaved output)
9//!   4. Compute GDN gates: gate = exp(-A * softplus(alpha + dt_bias)), beta = sigmoid(b)
10//!   5. Conv1d update on [Q | K | V] concatenated (d_inner=8192)
11//!   6. Split conv output → Q', K', V'
12//!   7. GDN decode (Q', K', V', gate, beta) — kernel handles GQA internally
13//!   8. Gated RMS norm (GDN output, Z gate)
14//!   9. Output projection [value_dim → hidden_size]
15//!  10. MoE FFN
16
17use anyhow::Result;
18use spark_runtime::gpu::{DevicePtr, GpuBackend, KernelHandle};
19use spark_runtime::kv_cache::PagedKvCache;
20
21use crate::layer::{ForwardContext, GdnPrefillBuffers, LayerState, SsmLayerState};
22use crate::layers::FfnComponent;
23use crate::layers::ops;
24use crate::layers::w4a16_gemv_tiers::W4a16BatchmTiers;
25use crate::weight_map::{DenseWeight, Fp8Weight, QuantizedWeight, SsmWeights};
26
27/// Qwen3-Next SSM/GDN layer (36 of 48 layers).
28///
29/// Supports two QKVZ projection modes:
30/// - **Interleaved** (80B): `w4a16_gemv_qkvz` or GEMV + `deinterleave_qkvz`
31/// - **Sequential** (3.5-35B): plain GEMV → `[Q|K|V|Z]` already in order
32#[allow(dead_code)]
33pub struct Qwen3SsmLayer {
34    /// mHC weights when the model carries a `hc_mult`-wide highway; see `hc`.
35    pub(crate) hc: Option<crate::layers::qwen3_attention::HcWeights>,
36    /// PLE n-gram injection. `Some` on exactly ONE model layer (layer 1 on
37    /// this checkpoint); it runs at the TOP of the mHC forward, before this
38    /// layer's own hyper-connection, matching the reference's
39    /// `hidden_states = hidden_states + self.ple(...)`.
40    pub(crate) ple: Option<crate::layers::ple::PleLayer>,
41    /// mHC kernel handles. Resolved only when `config.hc_mult > 0`, so a
42    /// plain GDN model issues no lookup and leaves no row in the startup
43    /// audit. See `qwen3_attention::init_arch_gates`.
44    pub(super) hc_pre_k: KernelHandle,
45    pub(super) hc_post_k: KernelHandle,
46    /// Seeds the highway on MODEL layer 0 — which on a 3:1 GDN:attention
47    /// interleave is a GDN layer, so this side owns the expand that the
48    /// attention side used to do.
49    pub(super) hc_expand_k: KernelHandle,
50    input_norm: DenseWeight,
51    ssm: SsmWeights,
52    post_attn_norm: DenseWeight,
53    ffn: FfnComponent,
54    /// GDN `out_proj` LoRA delta for this layer, with the kernels to apply it.
55    /// `None` on every base serve, which keeps the base path byte-identical.
56    lora_out_proj: Option<(
57        crate::layers::ops::lora_delta::LoraPair,
58        crate::layers::ops::lora_delta::LoraKernels,
59    )>,
60    // NVFP4-quantized QKVZ weight (quarters bandwidth vs BF16)
61    qkvz_nvfp4: Option<QuantizedWeight>,
62    // Transposed [K/2, N] copy for coalesced w4a16_gemm reads (prefill)
63    qkvz_nvfp4_t: Option<QuantizedWeight>,
64    // Transposed out_proj for prefill GEMM
65    out_proj_nvfp4_t: Option<QuantizedWeight>,
66    // BF16 out_proj for models where SSM weights are not pre-quantized
67    pub out_proj_dense: Option<DenseWeight>,
68    // FP8 E4M3 checkpoint weights for native FP8 serving (w8a16_gemv LUT kernel)
69    qkvz_fp8w: Option<Fp8Weight>,
70    out_proj_fp8w: Option<Fp8Weight>,
71    /// PER-ROW FP8 (`Fp8PerRow`) for PREFILL ONLY, from mixed-precision
72    /// compressed-tensors checkpoints (`ATLAS_FP8_ROWWISE=1`).
73    ///
74    /// Separate fields rather than reusing `qkvz_fp8w`/`out_proj_fp8w`, and
75    /// that separation is the safety property: those two are read by
76    /// `w8a16_gemv` in `ssm_forward.rs` and `trait_decode_batched.rs`, which
77    /// index the scale as a `[N/128, K/128]` block grid. A per-row buffer is
78    /// SMALLER than that index space, so it would not fault — it would return
79    /// plausible garbage. Only the row-wise cuBLASLt prefill arm reads these;
80    /// decode keeps the NVFP4 copy.
81    qkvz_fp8w_rowwise: Option<Fp8Weight>,
82    out_proj_fp8w_rowwise: Option<Fp8Weight>,
83    /// This layer's slice of the LEDGERED row-wise BF16-weight slab
84    /// (`BufferSizes::ssm_rowwise_w_bf16`), or 0 before the first prefill
85    /// through the matching arm carves and fills it. See `rowwise_bf16.rs`
86    /// for the #917 receipt that moved these bytes out of a by-pointer cache
87    /// of `gpu.alloc`s and into the arena.
88    ///
89    /// Per LAYER rather than per weight pointer, because that is what the
90    /// lifetime is: the slab is the arena's, and the arena outlives every
91    /// prefill this layer runs.
92    qkvz_rowwise_bf16: std::sync::atomic::AtomicU64,
93    out_proj_rowwise_bf16: std::sync::atomic::AtomicU64,
94    /// Tier-1c keep-packed ternary Q2_0 fused in_proj_qkvz (`ATLAS_GGUF_NATIVE_Q2`).
95    /// [Q|K|V|Z] rows byte-concatenated from packed `in_proj_qkv` (V-region
96    /// row-permuted) + `in_proj_z` (row-permuted) at load, so the 2-bit weight is
97    /// HF-correct. `out_proj` stays NVFP4 (column reorder not packed-permutable here).
98    qkvz_q2: Option<crate::weight_map::PackedQ2Weight>,
99    /// Q2_0 kernels for the packed qkvz: `gemv` = `q2_0_gemv_vec` decode; `dequant`
100    /// = load-time packed→BF16 for the transient-dequant prefill fallback;
101    /// `mmq_{nc,wc}` = Tier-2 keep-packed tensor-core MMQ prefill (`KernelHandle(0)`
102    /// → fallback); `q4k_quant_act` = shared q8_1 activation quantizer.
103    q2_0_gemv_k: KernelHandle,
104    dequant_q2_0_gn_k: KernelHandle,
105    q2_0_mmq_nc_k: KernelHandle,
106    q2_0_mmq_wc_k: KernelHandle,
107    q4k_quant_act_k: KernelHandle,
108    /// When true, QKVZ projection output is already sequential [Q|K|V|Z].
109    /// Skips the deinterleave kernel (used by Qwen3.5 where QKV+Z are
110    /// concatenated at load time rather than interleaved per-group).
111    sequential_qkvz: bool,
112    /// Streaming multiprocessor count, read from the driver ONCE at
113    /// construction (`GpuBackend::sm_count`). `ms_proj_gemm` needs to know how
114    /// wide the machine is to decide whether halving the CTA rows is a saving
115    /// or an under-fill; a compiled-in constant would be wrong on every part
116    /// that is not the one it was tuned on.
117    sm_count: u32,
118    // Kernels — decode path (single-token GEMV)
119    rms_norm_residual_k: KernelHandle,
120    gated_rms_norm_k: KernelHandle,
121    gated_rms_norm_f32_k: KernelHandle,
122    /// `gated_rms_norm_f32_input_strided` — the SAME per-(sequence, head) math
123    /// as `gated_rms_norm_f32_k`, with `blockIdx.y` walking the sequences, so
124    /// a batched decode step spends ONE launch per layer instead of one per
125    /// row. 0 when absent (notably on a `gdn_norm_sigmoid` model, which has no
126    /// strided sigmoid twin), which keeps the per-seq loop. #927: the H100
127    /// batch-16 trace showed the per-seq loop at 768 launches / 1.61 ms.
128    gated_rms_norm_f32_strided_k: KernelHandle,
129    dense_gemv_k: KernelHandle,
130    /// K=2 verify: batched (M=2) BF16 GDN in_proj_qkvz — one weight pass for
131    /// both verify tokens instead of two M=1 `dense_gemv` reads.
132    dense_gemv_batch2_k: KernelHandle,
133    w4a16_gemv_k: KernelHandle,
134    /// Single-warp `w4a16_gemv_sw`. `KernelHandle(0)` on miss → base GEMV.
135    w4a16_gemv_sw_k: KernelHandle,
136    w8a16_gemv_k: KernelHandle,
137    w4a16_gemv_qkvz_k: KernelHandle,
138    deinterleave_k: KernelHandle,
139    conv1d_k: KernelHandle,
140    conv1d_l2norm_k: KernelHandle,
141    conv1d_l2norm_f32_k: KernelHandle,
142    /// `conv1d_l2norm_f32_k` with explicit input/output row strides, letting
143    /// the concurrent-decode path batch all N sequences into one launch.
144    /// `KernelHandle(0)` on kernel sets that predate it — the multi-seq path
145    /// then falls back to the per-sequence conv loop.
146    conv1d_l2norm_f32_strided_k: KernelHandle,
147    gdn_k: KernelHandle,
148    gdn_f32_k: KernelHandle,
149    gdn_f32_norm_k: KernelHandle,
150    gdn_f32_conv_norm_k: KernelHandle,
151    gdn_f32_strided_k: KernelHandle,
152    gdn_f32_strided_norm_k: KernelHandle,
153    /// Half-width register retention (k_dim==v_dim==128): retains the first 64 H
154    /// columns so the update re-reads only the rest (2R+1W -> 1.5R+1W).
155    gdn_f32_strided_norm_half_k: KernelHandle,
156    /// SRAM-staged full retention (k_dim==v_dim==128): the columns the register
157    /// file cannot hold are staged in shared memory on the first pass instead of
158    /// being re-read from H (1.5R+1W -> 1.0R+1W). Bit-identical to
159    /// `gdn_f32_strided_norm_half_k` but measured throughput-NEUTRAL, so it is
160    /// OPT-IN via `gdn_smem_stage_enabled()` (`ATLAS_GDN_SMEM_STAGE`).
161    gdn_f32_strided_norm_smem_k: KernelHandle,
162    /// FP16 h-state twin of `gdn_f32_strided_norm_half_k` (`ATLAS_SSM_H_FP16`).
163    /// Additive: it never replaces the FP32 kernel, it is selected instead of
164    /// it when the sequence's `SsmLayerState::h_is_f16` is set.
165    gdn_f16_strided_norm_half_k: KernelHandle,
166    /// FP16 h-state twin of `gdn_f32_norm_k` — the per-sequence arm the batched
167    /// dispatch falls back to at n == 1 and whenever pool slots fragment out of
168    /// slice order. Without it the FP16 pool would be read as FP32 on exactly
169    /// those steps.
170    gdn_f16_norm_k: KernelHandle,
171    ba_gates_k: KernelHandle,
172    residual_add_k: KernelHandle,
173    l2_norm_k: KernelHandle,
174    residual_add_rms_norm_k: KernelHandle,
175    /// Dual-output (bf16 + f32) MoE-input norm for ATLAS_FP32_ROUTING. Zero if absent.
176    residual_add_rms_norm_gatef32_k: KernelHandle,
177    gated_rms_norm_prefill_k: KernelHandle,
178    // Kernels — batched verification path (multi-token GEMM)
179    w4a16_gemm_k: KernelHandle,
180    w4a16_gemm_t_k: KernelHandle, // Transposed B layout [K/2, N] — K_STEP_T=32
181    w4a16_gemm_t_k64_k: KernelHandle, // K64 variant: K_STEP_T=64, halves outer loop
182    /// K64 with a 64-wide N tile: same math, 2x the CTAs. `KernelHandle(0)`
183    /// when absent or killed by `ATLAS_NO_K64_N64`.
184    w4a16_gemm_t_k64_n64_k: KernelHandle,
185    w4a16_gemm_t_m128_k: KernelHandle, // M128 variant: 2 M-chunks per CTA, halves B re-reads
186    w4a16_gemm_t_m128_v2_k: KernelHandle, // M128 8-warp pipelined (fast at small M; the FFN's kernel)
187    w4a16_gemv_batch2_k: KernelHandle,
188    dense_gemm_k: KernelHandle,
189    dense_gemm_pipelined_k: KernelHandle,
190    gdn_prefill_k: KernelHandle,
191    gdn_prefill_split_k: KernelHandle,
192    gdn_prefill_split4_k: KernelHandle,
193    gdn_prefill_persistent_k: KernelHandle,
194    gdn_prefill_persistent_wy4_k: KernelHandle,
195    /// Register-resident token-sequential warm-replay recurrence (H in regs, >=2
196    /// CTA/SM, no barriers). Token-equal to WY4 (cosine 1.0), ~2.9x faster.
197    /// DEFAULT-ON since 2026-07-25 (serve-validated: full MLPerf-edge e2e, wall
198    /// −7.25%, BFCL identical); kill switch `ATLAS_NO_GDN_REGRESIDENT=1`.
199    gdn_prefill_regresident_k: KernelHandle,
200    /// FLA multi-kernel chunked prefill (baked default for 128-dim GDN): recompute_wu →
201    /// chunk_delta_h_ksplit (k-split occupancy) → chunk_fwd_o. 1.75x vs wy4 @16k,
202    /// token-equal (cos=1.0 vs scalar). Three handles; all must be non-null.
203    gdn_prefill_fla_recompute_wu_k: KernelHandle,
204    /// Hopper twins of the two SCALAR REMNANTS of the FLA prefill (#928):
205    /// `gdn_recompute_wu_hopper.cu`'s blocked triangular solve on tensor cores
206    /// and `gdn_fwd_o_hopper.cu`'s masked `tril(kq).uc` square. They live only
207    /// under `kernels/hopper`, so `try_kernel` gives 0 everywhere else and the
208    /// launcher then runs the unchanged parents. Selected by the family lever
209    /// `[defaults] gdn_prefill_tc` (`ATLAS_GDN_PREFILL_TC` overriding), which
210    /// Hopper ships ON since round 13; `ATLAS_NO_GDN_PREFILL_TC_REMNANTS=1` pins them
211    /// off while keeping the tensor-core state spine, which is the A/B that
212    /// separates the three kernels. The nsys receipt that motivates them is in
213    /// `GDN-PREFILL-ATTRIBUTION.md`: 5.5% and 4.0% of a 1193-token H100
214    /// prefill, both with their big matmuls already on `mma.sync` and their
215    /// remainder — a triangular product on 128 of 512 threads, and two forward
216    /// substitutions worth 79-85% of their kernel — still scalar.
217    gdn_prefill_fla_recompute_wu_hopper_k: KernelHandle,
218    gdn_prefill_fla_chunk_fwd_o_hopper_k: KernelHandle,
219    gdn_prefill_fla_chunk_delta_h_k: KernelHandle,
220    /// Tensor-core / DV-block-split variant of the FLA chunk_delta_h spine
221    /// (`gated_delta_rule_chunk_delta_h_tc_vblock`). Loaded by default but not
222    /// yet wired into the prefill dispatch — the cos-gate validates it in
223    /// isolation first. `allow(dead_code)` until the launch site reads it.
224    #[allow(dead_code)]
225    gdn_prefill_fla_chunk_delta_h_tc_vblock_k: KernelHandle,
226    /// TENSOR-CORE chunked-prefill state spine
227    /// (`gated_delta_rule_chunk_tc::gated_delta_rule_chunk_delta_h_tcfuse`),
228    /// behind `[defaults] gdn_prefill_tc` — ON for `kernels/hopper` since round
229    /// 13, OFF elsewhere, with `ATLAS_GDN_PREFILL_TC` overriding either way.
230    /// Both per-chunk
231    /// products run on `mma.sync.m16n8k16` with bf16 operands and an f32
232    /// accumulator that IS the recurrent state; `h` stays f32 in memory. The
233    /// nsys receipt that motivates it is in `GDN-PREFILL-ATTRIBUTION.md`
234    /// (#928): the shipped scalar spine is 26.6%/32.3% of the 1193/4593-token
235    /// H100 prefill at 3.7 TFLOP/s, i.e. latency-bound at 4.5% warp residency.
236    /// `try_kernel` => 0 on images without it, and the launcher additionally
237    /// refuses any head/chunk that differs from the compile-time tile.
238    gdn_prefill_fla_chunk_delta_h_tcfuse_k: KernelHandle,
239    /// Warp-dense fused GDN state spine (`gated_delta_rule_chunk_delta_h_vtile`).
240    /// 512 threads = 16 warps/CTA against ksplit's 8, with the SAME grid (one CTA
241    /// per head) so `W`/`K` global loads are not duplicated — an ncu profile put
242    /// ksplit at L2 60.6% / L1 56.2%, i.e. memory-pipeline bound, which is why the
243    /// DV-split variants (which duplicate those loads) lost. Fusing the two
244    /// per-chunk passes deletes ksplit's `duc[CHUNK]` register array, and that is
245    /// what pays for the extra warps: 118 registers, no spills. Measured 2.15-2.18x
246    /// vs ksplit at 2048/8192/16384 with cos=1.0000 (`gdn_chunk_shapetest`).
247    gdn_prefill_fla_chunk_delta_h_fused_k: KernelHandle,
248    /// TMA (`cp.async.bulk.tensor`) build of the state spine, behind
249    /// `ATLAS_GDN_TMA=1`. `try_kernel` => 0 on images that lack it, and the
250    /// launcher additionally refuses varlen and any head narrower than the
251    /// compile-time tile — the descriptors encode that tile, and a mismatched
252    /// shape loads the wrong columns without erroring.
253    gdn_prefill_fla_chunk_delta_h_tma_k: KernelHandle,
254    gdn_prefill_fla_chunk_fwd_o_k: KernelHandle,
255    /// WY32 chunked prefill: processes 32 tokens per WY iteration with H in
256    /// shared memory. ~30x faster than per-token for 14k+ sequences.
257    gdn_prefill_wy32_k: KernelHandle,
258    // ── Q12 Phase 2b: same-chunk-len batched GDN prefill kernels ──
259    // Each takes `float* const* h_state_ptrs` plus stacked QKV/gate/beta/output.
260    // Used by `Qwen3SsmLayer::prefill_batched` when N≥2 streams have matching
261    // chunk_len. Null on targets that don't carry the corresponding kernel.
262    gdn_prefill_wy32_batched_k: KernelHandle,
263    gdn_prefill_persistent_batched_k: KernelHandle,
264    gdn_prefill_persistent_wy4_batched_k: KernelHandle,
265    gdn_prefill_split4_batched_k: KernelHandle,
266    compute_gdn_gates_k: KernelHandle,
267    ba_gates_prefill_k: KernelHandle,
268    /// Hopper twin of `ba_gates_prefill_k`
269    /// (`ssm_ba_gates_hopper::dense_gemm_ba_gates_prefill_hopper`, #928): one
270    /// CTA per token instead of `ceil(N/4)`, bit-identical output. Null on
271    /// every target but hopper, and declined below the token-count floor —
272    /// `ops::ba_gates_pick` owns both rules.
273    ba_gates_prefill_hopper_k: KernelHandle,
274    // Kernels — prefill (multi-token sequential)
275    conv1d_prefill_k: KernelHandle,
276    /// Token-parallel prefill conv1d (`causal_conv1d_update_prefill_tp`).
277    conv1d_prefill_tp_k: KernelHandle,
278    // Kernels — fused chunk2 path (2-token verification)
279    gdn_chunk2_k: KernelHandle,
280    conv1d_chunk2_k: KernelHandle,
281    // Kernels — fused chunk3 path (3-token verification)
282    gdn_chunk3_k: KernelHandle,
283    w4a16_gemv_batch3_k: KernelHandle,
284    // NVFP4 batched decode GEMV (multi-seq concurrency + chain verify):
285    // the narrow batch{4,5,6,7,8} family plus batch16 (M<=16) — siblings of
286    // w8a16_gemv_batch4/16 for the FP4 QKVZ + out_proj, so FP4 decode
287    // amortizes the weight read at C=4..16 like FP8.
288    w4a16_batchm: W4a16BatchmTiers,
289    w4a16_gemv_batch16_k: KernelHandle,
290    // Kernels — WY-chunkwise path (2-pass verification)
291    gdn_wy2_k: KernelHandle,
292    /// Register-resident wy2 twin (K=2 verify, the C=32 hot shape): Pass 2
293    /// is served from the Pass 1 H read retained in registers
294    /// (`__launch_bounds__(128,1)`, 128 floats/thread — the regresident
295    /// prefill pattern), cutting the kernel's HBM state traffic from 2R+2W
296    /// to 1R+2W. Byte-identical accumulation order to `gdn_wy2_k`
297    /// (bitwise-asserted by gdn_wy_verify_microtest's parity leg).
298    /// KernelHandle(0) when not linked (e.g. strix module sets). Selection +
299    /// kd/vd==128 guard + width gate (n >= wy_resident_min_width(); the
300    /// 1-block/SM kernel loses at narrow launches) live in `wy2_kernel`
301    /// (trait_decode_batched_conv_gdn);
302    /// kill switch ATLAS_NO_GDN_WY2_RESIDENT (PRESENCE — `=0` is NOT off).
303    gdn_wy2_resident_k: KernelHandle,
304    gdn_wy3_k: KernelHandle,
305    /// Register-resident wy3 twin (K=3 verify — the 16:2 ladder rung's 3
306    /// rows/seq shape, plus the 24:2/32:2 rungs of the 96-row envelope):
307    /// Pass 2 served from the Pass 1 H read retained in registers, cutting
308    /// HBM state traffic from 2R+3W to 1R+3W. Byte-identical accumulation
309    /// order to `gdn_wy3_k` (bitwise-asserted by gdn_wy_verify_microtest's
310    /// wy3 parity leg). KernelHandle(0) when not linked. Selection +
311    /// kd/vd==128 guard + width gate (n >= wy_resident_min_width()) live in
312    /// `wy3_kernel` (trait_decode_batched_conv_gdn);
313    /// kill switch ATLAS_NO_GDN_WY3_RESIDENT (PRESENCE — `=0` is NOT off).
314    gdn_wy3_resident_k: KernelHandle,
315    gdn_wy4_k: KernelHandle,
316    /// FP16 h-state twins of the five WY verify kernels above
317    /// (`ATLAS_SSM_H_FP16` stage 2). Same launch contracts, same float
318    /// expressions and accumulation orders as their FP32 parents — the h-state
319    /// and its rollback intermediates are simply `__half` in memory, with the
320    /// state rounded once per token boundary so a rollback checkpoint holds
321    /// exactly the bits the forward chain carried.
322    ///
323    /// Stage 1 narrowed only the NON-speculative decode scan, so `--speculative`
324    /// and the flag were mutually exclusive (preflight refused). These close
325    /// that: with speculation on, the WY kernels are the only GDN h-state
326    /// readers/writers in the step, so the rungs whose best config is spec-ON
327    /// could not use FP16 at all.
328    ///
329    /// KernelHandle(0) when not linked. The selectors (`wy2_kernel`,
330    /// `wy3_kernel`, and the K=4 sites) gate on `.0 != 0` and fall back to the
331    /// FP32 parent — which is why preflight must independently refuse the flag
332    /// when a reachable K has no twin, since that fallback would read an FP16
333    /// pool through an FP32 kernel and produce fluent garbage.
334    gdn_wy2_f16_k: KernelHandle,
335    gdn_wy2_resident_f16_k: KernelHandle,
336    gdn_wy3_f16_k: KernelHandle,
337    gdn_wy3_resident_f16_k: KernelHandle,
338    gdn_wy4_f16_k: KernelHandle,
339    /// Stage-3 f16-SIZED pool (`--ssm-h-dtype f16-pool`): the two h-state
340    /// width converters (`ssm_h_dtype.cu`). PREFILL uses them as a matched
341    /// pair around its FP32 kernels — widen the narrow slot into the
342    /// sequence's FP32 staging blob, run, narrow back — so unlike the
343    /// decode-side one-shot conversion these launch once per SSM layer per
344    /// prefill pass and are self-cancelling. A 0 handle is a hard error at
345    /// the first prefill, never an FP32 fallback: an FP32 kernel writing a
346    /// 2-byte-sized slot is an OOB write into the neighbouring slot.
347    ssm_h_f16_to_f32_k: KernelHandle,
348    ssm_h_f32_to_f16_k: KernelHandle,
349    /// STAGE 1 fused K=2 MTP-verify epilogue: conv1d+L2norm ×2 and
350    /// gated-RMS-norm ×2 each folded into a single launch. Dispatched only
351    /// when the `ATLAS_GDN_FUSED_VERIFY` env flag is set (default OFF); the
352    /// per-token path runs unchanged otherwise. Bit-identical (cos == 1.0).
353    gdn_verify_fused_conv_k2_k: KernelHandle,
354    gdn_verify_fused_norm_k2_k: KernelHandle,
355    /// Fused generic-K verify conv1d+L2norm (one launch for all K positions,
356    /// rollback snapshots written inline). Used by the K=17 DFlash verify arm;
357    /// default ON when present, kill-switch `ATLAS_GDN_FUSED_CONV17=0`.
358    /// NULL handle on targets lacking the .cu → per-token loop unchanged.
359    gdn_verify_fused_conv_kn_k: KernelHandle,
360    /// Batched twin (gridDim.y = n_seq) — batched spec decode. 0 when absent.
361    gdn_verify_fused_conv_kn_batched_k: KernelHandle,
362    /// Exact-verify `_snap` twins (issue #435 route (a)): the fused-norm
363    /// decode kernels with an inline per-token h-state rollback snapshot, and
364    /// the FP32-output fused verify conv. All OPTIONAL (model-shadow staged,
365    /// currently qwen3.6-27b/nvfp4 only): a 0 handle makes the exact arm fall
366    /// back to the parent kernel + `copy_d2d_async` snapshots — the same
367    /// bits, more launches.
368    gdn_f32_norm_snap_k: KernelHandle,
369    gdn_f32_strided_norm_snap_k: KernelHandle,
370    gdn_verify_fused_conv_kn_f32_k: KernelHandle,
371    /// WY-Chunkwise K=17 GDN verify (DFlash γ+1). Only present in
372    /// qwen3.6-35b-a3b's PTX module set; NULL handle for other targets,
373    /// in which case decode_batched(K=17) falls through to the sequential
374    /// per-token path.
375    gdn_wy17_k: KernelHandle,
376    /// WY-Chunkwise K∈{5..16} GDN verify (every chain-verify width between
377    /// the dedicated wy4 and the DFlash wy17; K=9..16 added 2026-08-29 for
378    /// the γ>8 window class, which previously fell to the sequential
379    /// per-token loop — the measured γ10 tax). One K-templated source
380    /// (`gated_delta_rule_wyn.cu`, gb10 common) instantiates wy5..wy16 with
381    /// the same pool-layout intermediates contract as wy17. Index = K-5;
382    /// NULL handles on targets lacking the module → sequential fallback.
383    /// Kill-switch: `ATLAS_GDN_WYN=0` (default ON).
384    gdn_wyn_k: [KernelHandle; 12],
385    /// FP16 h-state twins of the wyN family (K=5..16), stage 2 of
386    /// `ATLAS_SSM_H_FP16` — added 2026-08-29 (#812: the FP16 pool is the
387    /// lever that lets MTP serve wide batch; DFlash was refused it for
388    /// want of these twins). Same index contract (K-5); zero handles on
389    /// targets lacking the module. Under the f16 pool a missing twin is a
390    /// HARD ERROR at dispatch (never a silent FP32 fallback over FP16
391    /// state). provenance-id: 526f6e616c6420522e205374657369616b
392    gdn_wyn_f16_k: [KernelHandle; 12],
393    // State allocation sizes (pre-computed from config)
394    h_state_bytes: usize,
395    conv_state_bytes: usize,
396    // Pre-dequanted FP8 weights for zero-overhead prefill GEMMs
397    qkvz_fp8: Option<DevicePtr>,
398    out_proj_fp8: Option<DevicePtr>,
399    fp8_gemm_k: KernelHandle,
400    fp8_gemm_t_m128_k: KernelHandle, // M128: halves B re-reads for out_proj at ISL > 128
401    // Block-scaled W8A16 prefill kernels (preferred over single-scale
402    // fp8_gemm_n128 when block-scaled FP8 weights are available — matches
403    // vLLM's per-128-block scale precision instead of single-scale).
404    w8a16_gemm_k: KernelHandle,
405    // Pipelined (cp.async) rewrite of w8a16_gemm: bit-identical, ~4.6× faster.
406    // KernelHandle(0) when not linked into the image. Gated ON only when
407    // ATLAS_W8A16_PIPELINED=1 (default OFF — production dispatch unchanged).
408    w8a16_gemm_pipelined_k: KernelHandle,
409    // M<=4 weight-streaming block-scaled FP8 GEMV. Replaces the M-padded
410    // w8a16_gemm_pipelined for n<=4 batched decode (qkvz + out_proj): pipelined
411    // pads M=4 to a 128-row MMA tile (32× compute over-provision, issue-bound);
412    // this streams the weight once with 4 FP32 accumulators. Bit-identical per
413    // row to w8a16_gemv. KernelHandle(0) when not linked.
414    w8a16_gemv_batch4_k: KernelHandle,
415    // M<=16 sibling of batch4 for high-concurrency decode (n=5..16): same
416    // weight-streaming GEMV, avoids the M-padded MMA at C=8/16.
417    w8a16_gemv_batch16_k: KernelHandle,
418    w8a16_gemm_t_k: KernelHandle,
419    // W8A8 + FP32 epilogue (vLLM-equivalent) prefill kernels.
420    // `per_token_group_quant_fp8` produces FP8 activations + per-token-per-128
421    // FP32 scale; `fp8_gemm_t_blockscaled` consumes both with FP8 MMA and
422    // applies a_scale × b_scale in the FP32 epilogue. Gated behind
423    // `ATLAS_FP8_W8A8=1` for staged rollout.
424    per_token_group_quant_fp8_k: ops::Fp8ActQuant,
425    fp8_gemm_t_blockscaled_k: KernelHandle,
426    /// `fp8_act_scale_to_kmajor` — rewrites the quantizer's `[M, K/128]`
427    /// VEC128 activation scales into the `[K/128, ceil16(M)]` layout cuBLASLt
428    /// documents. 0 when the module is absent, which makes the cuBLASLt QKVZ
429    /// arm decline (see `prefill_w8a8.rs`); the in-tree kernel reads the
430    /// quantizer's own order and needs no adapter.
431    fp8_act_scale_kmajor_k: KernelHandle,
432}
433
434// Kernel-selection helpers moved to `kernel_select.rs` (≤500 LoC split).
435
436// ── Sub-files (split for ≤500 LoC) ────────────────────────────────────────
437mod debug;
438mod decode_w8a8_proj;
439pub mod gdn_flags;
440mod init;
441mod init_fp8;
442mod init_q2;
443mod kernel_select;
444mod lora;
445mod prefill_out_w8a8;
446mod prefill_w8a8;
447mod rowwise_bf16;
448mod ssm_forward;
449pub(crate) mod ssm_h_fp16;
450mod trait_decode;
451mod trait_decode_batched;
452mod trait_decode_batched_conv_gdn;
453mod trait_decode_batched_conv_gdn_exact;
454mod trait_decode_batched_conv_gdn_multi;
455mod trait_decode_batched_conv_gdn_multi_exact;
456mod trait_decode_batched_conv_gdn_wyn;
457mod trait_decode_hc;
458mod trait_decode_multi_seq;
459mod trait_layer;
460mod trait_prefill;
461mod trait_prefill_block;
462mod trait_prefill_gdn;
463mod trait_prefill_hc;
464mod trait_prefill_helper;
465mod trait_prefill_phase1;
466mod trait_prefill_phase3;
467mod trait_prefill_proj;
468mod trait_prefill_recur;
469
470pub use gdn_flags::{
471    GdnFlags, MAX_F16_TWIN_DFLASH_GAMMA, MAX_F16_TWIN_K, default_dflash_gamma,
472    gdn_fused_norm_enabled, ssm_batched_recurrent_enabled, ssm_h_dtype_bits,
473    ssm_h_f16_pool_enabled, ssm_h_fp16_enabled, verify_exact_enabled,
474};
475
476// ── TransformerLayer impl (delegates to per-file inherent _inner methods) ──
477
478#[cfg(test)]
479#[path = "prefill_alloc_tests.rs"]
480mod prefill_alloc_tests;
481#[cfg(test)]
482#[path = "rowwise_alloc_tests.rs"]
483mod rowwise_alloc_tests;
484#[cfg(test)]
485mod tests;
486
487#[path = "hc.rs"]
488mod hc;