Skip to main content

memra_engine/
lib.rs

1//! memra engine: Stage-1 correctness-first forward-pass kernels + ops, on sm_120 via cudarc.
2
3use cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES;
4use cudarc::driver::{
5    CudaContext, CudaFunction, CudaModule, CudaSlice, CudaStream, DeviceSlice, LaunchConfig,
6    PushKernelArg,
7};
8use cudarc::nvrtc::Ptx;
9use std::sync::{Arc, Mutex};
10
11const GDN_K2_DYNAMIC_SHARED_BYTES: u32 = 67_072;
12
13/// The default dynamic-shared-memory launch bound the naive SDPA family lives under: past
14/// `T_kv * 4 > 48KB` (T_kv > 12288) the smem kernel cannot launch — the measured
15/// dspark/full-attn long-ctx crash class. `sdpa_naive` dispatches to the byte-identical
16/// gmem-scores twin above this line.
17const SDPA_NAIVE_SMEM_MAX: usize = 48 * 1024;
18
19/// Guard on the gmem twin's `n_head * T * T_kv * 4`-byte scores workspace. The shapes that
20/// legitimately hit the smem bound are tall-KV blocks (T <= draft block size), which land in
21/// the tens of MB; 1 GiB refuses a square T==T_kv misuse before it silently eats the card.
22const SDPA_NAIVE_GMEM_WS_MAX: usize = 1 << 30;
23
24#[cfg(debug_assertions)]
25pub(crate) fn debug_assert_tensor_stream_device<T>(
26    tensor: &CudaSlice<T>,
27    stream: &CudaStream,
28    site: &str,
29) {
30    let tensor_dev = tensor.ordinal();
31    let stream_dev = stream.context().ordinal();
32    assert_eq!(
33        tensor_dev, stream_dev,
34        "PP cross-device tensor read at {site}: tensor on dev{tensor_dev}, stream on dev{stream_dev}"
35    );
36}
37
38fn ensure_tensor_stream_device<T>(
39    tensor: &impl DeviceSlice<T>,
40    stream: &CudaStream,
41    site: &str,
42) -> Result<(), Box<dyn std::error::Error>> {
43    let tensor_dev = tensor.stream().context().ordinal();
44    let stream_dev = stream.context().ordinal();
45    if tensor_dev != stream_dev {
46        return Err(format!(
47            "PP cross-device tensor access at {site}: tensor on dev{tensor_dev}, \
48             stream on dev{stream_dev}"
49        )
50        .into());
51    }
52    Ok(())
53}
54
55pub use memra_gguf;
56pub use memra_runtime;
57
58pub mod forward;
59pub mod hybrid;
60pub mod hybrid_forward;
61pub mod model;
62pub mod sigrouter_contract;
63pub mod vision;
64pub mod vision_gemma;
65pub mod vision_pre;
66pub mod vision_step;
67/// The dual cache lives in the shared `memra-kv` crate (Phase D extraction); this
68/// re-export keeps every `crate::cache::` / `memra_engine::cache::` path unchanged.
69pub mod cache {
70    pub use memra_kv::*;
71}
72pub mod decode;
73pub mod decode_batch;
74pub mod dflash;
75pub mod eagle;
76pub mod gemma_spec;
77pub mod graph_update;
78/// MLA (multi-head latent attention) CPU f32 reference — GLM-5.2 bring-up lane increment 1.
79/// Naive vs absorbed decode forms + NORM/NEOX rope permutation, unit-tested; the permanent
80/// oracle for the MLA kernel family (`research/mla-bringup-20260801/DESIGN.md`). No CUDA deps.
81pub mod mla;
82pub mod moesd;
83pub mod parallel;
84pub mod plan_backend;
85pub mod pp;
86pub mod round_stream;
87pub mod spec;
88pub mod tp;
89pub use memra_sampling as sampler;
90
91/// In-house MoE router GEMV on the spec-verify small-t path (DEFAULT ON since 2026-07-10:
92/// battery green on 35B p2/p3 K=1..8, acceptance bit-identical, +2-4% spec e2e — replaces
93/// ~240 per-column cuBLAS gemv launches/round). MEMRA_ROUTER_KERNEL=0 is the rollback seam.
94/// MoE grouped f16 GEMM door (experimental until gated), f16-mirror numeric class:
95/// per-layer expert dequant to f16 + one grouped f16 GEMM over the CSR groups.
96///   MEMRA_MOE_F16G=1  cublasGemmGroupedBatchedEx (round 46 arc 2). The grouped API issues
97///                     through cublas-internal streams NOT ordered with ours — v1 pays a full
98///                     stream sync per projection (round-47 ledgered defect).
99///   MEMRA_MOE_F16G=2  single-kernel grouped GEMM on the engine stream (round 49): ordered by
100///                     construction, zero syncs, f32 C with the act row-scale folded in.
101/// DEFAULT (2026-08-01, round 49 promotion): mode 1 on the Hopper lane — with the 41/41
102/// dequant coverage fix the q35 board-2048 prime measured 5490 (MMQ) / 8380 (mode 1,
103/// +53%) / 7990 (mode 2) x3 interleaved on the H100, argmax MATCH — the last board loss
104/// flips. The 5090 measured FLAT (858GB/s makes the dequant-workspace traffic cancel the
105/// GEMM win) — but that verdict is for expert banks the int8-MMA MMQ arm can take
106/// (IQ3_S/IQ4_XS/Q4_0). MEMRA_MOE_F16G=0 kills anywhere.
107///
108/// HOPPER RE-VERDICT (2026-08-02, lane/h100-flip-full): mode 2 with full direct coverage
109/// (Q4_K/Q6_K/IQ4_XS/IQ3_S tile loaders, lane/iq-direct-loaders) + the deep tail
110/// (lane/sk-tail-form) FLIPS past cublas mode 1 on the H100 — q35 board-2048 prime
111/// 13163.6 (mode 2, cross=32) vs 8626.5 (mode 1) vs 8073.4 (round-51 sk form), +52.6%,
112/// interleaved x5 zero overlap, argmax MATCH 30/30. The round-54 NO-FLIP (8547 vs 8112)
113/// was coverage-priced at 5.2% direct; ~100% coverage kills the workspace pass and the
114/// verdict inverts. Hopper naked default -> mode 2 (this arm); the gemma (gelu) site
115/// stays env-explicit-only via moe_f16g_gemma_on (Err => closed, unaffected by this arm).
116///
117/// MODE-2 DEFAULT (sm_120a naked, 2026-08-02, lane/f16g-default-rearb): with the direct
118/// tile loaders covering Q4_K/Q6_K/IQ4_XS/IQ3_S, the sk visitor beats the int8-MMA MMQ
119/// tiles on the IQ-bank models too (q35 board-2048 +33.9%, KAT pp512 +46.7% / pp2048
120/// +30.6% — research/iq-direct-loaders-20260802 §3-5, confirmed + full battery in
121/// research/f16g-default-rearb-20260802/), so every f16g-admitted expert layer rides
122/// mode 2 naked. Decode/verify stay on dp4a (t >= 16 floor). f16-mirror numeric class
123/// for naked q35/KAT prefill+prime — new token-sha anchors stamped in the rearb lane.
124///
125/// AUTO-KQUANT (mode 3, 2026-08-02, lane/q4k-expert-prefill): the previous sm_120a
126/// default, kept reachable via MEMRA_MOE_F16G=3. The mode-2 sk form is admitted ONLY for
127/// layers the MMA MMQ arm rejects (k-quant expert projections — Q3_K/Q4_K/Q6_K), i.e.
128/// exactly where the baseline is the per-pair moe_pairs_matvec_q8_em fallback with zero
129/// token reuse (Ornith-35B Q4_K_M board-2048 1098.2 -> 3453.7, 3.14x,
130/// research/q4k-expert-prefill-20260802/). Its "IQ banks keep their measured-faster MMQ
131/// tiles" ruling was priced BEFORE the IQ direct loaders and is refuted on the 5090 —
132/// the k-quant-only admission survives as the rollback seam, not the default.
133/// The gemma (gelu) site stays env-explicit-only (moe_f16g_gemma_on).
134pub fn moe_f16g_mode() -> u8 {
135    static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
136    *M.get_or_init(|| match std::env::var("MEMRA_MOE_F16G").as_deref() {
137        Ok("0") => 0,
138        Ok("2") => 2,
139        Ok("3") => 3,
140        Ok(_) => 1,
141        // Both arches independently re-arbitrated to mode 2 on 2026-08-02
142        // (5090: lane/f16g-default-rearb; H100: lane/h100-flip-full) — unset = 2 everywhere.
143        Err(_) => 2,
144    })
145}
146/// Mode-2 sk kernel form policy (round 51, lane/sk-bm128): the single-kernel grouped GEMM runs
147/// as a persistent problem-visitor over the real CSR tiles with two tile forms. Returns
148/// (shape_sel, cross) for the FFI:
149///   MEMRA_F16G_SK=0    -> (-1, _): the round-49 grid-scan kernel (rollback seam).
150///   MEMRA_F16G_SK=32   -> all groups on the 32x64x32 2-stage form (cross = i32::MAX).
151///   MEMRA_F16G_SK=128  -> all groups on the 128x64x64 3-stage form (cross = 1; groups fall
152///                         back to 32x64 in-launcher when the device/in_f can't take it).
153///   unset              -> hybrid split: groups with m_e >= MEMRA_F16G_SK_CROSS ride the 128
154///                         form. Default cross = 64 (5090 sweep 2026-08-01, receipts
155///                         research/sk-bm128-20260801/; H100 re-swept on the direct+tail
156///                         form 2026-08-02, lane/h100-flip-full: {16,32,64} ->
157///                         12868/13192/13225 — 64 wins there too, the pre-direct 32
158///                         verdict was stale).
159pub fn moe_f16g_sk_params() -> (i32, i32) {
160    static P: std::sync::OnceLock<(i32, i32)> = std::sync::OnceLock::new();
161    *P.get_or_init(|| match std::env::var("MEMRA_F16G_SK").as_deref() {
162        Ok("0") => (-1, 0),
163        Ok("32") => (0, i32::MAX),
164        Ok("128") => (0, 1),
165        _ => {
166            let cross = std::env::var("MEMRA_F16G_SK_CROSS")
167                .ok()
168                .and_then(|v| v.parse().ok())
169                .unwrap_or(64);
170            (0, cross)
171        }
172    })
173}
174/// DIRECT-FROM-QUANT sk tile loaders (lane/kquant-tile-loaders, 2026-08-02; IQ classes added
175/// by lane/iq-direct-loaders): Q4_K/Q6_K/IQ4_XS/IQ3_S expert projections on the mode-2/3 sk
176/// visitor forms dequant their weight tiles in-register from the quant superblocks instead of
177/// running the per-(layer,projection) dequant pass into an f16 workspace (41.8% of Ornith-35B
178/// t=512 kernel time — the pp512 wall, research/q4k-expert-prefill-20260802 §5; the IQ classes
179/// are 94.8% of q35's bank bytes — the h100-sk-direct coverage pricing). Bit-identical to the
180/// workspace path by construction (kernel-check "f16g-kq-direct" gates it bitwise) — a
181/// data-movement change, not a numeric-class change. Default ON; MEMRA_F16G_DIRECT=0 reverts
182/// to the workspace path everywhere; MEMRA_F16G_DIRECT=kq keeps the k-quant loaders and
183/// reverts only the IQ classes (the iq-direct-loaders A/B seam — the pre-lane shipped config).
184pub fn moe_f16g_direct_on(qtype: i32) -> bool {
185    static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
186    let m = *M.get_or_init(|| match std::env::var("MEMRA_F16G_DIRECT").as_deref() {
187        Ok("0") => 0,
188        Ok("kq") => 1,
189        _ => 2,
190    });
191    match m {
192        0 => false,
193        1 => qtype == QT_Q4_K || qtype == QT_Q6_K,
194        _ => true,
195    }
196}
197/// DEEP-TAIL sk form (lane/sk-tail-form, 2026-08-02): groups below the visitor crossover ride
198/// a 32x64x64 3-STAGE cp.async tile instead of the round-51 32x64x32 2-stage — the same 32-row
199/// tile (zero extra padding), 2 k-blocks in flight instead of 1 and half the syncs per k. The
200/// H100 ncu pricing (research/sk-bm128-20260801) put the 2-stage tail at 31% of the sk GEMM
201/// stage under q35's routing skew. Bit-identical to every other sk form by construction
202/// (kernel-check "f16g-sk" gates all tail arms maxdiff==0); exists in both the workspace-f16
203/// and direct-from-quant variants. Default ON; MEMRA_F16G_TAIL=0 = rollback to the 2-stage
204/// tail. in_f % 64 != 0 falls back in-launcher.
205pub fn moe_f16g_tail_on() -> bool {
206    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
207    *ON.get_or_init(|| std::env::var("MEMRA_F16G_TAIL").as_deref() != Ok("0"))
208}
209
210/// Per-model door for the gemma-MoE (gelu) grouped path: round 49's Hopper default
211/// REGRESSED g26 board-2048 prefill -8.3% interleaved x5 on-box (def median 10380,
212/// wild 8.9k-11.7k spread; off 11317, ±0.13%) — the +6-15% probe verdict didn't
213/// survive the board workload (stale-verdict law, round 50). The silu/qwen class
214/// keeps the round-49 default (q35 +53% board-2048). Explicit MEMRA_MOE_F16G=1/2
215/// still opens this door for A/B.
216pub fn moe_f16g_gemma_on() -> bool {
217    static M: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
218    *M.get_or_init(|| !matches!(std::env::var("MEMRA_MOE_F16G").as_deref(), Ok("0") | Err(_)))
219}
220
221/// Fused act-epilogue (silu/gelu-mul + q8_1_mmq quantize in one launch) for the MoE prefill
222/// MMA arms. Byte-identical to the two-pass path (kernel-check gated) — default ON.
223/// MEMRA_MOE_FUSE_ACTQ=0 is the rollback/A-B seam.
224pub fn moe_fuse_actq_on() -> bool {
225    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
226    *ON.get_or_init(|| std::env::var("MEMRA_MOE_FUSE_ACTQ").as_deref() != Ok("0"))
227}
228
229/// PREFILL router m-invariance (lane/concat-prime-exact, 2026-08-02). The batched cuBLASLt
230/// router GEMM changes a row's logits when OTHER rows join the call (probed: first change at
231/// m=65 on the Ornith-35B router, 3.9e-3 — while the MMQ/f16 trunk GEMMs are bit-identical
232/// across m). Feeding a top-k discontinuity, that made a served request's expert selection a
233/// function of its CO-ARRIVALS under cross-request prime batching. The in-house router GEMV
234/// is m-invariant, so prefill uses it too and routing depends on a session's own tokens only.
235/// DEFAULT ON: it is the serving isolation contract, and it is the same kernel decode and spec
236/// verify already use (dispatch parity, one router kernel for every t).
237/// MEMRA_ROUTER_PREFILL_EXACT=0 reverts to the batched GEMM.
238pub fn router_prefill_exact_on() -> bool {
239    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
240    *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_PREFILL_EXACT").as_deref() != Ok("0"))
241}
242
243pub fn router_kernel_on() -> bool {
244    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
245    *ON.get_or_init(|| {
246        let on = std::env::var("MEMRA_ROUTER_KERNEL").as_deref() != Ok("0");
247        if !on {
248            eprintln!("[memra] router kernel OFF (rollback: per-column cuBLAS gemv)");
249        }
250        on
251    })
252}
253
254/// FAST-ROUTER batch twin (lane/fast-router, 2026-08-02). The concat-prime exactness fix
255/// (router_prefill_exact_on) routes prefill through router_gemv — m-invariant, but a
256/// per-(expert,token) GEMV program with zero operand reuse, so q35 board-2048 prefill paid
257/// -10% on the 5090. router_gemv_f32_w8_batch register-tiles (8x8 expert-x-token) the same
258/// per-row FP chains (BIT-IDENTICAL per row — kernel-check sweeps m=1..2048 on real router
259/// weights), so the t crossover below is pure perf, not a numeric config. Swept on-box
260/// (research/fast-router-20260802/crossover-router*.jsonl): plain wins t<=4, batch +7-9%
261/// at t=8, 1.9x at t=16 rising to 3.45x at t=2048 — MIN_T=8. Decode t=1 and spec verify
262/// t<8 keep the plain w8 form. MEMRA_ROUTER_BATCH=0 forces plain at every t (rollback
263/// seam, perf-only: bits are equal by the kernel-check gate).
264/// Killed arms (same sweep, JSONL is the record): the 8x16 tile lost to 8x8 at every t
265/// (128-accumulator register pressure beats the halved w-traffic), and the same-shape
266/// sigmoid_dot_rows twin (out_f=1) measured 0.62-0.89x at every prefill t
267/// (launch-latency-bound, ~7us/layer at m=2048) — both bit-identity-PASSED before dying.
268pub const ROUTER_BATCH_MIN_T: usize = 8;
269pub fn router_batch_on() -> bool {
270    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
271    *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_BATCH").as_deref() != Ok("0"))
272}
273mod cpu_experts;
274#[cfg(memra_cutlass)]
275pub mod cutlass_ffi;
276pub mod dsv4_ffi;
277pub mod dsv4_gpu;
278pub mod f16_ffi;
279pub mod fp8_ffi;
280pub mod mmq_ffi;
281pub mod moe_cache;
282pub mod prime_graph;
283pub mod spill;
284mod spill_pread;
285
286// Fatbins are EMBEDDED (crates-release lane, 2026-08-04): build.rs still writes them to
287// OUT_DIR, but the bytes ship inside the binary via include_bytes! and load through
288// cuModuleLoadData. Distribution contract: a prebuilt or cargo-installed binary must be
289// self-contained — the old baked OUT_DIR *paths* pointed at the builder's temp dir and
290// broke every machine that wasn't the build machine. Same bytes, same module image;
291// the runtime MEMRA_GEMM_FATBIN tune-seam override below is preserved.
292const FATBIN: &[u8] = include_bytes!(env!("MEMRA_ENGINE_FATBIN"));
293const HYBRID_FATBIN: &[u8] = include_bytes!(env!("MEMRA_HYBRID_FATBIN"));
294const QMATVEC_FATBIN: &[u8] = include_bytes!(env!("MEMRA_QMATVEC_FATBIN"));
295const FLASH_FATBIN: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN"));
296const GEMM_FATBIN: &[u8] = include_bytes!(env!("MEMRA_GEMM_FATBIN"));
297const ROUTER_FATBIN: &[u8] = include_bytes!(env!("MEMRA_ROUTER_FATBIN"));
298/// spec_sample.cu: sampled-spec primitives (Philox Gumbel-max / softmax gather / residual sampler).
299const SAMPLE_FATBIN: &[u8] = include_bytes!(env!("MEMRA_SAMPLE_FATBIN"));
300
301/// TUNE SEAM (tools/sweep): a RUNTIME `MEMRA_GEMM_FATBIN=<path>` overrides the baked-in
302/// qmatvec_gemm.cu fatbin path (build.rs bakes the same name at COMPILE time via
303/// cargo:rustc-env — that constant is the default). Lets the sweep harness swap in a
304/// `-D`-tuned fatbin per process with NO rust rebuild. Unset at runtime => the
305/// compile-time default (zero behavior change).
306fn gemm_fatbin_bytes() -> std::borrow::Cow<'static, [u8]> {
307    assert!(
308        !(portable_mma_gated() && std::env::var_os("MEMRA_GEMM_FATBIN").is_some()),
309        "MEMRA_GEMM_FATBIN overrides are not allowed in the portable CUDA lane"
310    );
311    match std::env::var("MEMRA_GEMM_FATBIN") {
312        Ok(path) => std::borrow::Cow::Owned(
313            std::fs::read(&path).unwrap_or_else(|e| panic!("MEMRA_GEMM_FATBIN read {path}: {e}")),
314        ),
315        Err(_) => std::borrow::Cow::Borrowed(GEMM_FATBIN),
316    }
317}
318
319/// Phase A (ARCHITECTURE-H100.md): sm_90a re-enables the portable-PTX tensor-core paths
320/// (int8 mma.m16n8k32/k16.s8, bf16 m16n8k16, ldmatrix, cp.async — all sm_80-class, native
321/// on Hopper) that the portable boot lane gates off. Dispatch guards that used to test
322/// `cfg!(memra_portable_cuda)` test this instead; sm_89 keeps the pure-portable behavior.
323/// The sm_120a/sm_100a-only MMA kinds (mxf4nvf4, kind::f8f6f4) are NOT covered — their
324/// launchers stay fail-closed stubs on 90a and their dispatch arms stay arch-gated.
325pub(crate) const fn portable_mma_gated() -> bool {
326    cfg!(memra_portable_cuda) && !cfg!(memra_hopper_mma)
327}
328
329/// Refuse an env force that would reach a kernel THIS BUILD DOES NOT CONTAIN.
330///
331/// Doors of the shape `MEMRA_X=1 => true` are arch-blind: they were written so an operator could
332/// force a promoted path on, and the default arm (`cfg!(memra_hopper_mma)` or similar) is the only
333/// thing that consulted the arch. On a portable build the forced path then reaches
334/// `Engine::func`, which resolves lazily and ends in `panic!("kernel {name} not in any fatbin")` —
335/// a confusing crash naming a kernel the operator never heard of, several frames from the switch
336/// they actually flipped.
337///
338/// Found 2026-08-23 by tools/fatbin-lookup-census.py, which listed 20 looked-up kernels absent
339/// from the sm_89 fatbins. 18 of those turned out to be correctly unreachable (the GDN varlen
340/// chain is gated through `gdn_mma_enabled`, which starts with `!portable_mma_gated()`); these
341/// env doors were the two that were genuinely reachable, and only by explicit operator action.
342///
343/// Same shape and same message style as `gemm_fatbin_bytes`'s refusal above — one idiom for
344/// "this switch cannot work on this build", so it fails at the switch instead of at the lookup.
345#[track_caller]
346pub(crate) fn refuse_portable_force(var: &str, needs: &str) {
347    assert!(
348        !portable_mma_gated(),
349        "{var} forces a kernel path this build does not contain: it needs {needs}, and this is a \
350         portable-CUDA build (sm_89). Unset {var} — the default path serves this arch."
351    );
352}
353
354/// The GDN K4/K5 mma pair's UNSET-env default — ONE definition for the three read sites
355/// (gdn_mma_enabled, the k123 pre-work, gdn_scan_chunked's dispatch). They read the env
356/// per call ON PURPOSE (kernel-check toggles it to pin both configs), so the shared part
357/// is this compile-time constant: ON for Hopper-MMA builds (the original 90a promotion)
358/// and for sm_120a builds (lane/moeprime-nvfp4-direct, 2026-08-21 — measured on one RTX
359/// PRO 6000 ornith15 pp14715 +6-8% and the local 5090 q38-27b +1-2%, both orders both
360/// rigs). A site defaulting differently from its peers arms the mma pre-work while the
361/// scan takes the scalar route — measured as a 0.8% LOSS, the drift this helper kills.
362pub(crate) const fn gdn_mma_default_on() -> bool {
363    cfg!(memra_hopper_mma) || konst_eq(env!("MEMRA_BUILT_CUDA_ARCH"), "120a")
364}
365
366/// const str-eq (std `==` on &str is not const-stable on this toolchain floor).
367const fn konst_eq(a: &str, b: &str) -> bool {
368    let (a, b) = (a.as_bytes(), b.as_bytes());
369    if a.len() != b.len() {
370        return false;
371    }
372    let mut i = 0;
373    while i < a.len() {
374        if a[i] != b[i] {
375            return false;
376        }
377        i += 1;
378    }
379    true
380}
381
382/// The legacy quantized prefill GEMMs are tuned and validated for sm_120a; sm_90a re-admits
383/// them through the Hopper-MMA lane (int8 m16n8k32.s8 is sm_80-class PTX).  Keep the policy
384/// in a pure helper so the dispatch guard can be regression-tested without constructing an
385/// Engine or allocating a GPU tensor.
386const fn legacy_quant_gemm_allowed(portable_cuda: bool, hopper_mma: bool, no_gemm: bool) -> bool {
387    (!portable_cuda || hopper_mma) && !no_gemm
388}
389
390// ---- KV-cache format selection (kvbytes lane, 2026-07-08; default OFF = daily config) ----
391// `MEMRA_KV_K` = q8_0 (default, 34 B/32elem) | fp8 (raw e4m3, 32 B — the -6% K-bytes arm)
392// `MEMRA_KV_V` = q5_1 (default, 24 B/32elem) | q4_0 (18 B, -25% V bytes) | fp8 (32 B, +33%)
393// A non-default format is a NEW NUMERIC CONFIG: its own run-gen argmax baseline is legal,
394// but the gate battery (kernel-check, run-spec self-consistency) must pass WITHIN it and
395// the choice is explicit env, never silent. flash_attn.cu is compiled once per format pair
396// (build.rs); the kernels keep their names — Engine::new just loads the matching fatbin.
397const FLASH_FATBIN_VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VQ4"));
398const FLASH_FATBIN_VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VF8"));
399const FLASH_FATBIN_KF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8"));
400const FLASH_FATBIN_KF8VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VQ4"));
401const FLASH_FATBIN_KF8VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VF8"));
402
403/// KV format policy moved to the shared `memra-kv` crate (Phase D); re-exported so the
404/// fatbin router below and every existing `crate::kv_blk_bytes()` call site is unchanged.
405pub use memra_kv::{kv_blk_bytes, kv_cache_formats};
406
407/// The flash_attn fatbin matching the selected KV formats.
408fn flash_fatbin_bytes() -> &'static [u8] {
409    match kv_cache_formats() {
410        ("q8_0", "q5_1") => FLASH_FATBIN,
411        ("q8_0", "q4_0") => FLASH_FATBIN_VQ4,
412        ("q8_0", "fp8") => FLASH_FATBIN_VF8,
413        ("fp8", "q5_1") => FLASH_FATBIN_KF8,
414        ("fp8", "q4_0") => FLASH_FATBIN_KF8VQ4,
415        ("fp8", "fp8") => FLASH_FATBIN_KF8VF8,
416        other => unreachable!("kv_cache_formats returned {other:?}"),
417    }
418}
419
420/// TUNE SEAM (tools/sweep): kernel1 (Q8_0/Q4_K/Q5_K) launch-tile override,
421/// `MEMRA_GEMM_K1_LAUNCH="BM,BN,NWARP"`. MUST match the `-D K1_BM/K1_BN/NWARP` the swept
422/// fatbin was compiled with (the .cu tile and the host launch grid/block have to agree —
423/// the hardcoded (128,128,8) in qmatvec_gemm/qmatvec_gemm_raw is the shipped default).
424/// Kernel2 (Q6_K/NVFP4) launch is untouched. Unset or malformed => None => shipped
425/// defaults (zero behavior change).
426fn k1_launch_override() -> Option<(u32, u32, u32)> {
427    static K1: std::sync::OnceLock<Option<(u32, u32, u32)>> = std::sync::OnceLock::new();
428    *K1.get_or_init(|| {
429        let v = std::env::var("MEMRA_GEMM_K1_LAUNCH").ok()?;
430        let p: Vec<u32> = v.split(',').filter_map(|s| s.trim().parse().ok()).collect();
431        match p.as_slice() {
432            [bm, bn, w] => Some((*bm, *bn, *w)),
433            _ => None,
434        }
435    })
436}
437
438/// H100 wgmma prefill-GEMM seam (task 8, ARCHITECTURE-H100.md): OPT-IN (MEMRA_WGMMA=1).
439/// v0 verdict (2026-07-26, N=5 pp512 9B-Q8_0): wgmma 3845 tok/s vs MMQ 8692 — the
440/// standalone harness's "688us MMQ ref" was a pp2048-shape figure, so v0 (unpipelined,
441/// 64x64 tile, wait_group<0> every 32-K step) is ~3x SLOWER per launch at m=512 model
442/// shapes. Default stays MMQ until the pipelined version beats it N=5 (repo law).
443/// Correctness stays pinned regardless: kernel-check's wgmma case is cfg-gated, not env-gated.
444pub(crate) fn wgmma_gemm_enabled() -> bool {
445    static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
446    *V.get_or_init(|| std::env::var("MEMRA_WGMMA").as_deref() == Ok("1"))
447}
448
449/// TUNE SEAM: keys per FA-decode split (`MEMRA_FA_SPLIT` forces a fixed size; default 64). Smaller
450/// splits raise grid.y so grid = n_head_kv * n_splits fills the 82 SMs at short/mid ctx (vec path
451/// launches only n_head_kv=8 CTAs per split). Swept clock-locked 2026-07-03 (graph tg128): 32 beat
452/// 64 at ctx 128/512 (+0.5/+1.2%) and lost at 2048 (-3%) — BUT the adaptive 32/64 default BROKE the
453/// MTP spec-decode exact-match gate (run-spec K=1/2 self-consistency FAIL with 32; PASS with 64):
454/// the split count changes the combine's FP summation order, and the spec verify's batched forward
455/// only argmax-matches single-step decode under the 64-split order on real prompts. Spec exactness
456/// (the bigger lever) outranks a <=1.2% decode win -> default stays FIXED 64; sweeps use the env.
457/// Takes t_kv so eager, _dc capture, and fa_geom_eager stay signature-compatible for future
458/// adaptive retries (any retry MUST pass run-spec self-consistency first).
459/// Minimum t_kv for the warp-per-token vec FA path (below it the scalar path's 4x-more-blocks
460/// hides latency better — measured crossover, see `fa_decode`). Shared by fa_decode / fa_decode_dc /
461/// fa_geom_eager / fa_decode_rows-eligibility (spec verify) so the kernel pick NEVER diverges
462/// between eager decode and the verify (the spec-exactness law).
463pub const FA_VEC_MIN_TKV: usize = 96;
464/// Env-overridable crossover (MEMRA_FA_VEC_MIN, default FA_VEC_MIN_TKV). The 96 floor was
465/// measured on the qwen geometry (nkv=2); gemma4 SWA layers run nkv=8 = 4x the vec grid,
466/// which moves the crossover — sweep per model, adopt per the battery.
467pub fn fa_vec_min_tkv() -> usize {
468    static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
469    *V.get_or_init(|| {
470        std::env::var("MEMRA_FA_VEC_MIN")
471            .ok()
472            .and_then(|v| v.parse().ok())
473            .unwrap_or_else(|| FA_VEC_MIN_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
474    })
475}
476
477/// f16-P/V class (DEFAULT since 2026-07-23 stamp v4; MEMRA_FA_F16PV=0 = f32-class rollback):
478/// llama-fa=1-style f16 P + f16 P@V accumulation on the hd512/SWA prefill stamps
479/// (KQ/softmax/normalize stay f32). Laptop stamp: 12B 1.045x, 31B 0.979x vs llama.
480///
481/// SPEC-SERVING FLIP (2026-07-26, the wkv acceptance-law pattern): with MEMRA_DRAFT set the
482/// default is OFF. f16 P/V shifts the PRIME's hidden states/KV in the sub-argmax logit
483/// space the drafter feeds on — argmax gates stay MATCH while depth acceptance falls off a
484/// cliff (26B d1736 0.883 -> 0.405, -40% e2e; f16pv-off alone restores 0.846/314 tok/s —
485/// the perf-ci acceptance battery is the only gate that sees this class). Explicit
486/// MEMRA_FA_F16PV always wins; plain serving keeps the f16 prefill win.
487pub fn fa_f16pv_on() -> bool {
488    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
489    *ON.get_or_init(|| {
490        std::env::var("MEMRA_FA_F16PV")
491            .map(|v| v != "0")
492            .unwrap_or_else(|_| std::env::var("MEMRA_DRAFT").is_err())
493    })
494}
495
496/// hd512 head-pair arm (DEFAULT since stamp v4; MEMRA_FA512_HP=0 reverts to sp16): GQA
497/// ncols2=2 — 2 heads per CTA share each staged K/V tile, Q register-resident. Engages
498/// when n_head is even and the GQA group (n_head/n_head_kv) is even.
499pub fn fa512_hp_on() -> bool {
500    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
501    *ON.get_or_init(|| std::env::var("MEMRA_FA512_HP").as_deref() != Ok("0"))
502}
503
504/// SWA head-pair arm (DEFAULT since stamp v4; MEMRA_FAW_HP=0 reverts to p1): llama-class
505/// windowed geometry — 32 q-rows x 2 heads per CTA sharing staged K/V, f16 P@V
506/// accumulation. Even n_head and even GQA group required (guarded per call).
507pub fn faw_hp_on() -> bool {
508    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
509    *ON.get_or_init(|| std::env::var("MEMRA_FAW_HP").as_deref() != Ok("0"))
510}
511
512/// 4-warp sp16 experiment arm (MEMRA_FA512_W4=1, requires the f16pv door): GEMM0 split-K
513/// 4-way + GEMM1 4x128 O-dims. Own partial-sum order — oracle-band gated. Returns warp
514/// count (2 = base sp16). 8-warp arm measured NEGATIVE 2026-07-23 (jsonl) and removed.
515pub fn fa512_wide_warps() -> usize {
516    static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
517    *N.get_or_init(|| match std::env::var("MEMRA_FA512_W4").as_deref() {
518        Ok("1") => 4,
519        _ => 2,
520    })
521}
522
523/// hd-512 vec crossover floor (MEMRA_FA512_MIN, default 512) — shared by fa_decode dispatch
524/// and the gemma global-layer rows/parity call sites.
525pub fn fa512_min_tkv() -> usize {
526    static FA512_MIN: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
527    *FA512_MIN.get_or_init(|| {
528        std::env::var("MEMRA_FA512_MIN")
529            .ok()
530            .and_then(|v| v.parse().ok())
531            .unwrap_or(512)
532    })
533}
534/// Per-model crossover default, set at model load BEFORE the first decode (per-model
535/// numeric-config adoption law). qwen keeps the measured 96; gemma4 (nkv=8 SWA) measured
536/// vec-always fastest: 119.9 (96) / 130.0 (48) / 133.2 (1) tok/s tg128-regime, 2026-07-10.
537pub static FA_VEC_MIN_DEFAULT: std::sync::atomic::AtomicUsize =
538    std::sync::atomic::AtomicUsize::new(FA_VEC_MIN_TKV);
539/// Per-model windowed-split default (MEMRA_FA_SPW overrides): gemma MoE (26B, nkv=8) measured
540/// 32 (grid-limited t=1 under the raw-e4m3 sV ceiling, 2026-07-12); dense gemma (31B)
541/// measured 64 (37.13/37.12 vs 36.87/36.86 at 1.7k, N=2 — different attention geometry).
542pub static FA_SPW_DEFAULT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(32);
543/// Per-model hd512 (gemma globals) split default (MEMRA_FA_SP512 overrides): 26B measured 16
544/// (2026-07-11 N=2), dense 31B measured 32 (36.86/36.93 vs 36.73/36.73 at 1.7k, 2026-07-12).
545/// fused t=1 q4_0 pair/triple row mapping: true = mr1 (one row/warp). Per-model default
546/// (dense gemma wins +1.1% short / +0.6% depth on the 31B; MoE 26B REGRESSES −1.2% —
547/// its shared-expert fused2 shapes lose to the finer grid). MEMRA_Q40_MR env still wins.
548pub static FUSED_MR1_DEFAULT: std::sync::atomic::AtomicBool =
549    std::sync::atomic::AtomicBool::new(false);
550/// Per-model router-GEMV form (2026-07-31): the 8-warp twin is +8.8% on the H100 q35
551/// decode step (router was 14.8% of it) with argmax + spec self-consistency green on
552/// qwen-class MoE both rigs. The gemma-4 26B knife-edge block (2026-07-31, single
553/// synthetic prompt) was RE-ARBITRATED 2026-08-01 on 6 real prompts — gate outcomes
554/// identical to the lone-warp arm, +13% g26 decode — so gemma4 rides the default too
555/// (research/g26-decode-20260801/). MEMRA_ROUTER_V2 env overrides either way.
556pub static ROUTER_W8_DEFAULT: std::sync::atomic::AtomicBool =
557    std::sync::atomic::AtomicBool::new(true);
558pub static FA_SP512_DEFAULT: std::sync::atomic::AtomicUsize =
559    std::sync::atomic::AtomicUsize::new(16);
560/// Per-model rms_norm block size (per-model numeric-config law: the per-thread partial-sum
561/// split changes with blockDim -> different FP order -> battery-arbitrated per model).
562/// qwen keeps the shipped 256; gemma4 adopts 1024 (single-row 2816-col norms are one-block
563/// latency-bound at 256 threads — 7us/launch measured).
564pub static RMS_BLOCK_DEFAULT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(256);
565/// gemma4 fa split ladder switch (set at model load; see fa_split_keys).
566pub static FA_SP_GEMMA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
567/// Per-model stream-k entry override for SPEC serving (-1 = unset → env/default;
568/// 0 = force tiling; 1 = admit the deterministic form selector). The former timing
569/// selector made identical boots choose different fold orders; `MEMRA_MMQ_SK_FORM` is the
570/// explicit numerical-form seam. mmq_ffi reads this before the env.
571pub static MMQ_SK_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
572/// Per-model FP8-KV door — lives in memra-kv next to the format policy it drives
573/// (re-export keeps `crate::KV_FP8_FORCE` setters in model.rs/hybrid.rs working).
574pub use memra_kv::KV_FP8_FORCE;
575/// bf16 matvec family block size (MEMRA_MMV_BLOCK, default 128, clamped to [64, 256] and a
576/// multiple of 32 — the f32acc twin's shared reduce caps at 256). NUMERIC-CLASS knob: the
577/// per-thread stride and reduction order change with the block, same acceptance class as
578/// MEMRA_RMS_BLOCK (fresh-tape identity + battery at the pinned value).
579pub(crate) fn mmv_block() -> u32 {
580    static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
581    *V.get_or_init(|| {
582        std::env::var("MEMRA_MMV_BLOCK")
583            .ok()
584            .and_then(|v| v.parse().ok())
585            .filter(|&b: &u32| (64..=256).contains(&b) && b % 32 == 0)
586            .unwrap_or(128)
587    })
588}
589
590/// MEMRA_STEP_TP_W8=1: q8_0 mirror of the step TP attention projections for DECODE.
591///
592/// NUMERIC-CLASS door, same class and acceptance as `MEMRA_STEP_TP_QKV_FUSED` /
593/// `MEMRA_BF16_MMV`: the per-row arithmetic becomes an int8 dp4a dot
594/// with per-32 scales instead of a bf16xf32 fma chain, so a bit-tape cannot apply and the
595/// acceptance is the argmax gate plus the boot battery. Motivation is measured, not assumed
596/// (`decode-kernel-census`, 2026-08-25): the fused qkv shape runs 23.0 us in bf16 at
597/// 1.83 TB/s and 14.0 us in q8_0 at 1.60, and o_proj 24.2 -> 11.7 us — together
598/// ~-1.0 ms of a 13.16 ms token. Default OFF.
599/// MEMRA_W8_HYBRID=1 opts the door's HYBRID half in (LM head, shared expert, dense FFN).
600/// Default OFF on measurement AND on residency: it moved decode +0.1% (the W8 trace showed it
601/// only ever mirrored the shexp down rows, which SHEXP_OVERLAP already hides), while costing
602/// ~1.7 GB per card on top of the attention mirrors' ~0.9 GB — and at the model's NATURAL
603/// 262144-token context the full set does not fit: `MEMRA_STEP_TP_W8=1` there dies in
604/// CUDA_ERROR_OUT_OF_MEMORY while plain decode runs 76.03 tok/s.
605/// STEP37 SERVING DEFAULTS (owner flip, 2026-08-27). The step37 serving shape — the t-row walk,
606/// the q8 W8 doors, the SWA ring, the NVFP4 draft heads, and this lane's three verify fixes —
607/// was gated door by door (byte tape == plain, acceptance unchanged, run-spec K=1..8 PASS,
608/// interleaved x5 wall, vendor-default sampled cell with engagement receipts: greedy 93.18 vs
609/// 81.95 plain, sampled 81.79 vs 78.50) and the owner ordered the defaults ON. The doors' call
610/// sites are not all family-scoped (the W8 mirror routing sits inside generic matmul paths), so
611/// the default arms AT MODEL LOAD when the plan compiles to the SlidingGatedMoe program, never
612/// globally. Every door keeps a per-flag env override: `=1` forces ON for any family, `=0` is
613/// the kill switch — the rollback seam the FLAGS rows name. Per-process: a process that loads a
614/// step37-class model arms the defaults for its lifetime.
615static STEP37_SERVING_DEFAULTS: std::sync::atomic::AtomicBool =
616    std::sync::atomic::AtomicBool::new(false);
617
618pub fn arm_step37_serving_defaults() {
619    STEP37_SERVING_DEFAULTS.store(true, std::sync::atomic::Ordering::Relaxed);
620    crate::cache::set_swa_ring_default(true);
621    eprintln!(
622        "[step37-defaults] serving doors armed ON for the SlidingGatedMoe program \
623         (per-flag =0 kills, =1 forces; owner flip 2026-08-27)"
624    );
625}
626
627pub(crate) fn step37_defaults_armed() -> bool {
628    STEP37_SERVING_DEFAULTS.load(std::sync::atomic::Ordering::Relaxed)
629}
630
631/// Tri-state door: `=1` ON, `=0` OFF, unset = the family default (ON once a step37-class model
632/// armed it, OFF otherwise). The env parse is cached; the family default is read live because
633/// arming happens at model load, possibly after another door's first read.
634pub(crate) fn step37_door(cell: &'static std::sync::OnceLock<Option<bool>>, name: &str) -> bool {
635    match *cell.get_or_init(|| match std::env::var(name).ok().as_deref() {
636        Some("1") => Some(true),
637        Some("0") => Some(false),
638        _ => None,
639    }) {
640        Some(forced) => forced,
641        None => step37_defaults_armed(),
642    }
643}
644
645pub(crate) fn w8_hybrid_on() -> bool {
646    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
647    step37_door(&ENV, "MEMRA_W8_HYBRID")
648}
649
650pub(crate) fn step_tp_w8_on() -> bool {
651    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
652    step37_door(&ENV, "MEMRA_STEP_TP_W8")
653}
654
655/// MEMRA_W8_VIEW=1: extend the W8 hybrid half to the ROW-RANGE-VIEW GEMVs, i.e. the lo halves
656/// that `MEMRA_HEAD_SPLIT` and `MEMRA_SHEXP_OVERLAP` keep on rank 0. NOT a step37 family door
657/// and NOT armed by `arm_step37_serving_defaults`: it stays off until it carries its own
658/// interleaved speed rows and its own argmax gate. Unset or `=0` is the rollback seam.
659pub(crate) fn w8_view_on() -> bool {
660    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
661    *ON.get_or_init(|| std::env::var("MEMRA_W8_VIEW").as_deref() == Ok("1"))
662}
663
664/// MEMRA_Q8T_WONCE=1: the q8 t-column verify kernels take their weight-once `_tw` twins — one
665/// row grid, each weight int4 loaded once and dotted against all t columns — instead of the `_t`
666/// forms, whose column grid axis plus __ldcs (streaming, evict-first) re-reads the fully-shared
667/// weights from DRAM once per column (nsys 2026-08-27: qkv_rp_t 1.67x, b4_rp_t 1.43x a
668/// single-column call for 2 columns, where weight-bound scaling says ~1.1x). Per-column float
669/// program unchanged (same lane-strided blk order, own accumulator chain, same reduce); default
670/// off until the byte tape says so.
671/// MEMRA_STEP_GEMM_PRIME: prime chunks (t>=16) route the routed MoE through the grouped f16 GEMM
672/// over the resident NVFP4 banks instead of the per-token device routes. FAMILY-DEFAULT ON since
673/// 2026-08-28 because on the server route it is the only prime that WORKS: measured there, walk
674/// = ERR (tail chunk missing from the distributed kv), fallback chunked prime = 29 s on a
675/// ~450-token prompt and a 90 s TIMEOUT at 4k, grouped GEMM = 3.5-4.9 s with coherent output.
676/// `=0` is the kill switch back to the fallback prime.
677pub(crate) fn step_gemm_prime_on() -> bool {
678    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
679    step37_door(&ENV, "MEMRA_STEP_GEMM_PRIME")
680}
681
682pub(crate) fn q8t_wonce_on() -> bool {
683    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
684    step37_door(&ENV, "MEMRA_Q8T_WONCE")
685}
686
687/// MEMRA_TOPK_FAST=1: barrier-lean sigmoid top-k twin (warp-local top-k + one merge).
688/// Selection and weight arithmetic identical to the round-robin kernel — a latency twin.
689/// MEMRA_SIG_EXPF_DEV=1: device-libm expf sigmoid router (numeric-class door — the
690/// host-glibc transcription is FP64-rate-bound on consumer Blackwell). New tape + battery.
691pub(crate) fn sig_expf_dev_on() -> bool {
692    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
693    *ON.get_or_init(|| std::env::var("MEMRA_SIG_EXPF_DEV").as_deref() == Ok("1"))
694}
695
696pub(crate) fn topk_fast_on() -> bool {
697    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
698    *ON.get_or_init(|| std::env::var("MEMRA_TOPK_FAST").as_deref() == Ok("1"))
699}
700
701/// Select the sigmoid-router kernel without ever sending a shape wider than the fast
702/// kernels' fixed eight-pick scratch. The generic and dexp kernels support the full host
703/// contract; both `_fast` twins index `[warp][8]` storage and would write out of bounds for
704/// `n_used > 8` (Hermes `0d220d8c9a3eb634`).
705fn sigmoid_topk_kernel(sig_expf: bool, fast: bool, n_used: usize) -> &'static str {
706    match (sig_expf, fast && n_used <= 8) {
707        (true, true) => "moe_router_sigmoid_topk_f32_dexp_fast",
708        (true, false) => "moe_router_sigmoid_topk_f32_dexp",
709        (false, true) => "moe_router_sigmoid_topk_f32_fast",
710        (false, false) => "moe_router_sigmoid_topk_f32",
711    }
712}
713
714#[cfg(test)]
715mod sigmoid_topk_dispatch_tests {
716    #[test]
717    fn fast_kernel_refuses_wide_topk_and_composes_with_dexp() {
718        use super::sigmoid_topk_kernel;
719
720        assert_eq!(
721            sigmoid_topk_kernel(false, true, 8),
722            "moe_router_sigmoid_topk_f32_fast"
723        );
724        assert_eq!(
725            sigmoid_topk_kernel(true, true, 8),
726            "moe_router_sigmoid_topk_f32_dexp_fast"
727        );
728        assert_eq!(
729            sigmoid_topk_kernel(false, true, 9),
730            "moe_router_sigmoid_topk_f32"
731        );
732        assert_eq!(
733            sigmoid_topk_kernel(true, true, 9),
734            "moe_router_sigmoid_topk_f32_dexp"
735        );
736    }
737}
738
739pub(crate) fn rms_block() -> u32 {
740    static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
741    *V.get_or_init(|| {
742        std::env::var("MEMRA_RMS_BLOCK")
743            .ok()
744            .and_then(|v| v.parse().ok())
745            .unwrap_or_else(|| RMS_BLOCK_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
746    })
747}
748
749pub(crate) fn fa_split_keys(t_kv: usize, n_head_kv: usize) -> usize {
750    static S: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
751    if let Some(forced) = *S.get_or_init(|| {
752        std::env::var("MEMRA_FA_SPLIT")
753            .ok()
754            .and_then(|v| v.parse().ok())
755            .filter(|&s: &usize| s >= 8 && s % 8 == 0)
756    }) {
757        return forced;
758    }
759    // CTX-ADAPTIVE default (2026-07-05 40k sweep: sp32 24.5 vs sp128 26.0 tok/s = +5.8% — at
760    // deep ctx the n_splits count explodes (40k/32 = 1265 splits x 8 kv-heads) and the combine
761    // + partial-buffer cost dominates; at short ctx small splits fill the SMs). Exactness: split
762    // size only changes the PARTITION of keys; the rows/combine order per split is fixed and the
763    // gate battery (kernel-check + run-spec K=1..8) arbitrates every default change.
764    //
765    // SM-AWARE SHORT-CTX RUNG (2026-07-06 rtx6000): the 32-key rung was tuned on the 82-SM 5090.
766    // On 188 SMs the vec grid (n_head_kv x n_splits CTAs) starves at short ctx — the 35B has
767    // n_head_kv=2, so ctx128/split32 = 8 CTAs on 188 SMs. Measured on rtx6000 (N=1 sweep + N=3
768    // interleaved confirm): 35B ctx128 sp16 179 vs sp32 161 (+11%), ctx512 178 vs 158, ctx2048
769    // flat, ctx>=4096 sp64 edges sp16 by ~3%; 27B ctx128 70.9 vs 66.3 (+7%); 9B 177 vs 163
770    // (+9%). Rigs <=100 SMs keep the validated 5090 ladder EXACTLY (default unchanged there —
771    // rig-divergence law: this branch is measured on 188 SMs only).
772    // gemma4 all-16 ladder probe REVERTED (2026-07-10): +1.3 plain at d1736 (157.5 vs 156.2)
773    // but depth VERIFY collapsed (spec 203.5 -> 169 — the windowed rows' per-row combine over
774    // 64 splits). The mixed default (swa nkv=8 -> 32, globals nkv=2 -> 8-ladder) stays; a
775    // caller-split policy would break row-vs-decode split parity. FA_SP_GEMMA kept as a seam.
776    if FA_SP_GEMMA.load(std::sync::atomic::Ordering::Relaxed)
777        && std::env::var("MEMRA_FA_SP16").as_deref() == Ok("1")
778    {
779        return if t_kv <= 8192 {
780            16
781        } else if t_kv <= 16384 {
782            64
783        } else {
784            128
785        };
786    }
787    let big_rig = fa_sm_count() >= 128;
788    if big_rig {
789        let _ = n_head_kv;
790        if t_kv <= 2048 {
791            // MEMRA_FA_SP_SHORT=N: the SHORT rung only (the SWA layers' capped t_kv lands
792            // here on step37: 33 of 45 layers at t_kv=512). At 16 the tile loop runs
793            // HALF-EMPTY (FA_DEC_TILE=32 -> nt=16 per split), so the V staging pass moves a
794            // half tile per iteration and the combine carries 2x the partials; 32 makes each
795            // split exactly one full tile. A global MEMRA_FA_SPLIT cannot isolate this — it
796            // moves the deep-ctx rung too, where more splits measured worse.
797            // NUMERIC-CLASS door (key partition -> different per-split partials/combine):
798            // new tape + battery, exactly like every other split-ladder change.
799            static SHORT: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
800            if let Some(sp) = *SHORT.get_or_init(|| {
801                std::env::var("MEMRA_FA_SP_SHORT")
802                    .ok()
803                    .and_then(|v| v.parse().ok())
804                    .filter(|&s: &usize| s >= 8 && s % 8 == 0)
805            }) {
806                return sp;
807            }
808            16
809        } else if t_kv <= 16384 {
810            64
811        } else {
812            128
813        }
814    } else if n_head_kv <= 4 {
815        // KV-HEAD-AWARE RUNG (2026-07-08, 5090): the 8192->32 rung was validated on kv=8 models
816        // (27B/9B: 8 heads x n_splits fills 82 SMs). The 35B has n_head_kv=2 — at ctx512/sp32
817        // the vec grid is 2 x 20 = 40 CTAs on 82 SMs (half idle). Measured (35B, run-gen 128tok
818        // N=1 sweep + N=3 confirm): sp8 162.1 / sp16 161.3 / sp32 159.4 at short ctx.
819        // DEPTH TAPER (same day, the deep-ctx lesson re-learned on this rung): sp8 at d6257 =
820        // 782 splits -> combine + partial-buffer cost dominates (141.2 tok/s); the d6257 sweep
821        // says sp64 = 153.0 (sp16/32 147, sp96 147.6, sp128 141). Few-kv-head models need the
822        // taper EARLIER than kv=8 (per-split grid 4x thinner, same per-split combine cost).
823        // Crossover hunt: sp8 vs sp64 = 156.7/155.9 at d3072, 151.7/155.6 at d4096 -> boundary 3072.
824        // RUNG RE-SWEPT UNDER THE DEEP KERNEL (2026-08-02, lane/ladder-3072 — the stale-verdict
825        // law: the 3072 boundary was calibrated on the conflicted v4 core; the deep rewrite cut
826        // vec cost ~1.2-1.4x while combine scales with n_splits, so sp8's combine bill
827        // dominates far earlier). Kernel receipts (quiet-rig nsys, deep vec + combine us):
828        // d1024 sp8 17.1 vs sp64 10.6; d2048 31.0 vs 12.2; d3072 44.0 vs 18.3. e2e run-gen
829        // tg128 N=3 interleaved (KAT + q35, research/ladder-3072-20260802/): sp8 loses at
830        // EVERY depth >= 1024 (KAT d2048 182.6 vs 188.0 = -2.9%, d3072 175.9 vs 186.4 =
831        // -5.6%; q35 d4096 169.2 vs 182.6 = -7.4%); d512 flat (+-0.2%, inside noise). sp32
832        // ties sp64 within noise in the mid band and loses at d4096 -> no extra rung.
833        // Boundary 3072 -> 512: sp8 keeps only the short-ctx band it was validated on
834        // (ctx128-512); sp64 takes over where the deep kernel made combine the bill.
835        if t_kv <= 512 {
836            8
837        } else if t_kv <= 16384 {
838            64
839        } else {
840            128
841        }
842    } else {
843        if t_kv <= 8192 {
844            32
845        } else if t_kv <= 16384 {
846            64
847        } else {
848            128
849        }
850    }
851}
852
853/// SM count of device 0, cached (used by fa_split_keys' rig-size rung; primary-context query,
854/// same attribute Engine::batched_variant reads).
855pub(crate) fn fa_sm_count() -> i32 {
856    static N: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
857    *N.get_or_init(|| {
858        cudarc::driver::result::init().ok();
859        cudarc::driver::result::device::get(0)
860            .and_then(|d| unsafe { cudarc::driver::result::device::get_attribute(
861                d, cudarc::driver::sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT) })
862            .unwrap_or(82)
863    })
864}
865
866/// FA-prefill kernel-name suffix for a head_dim (the template-stamped twins in flash_attn.cu):
867/// 256 = the original names (qwen35 class, dispatch unchanged), 128 = `_hd128` (MiniMax-M3).
868/// Any other dim errors — callers gate to sdpa_naive before dispatching FA.
869fn fa_hd_suffix(head_dim: usize) -> Result<&'static str, Box<dyn std::error::Error>> {
870    match head_dim {
871        256 => Ok(""),
872        128 => Ok("_hd128"),
873        d => Err(format!(
874            "fa_prefill: no kernel stamped for head_dim={d} (only 256/128); \
875                          callers must gate to sdpa_naive"
876        )
877        .into()),
878    }
879}
880
881/// Quant type codes matching qmatvec.cu QType enum.
882pub const QT_Q8_0: i32 = 0;
883pub const QT_Q4_K: i32 = 1;
884pub const QT_Q6_K: i32 = 2;
885pub const QT_Q5_K: i32 = 3;
886pub const QT_Q3_K: i32 = 4;
887pub const QT_IQ4_XS: i32 = 5;
888pub const QT_IQ3_S: i32 = 6;
889pub const QT_NVFP4: i32 = 7;
890/// Slot-major v2 bank permutation of `QT_NVFP4` (see tp.rs `nvfp4_matrix_v2_permute`) — only the
891/// grouped-prefill dequant consumes this tag; every direct/dp4a lane must keep refusing it.
892pub const QT_NVFP4_V2: i32 = 107;
893/// Checkpoint-native FP8-E4M3 (MEMRA_ST_E4M3, lane e4m3dec): raw safetensors e4m3 weight bytes
894/// [out_f, in_f] row-major (row_bytes == in_f), per-tensor f32 weight_scale in GpuTensor `scale`
895/// (fused at the mmvq write / post-matmul scale_inplace). Decode = qmatvec_e4m3_mmvq (+ _b2/_b4/_b8
896/// batched twins); prefill (m>=16) = the cuBLASLt FP8 GEMM on the SAME resident bytes (fp8_ffi.rs)
897/// — ONE weight copy total, no Q8_0 re-encode duplicate.
898pub const QT_F8_E4M3: i32 = 10;
899/// Device-side tag for the A6 SPLIT-PLANE repacked NVFP4 layout (Stage-A generic kernel only;
900/// GpuTensor keeps qtype=QT_NVFP4 + an `rp` flag — this tag never lives in a GpuTensor).
901pub const QT_NVFP4_RP: i32 = 9;
902/// Unquantized f32 weight (safetensors MoE Path A: experts dequantized to f32 host-resident).
903pub const QT_F32: i32 = 8;
904pub const QT_BF16: i32 = 11;
905pub const QT_Q4_0: i32 = 12; // gemma-4 QAT GGUF weight format (18B/32: fp16 d + nibbles)
906/// GGUF Q2_K. Appended after the existing Q4_0 code so kernel ABI values do not move.
907/// Mixed-expert artifacts use the generic f32-dequant staged kernel until a target-rig-gated
908/// dp4a/MMQ implementation exists.
909pub const QT_Q2_K: i32 = 13;
910/// Checkpoint-native FP8-E4M3 with a BLOCK-128 weight-scale GRID (lane/fp8-blk128-decode,
911/// 2026-08-05) — the Qwen-official FP8 / DeepSeek-V3 scale class. Same raw e4m3 bytes as
912/// `QT_F8_E4M3` ([out_f, in_f] row-major, row_bytes == in_f), but the dequant scale is
913/// `GpuTensor::Quant.blk` (`Fp8BlockScales`, [ceil(out_f/128), ceil(in_f/128)] f32) and the
914/// scalar `scale` field is 1.0 by the layout contract.
915///
916/// WHY A DISTINCT CODE rather than `QT_F8_E4M3` + a `blk` flag: every existing QT_F8_E4M3
917/// consumer (qmatvec_e4m3_mmvq and its batched/fused twins, e4m3_fused_params,
918/// matmul_pre_dual_noscale's F8 arm, try_fp8_gemm) threads exactly ONE scalar weight scale. Under
919/// a shared code, any consumer that was not taught the grid would still MATCH and would dequant
920/// every tile at scale 1.0 — a silent numeric corruption. Under a distinct code every untaught
921/// consumer refuses loudly instead (`mmvq_supports`/`gemm_supports`/`mmq_supports` return false;
922/// the mmvq name match panics), so a missed dispatch site is a crash or a refusal receipt, never
923/// wrong numbers. Decode = `qmatvec_e4m3_blk_mmvq`; prefill (m>=16) = the per-block FP8 MMQ tile
924/// on the SAME resident bytes+grid (fp8_ffi::try_fp8_blk_mmq) — ONE weight copy total.
925pub const QT_F8_E4M3_BLK: i32 = 14;
926
927/// Engine device context: CUDA context, stream, loaded kernel modules, cuBLASLt (via runtime::Gpu).
928pub struct Engine {
929    pub gpu: memra_runtime::Gpu,
930    module: Arc<CudaModule>,
931    hybrid: Arc<CudaModule>,
932    qmatvec: Arc<CudaModule>,
933    flash: Arc<CudaModule>,
934    /// FP8-GLOBALS module (2026-07-11): the kf8vf8 fatbin loaded ALONGSIDE the default —
935    /// gemma GLOBAL layers (hd512) append + attend in e4m3 (dequant-latency arc, HANDOVER).
936    /// Lazy: loaded on first global-format use; None until then.
937    flash_g: std::sync::OnceLock<Arc<CudaModule>>,
938    gemm: Arc<CudaModule>,
939    router: Arc<CudaModule>,
940    /// Sampled-spec kernels (research/sampled-spec-impl-map.md piece A).
941    sample: Arc<CudaModule>,
942    /// EDGE-1 §B: one shared SLRU expert-residency cache, lazily built on first MoE dispatch under
943    /// MEMRA_MOE_CACHE. `Mutex` makes it multi-agent safe (§E.2); the lock covers only lookup/admit/
944    /// memcpy-issue (µs), NOT the GEMM, so streams still overlap. `None` => cache disabled.
945    moe_cache: Mutex<Option<crate::moe_cache::MoeSlotCache>>,
946    /// MEMRA_STEP_TP_W8, hybrid half: q8_0 mirrors of bf16 GEMV weights that do NOT live in a
947    /// TP resident bank (the LM head, the shared expert, the dense-FFN layers), keyed by the
948    /// bf16 slab's device pointer and built on first decode use. The mirror is 1.0625 B/w
949    /// against bf16's 2, and the raw slab stays resident, so prefill keeps its arithmetic.
950    /// KEYED ON (pointer, in_f, out_f), not on the pointer alone: a row-range VIEW of a slab
951    /// carries the PARENT's base pointer when the range starts at row 0, so a pointer-only key
952    /// would hand the head-split lo half (4096 x 64448) the full head's mirror (4096 x 128896)
953    /// and read 2x past the rows it owns. The shape is part of the identity of a mirror.
954    w8_mirrors: Mutex<std::collections::HashMap<(u64, u32, u32), CudaSlice<u8>>>,
955    /// Per-`in_f` q8_1 activation scratch for those mirrors (allocating per call would cost
956    /// more than the door saves).
957    w8_act: Mutex<std::collections::HashMap<usize, (CudaSlice<i8>, CudaSlice<f32>)>>,
958    /// Exact retained expert-block lengths collected after model load. Mixed-layout models use
959    /// this inventory to preallocate fixed-address size classes instead of sizing every slot to
960    /// the single largest block. The cache still owns every address for its full lifetime.
961    moe_cache_layout: Mutex<Option<Vec<usize>>>,
962    /// CAPTURE-RETAIN mode (graph arc, 2026-07-12): while a graph capture (and its allocator
963    /// warmups) runs, every Engine allocation is ALSO kept alive here — a captured graph's
964    /// transient buffers must never return to the pool, or later allocations (e.g. the spec
965    /// verify between replays) reuse their addresses and the replay reads/writes live memory
966    /// (the draft-graph corruption root cause). Fast-path cost when off: one relaxed atomic.
967    capture_keep_on: std::sync::atomic::AtomicBool,
968    /// VERIFY-EXACT scope (dflash lane, 2026-07-13): when set, matmul/matmul_pre skip the
969    /// m>=16 prefill-GEMM branches so a t>=16 batched VERIFY rides the decode-exact b-tier
970    /// class (the parity law). The t=16 dflash verify tripped the GEMM threshold — 770us/
971    /// matmul (54% of the round) AND a different FP order than decode (issue-10 landmine).
972    verify_exact: std::sync::atomic::AtomicBool,
973    capture_keep: Mutex<Vec<Box<dyn std::any::Any + Send>>>,
974    /// EDGE-1 §C.2: dedicated H2D copy stream for async prefetch (event-synced to the compute stream).
975    pub copy_stream: Arc<CudaStream>,
976    /// Resident CUTLASS NVFP4 prefill scratch (workspace + a_packed + sfa_linear + sfa_sw + y + alpha),
977    /// allocated ONCE and grown to the largest prefill GEMM shape, then reused per-call. Removes the
978    /// 6 fresh allocations + alpha htod that `cutlass_fp4_gemm` did every prefill matmul (~200/prefill).
979    /// Safe as a single shared buffer because all GPU compute serializes on the one `gpu.stream` worker
980    /// thread (the server runs one GPU worker; no concurrent CUTLASS GEMMs share this scratch). `None`
981    /// until the first CUTLASS FP4 GEMM. Mutex guards lazy build/grow only (matches `moe_cache`).
982    #[cfg(memra_cutlass)]
983    cutlass_scratch: Mutex<Option<crate::cutlass_ffi::CutlassScratch>>,
984    /// FP8-ACT PREFILL scratch (MEMRA_PP_FP8): quantized-activation buffer + scale block + cuBLASLt
985    /// workspace, allocated once and grown to the largest prefill m*k (see fp8_ffi.rs). `None`
986    /// until the first FP8 prefill GEMM; Mutex guards lazy build/grow only (matches cutlass_scratch).
987    fp8_scratch: Mutex<Option<crate::fp8_ffi::Fp8Scratch>>,
988    /// f16-P/V door: pooled V re-encode buffer (bf16->f16) for the hd512 _pre path. Lazy-grow;
989    /// per-call cudaMalloc was a laptop-regression suspect (VRAM pressure, 31B nkv=4 = 4x bytes).
990    fa_vf16_scratch: Mutex<Option<CudaSlice<u8>>>,
991    /// Pooled fa-decode split partials (part_o, part_m, part_l): per-call zeros() was 3
992    /// alloc+memset pairs per fa launch (~144 mem nodes per decode token — the graph door's
993    /// residual launch tax) — lazy-grow, memset-prefix per use, stream-ordered reuse.
994    fa_part_pool: Mutex<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
995    /// Retired fa-part pool generations (#68): old buffers whose addresses captured graphs may
996    /// have baked — kept alive for the Engine's lifetime instead of returning to the async pool
997    /// (see the RETIRE-ON-GROW comment at the realloc sites). Doubling growth bounds the total.
998    fa_part_retired: Mutex<Vec<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
999    /// name -> resolved CudaFunction (capture-safe lookups; see `func`).
1000    fn_cache: Mutex<std::collections::HashMap<String, CudaFunction>>,
1001    f16_scratch: Mutex<Option<crate::f16_ffi::F16Scratch>>,
1002    /// RANK1 LEVER (parallel argmax): resident pass-1 partials scratch (part_v[NB] f32, part_i[NB] i32),
1003    /// allocated ONCE on first parallel-argmax call and reused. Stable pointers so the 2-pass argmax
1004    /// is CUDA-graph-capturable (the buffer is referenced by both captured passes; lazy-allocated
1005    /// before capture under the generate_graph tracking-off window so it carries no events).
1006    argmax_partials: Mutex<Option<(CudaSlice<f32>, CudaSlice<i32>)>>,
1007    /// ARC B (chunk-prime dequant-once): resident bf16 K/V workspace for `fa_prefill_view_ws`
1008    /// ((K bytes, V bytes) u8 buffers holding [t_kv, kv_dim] bf16). Grown lazily to the largest
1009    /// (t_kv, kv_dim) seen, REUSED across layers/chunks/calls (contents rewritten per launch —
1010    /// safe because all compute serializes on the one gpu.stream). ~82MB at 40k ctx on the 27B.
1011    prime_deqw_ws: Mutex<Option<(CudaSlice<u8>, CudaSlice<u8>)>>,
1012    /// LAUNCH-STRUCTURE STAGE 1: persistent PINNED (cacheable, flags=0) host staging buffer for the
1013    /// fused-router sel/w readback — one async DtoH pair + ONE sync instead of two synced dtohs.
1014    /// Grown lazily; reused every MoE layer (single-threaded decode serializes on the sync).
1015    router_stage: Mutex<Option<PinnedStage>>,
1016}
1017
1018/// FAVENDOR lane env gate (2026-07-08): MEMRA_FA_V2=1 dispatches the llama-fattn-vec-mechanism
1019/// decode kernels (fa_decode_vec_q_v2 / fa_decode_vec_q_rows_v2 / fa_decode_vec_q_v2_dc):
1020/// tile-batched online softmax (one alpha rescale per 32-key tile instead of per key) + wide-load
1021/// block dequant in the staging phase. NOTE rev2: llama's register streaming (no smem) was ALSO
1022/// tried and measured 2x WORSE at depth in our gqa-warps frame — the smem KV-tile broadcast stays
1023/// (see the kernel comment). NEW NUMERIC CONFIG (tile-level softmax regrouping changes FP order vs
1024/// the per-key twins) — own argmax baseline; eager decode, the spec-verify rows path AND the
1025/// graph _dc path switch TOGETHER (the spec-exactness law). Default OFF. Read per call (not
1026/// OnceLock) so the gate battery can A/B within one process, matching the MEMRA_NO_FA_VEC pattern.
1027fn fa_v2_on() -> bool {
1028    // DEFAULT ON since 2026-07-08 (MEMRA_FA_V2=0 reverts): tile-batched online softmax, e2e
1029    // measured across every model x depth — 35B 168.7->173.4 (d512) / 153.1->158.5 (d6257),
1030    // 9B 131.2->132.7 / 108.4->124.5 (+15% — the engine-wide depth-slope fix), 27B 47.2->47.7 /
1031    // 42.2->44.9. One-time numeric-config change; kernel-check + argmax + spec self-consistency
1032    // + graph bit-identity green on all three models.
1033    std::env::var("MEMRA_FA_V2")
1034        .map(|v| v != "0")
1035        .unwrap_or(true)
1036}
1037
1038/// FA v3 gate (default ON since 2026-07-09; MEMRA_FA_V3=0 reverts to v2 — research/fa/fa_v3_design.md):
1039/// HYBRID decode twins (fa_decode_vec_q_v3 / _rows_v3 / _v3_dc): llama's int8-dp4a K.Q with
1040/// register-quantized Q (no K dequant, no K smem) + OUR CTA-shared staged bf16 V tile + OUR
1041/// split partition/combine. NEW NUMERIC CONFIG (int8 Q quantization changes the K.Q accumulation
1042/// vs the bf16-roundtrip FMA chain) — own argmax baseline; eager decode, the spec-verify rows
1043/// path AND the graph _dc path switch TOGETHER (the spec-exactness law). Read per call so the
1044/// gate battery can A/B within one process (the MEMRA_FA_V2 pattern).
1045/// `MEMRA_FA_PART_ZERO=1`: zero every freshly grown fa partial bank. DEFAULT OFF,
1046/// diagnostic only. See `fa_part_alloc` for what it discriminates and why it is not a fix.
1047pub(crate) fn fa_part_zero_on() -> bool {
1048    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1049    *ON.get_or_init(|| std::env::var("MEMRA_FA_PART_ZERO").as_deref() == Ok("1"))
1050}
1051
1052pub(crate) fn fa_v3_on() -> bool {
1053    // DEFAULT ON since 2026-07-09 (MEMRA_FA_V3=0 reverts to v2): dp4a-K hybrid FA decode —
1054    // fa kernel -21-23% at depth (micro), 35B spec p3 +5% (190->200, the last spec cell),
1055    // d6257 +1.7%. Own numeric config; full battery green on 35B+9B incl graph bit-identity.
1056    std::env::var("MEMRA_FA_V3")
1057        .map(|v| v != "0")
1058        .unwrap_or(true)
1059}
1060
1061/// The v3 dp4a K path reads RAW q8_0 bytes (34B blocks) and stages q5_1 V verbatim — it is only
1062/// correct on the DEFAULT KV formats — and needs dpl % 4 == 0 consecutive quants per lane
1063/// (head_dim % 128 == 0; both daily models are hd256). All three dispatch sites share this
1064/// predicate so the twins can never diverge.
1065fn fa_v4_mode() -> &'static str {
1066    static M: std::sync::OnceLock<String> = std::sync::OnceLock::new();
1067    M.get_or_init(|| std::env::var("MEMRA_FA_V4").unwrap_or_default())
1068}
1069fn fa_v4_on() -> bool {
1070    fa_v4_mode() != "0"
1071} // DEFAULT ON 2026-07-10 (MEMRA_FA_V4=0 rollback)
1072/// t_kv-conditional v4 pick (gemma depth lesson 2026-07-10: v4's key-per-lane pipeline starves
1073/// at the 1024-window with short splits — MEMRA_FA_V4=0 measured depth plain 158.0 vs 156.7).
1074/// Threshold MEMRA_FA_V4_MAX (default usize::MAX = unchanged behavior; gemma sets 1024 at load
1075/// via FA_V4_MAX_DEFAULT). Applied at EVERY dispatch site (eager, rows, rows_w, dc) so verify
1076/// stays kernel-family-identical to decode at the same t_kv.
1077/// Per-model deep-ctx smem floor default (MEMRA_FA_SMEM_TKV env overrides): gemma pushes it
1078/// above the 1024 window so the windowed decode + verify rows share the REGISTER family.
1079pub static FA_SMEM_TKV_DEFAULT: std::sync::atomic::AtomicUsize =
1080    std::sync::atomic::AtomicUsize::new(1024);
1081pub static FA_V4_MAX_DEFAULT: std::sync::atomic::AtomicUsize =
1082    std::sync::atomic::AtomicUsize::new(usize::MAX);
1083pub fn fa_v4_at_pub(t_kv: usize) -> bool {
1084    fa_v4_at(t_kv)
1085}
1086fn fa_v4_at(t_kv: usize) -> bool {
1087    static M: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
1088    let mx = *M.get_or_init(|| {
1089        std::env::var("MEMRA_FA_V4_MAX")
1090            .ok()
1091            .and_then(|v| v.parse().ok())
1092            .unwrap_or_else(|| FA_V4_MAX_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
1093    });
1094    fa_v4_on() && t_kv < mx
1095}
1096/// FA-DEEP gate (2026-08-02, lane fa-decode-deep): deep-ctx v4 twins
1097/// (fa_decode_vec_q_v4_deep / _deep_dc) — the depth-decode lane's priced fix. Unlike
1098/// v2/v3/v4 this is NOT a numeric config: the deep twins run the v4 program VERBATIM
1099/// (same split partition, same softmax/accumulation order, same partials/combine) and only
1100/// move the smem physical layout (bank de-conflict row pads) + the load schedule (next-tile
1101/// L2 prefetch) — kernel-check pins bitdiff==0 vs the v4 twins across depths, so eager /
1102/// rows-verify / graph / seqs stay mutually bit-identical wherever the threshold falls.
1103/// Engages at t_kv >= MEMRA_FA_DEEP_MIN. The swept floor is 0 = ALWAYS ON where v4 ran
1104/// (fa-deep-bench fine grid 96..6144, 2026-08-02: deep flat-or-better at EVERY depth,
1105/// 1.01-1.26x, no losing cell — so there is no engagement boundary and no new
1106/// capture-recapture edge; the env stays as a sweep/diagnostic seam only).
1107/// MEMRA_FA_DEEP=0 is the rollback seam. Read per call so the battery + bench can A/B
1108/// within one process (the v2/v3 pattern).
1109pub const FA_DEEP_MIN_DEFAULT: usize = 0;
1110fn fa_deep_at(t_kv: usize) -> bool {
1111    if std::env::var("MEMRA_FA_DEEP").as_deref() == Ok("0") {
1112        return false;
1113    }
1114    let min = std::env::var("MEMRA_FA_DEEP_MIN")
1115        .ok()
1116        .and_then(|v| v.parse().ok())
1117        .unwrap_or(FA_DEEP_MIN_DEFAULT);
1118    t_kv >= min
1119}
1120/// Public twin (kernel-check builds the deep-vs-v4 bit pin; bench sweeps the floor).
1121pub fn fa_deep_at_pub(t_kv: usize) -> bool {
1122    fa_deep_at(t_kv)
1123}
1124
1125fn fa_v3_active(head_dim: usize) -> bool {
1126    // v3's dp4a-K walk reads raw q8_0 K bytes — no e4m3 arm; the fp8-KV arm (MEMRA_KV_FP8)
1127    // must fall back like any non-default KV format (the rows_dc stream path asserts on it).
1128    fa_v3_on()
1129        && head_dim % 128 == 0
1130        && kv_cache_formats() == ("q8_0", "q5_1")
1131        && !Engine::kv_fp8_on()
1132}
1133
1134/// BATCHED-TICK increment 2 (2026-08-01): true iff a row at this t_kv would take the v4
1135/// eager arm in `fa_decode_kvmod`'s dispatch — the exact precondition for the z-batched
1136/// `fa_decode_vec_q_seqs_v4` twin to reproduce its per-seq program bit-identically.
1137/// Mirrors the kvmod predicates: vec on + above the vec floor + hd256 + inside the v4
1138/// window + the PRODUCTION v4 body (the noB3/stage phase probes are wrong-output) + the
1139/// default flash module (no fp8-KV g-module). Callers must ALSO group rows on one
1140/// `fa_split_keys` rung (the rows-twins' straddle law) before batching.
1141pub fn fa_seqs_eligible(t_kv: usize, head_dim: usize) -> bool {
1142    std::env::var("MEMRA_NO_FA_VEC").is_err()
1143        && t_kv >= fa_vec_min_tkv()
1144        && head_dim == 256
1145        && fa_v4_at(t_kv)
1146        && !matches!(fa_v4_mode(), "noB3" | "stage")
1147        && !Engine::kv_fp8_on()
1148}
1149/// Public twin of the crate-private split ladder (kernel-check builds the seqs-vs-loop pin).
1150pub fn fa_split_keys_pub(t_kv: usize, n_head_kv: usize) -> usize {
1151    fa_split_keys(t_kv, n_head_kv)
1152}
1153
1154/// A raw pinned (page-locked, CACHEABLE — flags=0, not write-combined) host allocation for
1155/// DtoH staging. cudarc's `alloc_pinned` uses CU_MEMHOSTALLOC_WRITECOMBINED, which is right for
1156/// HtoD streams but pathologically slow for host READS — the router readback is host-read-heavy,
1157/// so we allocate through `result::malloc_host` with flags=0 directly.
1158struct PinnedStage {
1159    ptr: *mut u8,
1160    cap: usize,
1161}
1162unsafe impl Send for PinnedStage {}
1163impl PinnedStage {
1164    fn new(cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
1165        let ptr = unsafe { cudarc::driver::result::malloc_host(cap, 0)? } as *mut u8;
1166        Ok(PinnedStage { ptr, cap })
1167    }
1168}
1169impl Drop for PinnedStage {
1170    fn drop(&mut self) {
1171        let _ = unsafe { cudarc::driver::result::free_host(self.ptr as _) };
1172    }
1173}
1174
1175/// Number of pass-1 blocks for the parallel argmax (fan-out across SMs to saturate HBM). 256 blocks
1176/// x 256 threads = 65536 threads covering the 248K-vocab scan in ~4 strided loads/thread.
1177pub const ARGMAX_NB: usize = 256;
1178
1179/// crate-visible alias for the batched FA3 shim entry (hybrid_forward's batch arm).
1180pub(crate) use memra_fa3_vl as fa3_vl_raw;
1181
1182unsafe extern "C" {
1183    /// FA3 v10 shim (cu/fa3_prefill.cu): TMA-swizzled wgmma FA, fresh causal hd256.
1184    fn memra_fa3_prefill(
1185        q16: *const core::ffi::c_void,
1186        k16: *const core::ffi::c_void,
1187        v16: *const core::ffi::c_void,
1188        o: *mut f32,
1189        t: i32,
1190        h: i32,
1191        hkv: i32,
1192        d: i32,
1193        scale: f32,
1194        stream: *mut core::ffi::c_void,
1195    ) -> i32;
1196    /// batched varlen twin: host arrays of device pointers per seq (B <= 8).
1197    pub(crate) fn memra_fa3_vl(
1198        q16s: *const *const core::ffi::c_void,
1199        k16s: *const *const core::ffi::c_void,
1200        v16s: *const *const core::ffi::c_void,
1201        os: *const *mut f32,
1202        ts: *const i32,
1203        b: i32,
1204        h: i32,
1205        hkv: i32,
1206        d: i32,
1207        scale: f32,
1208        stream: *mut core::ffi::c_void,
1209    ) -> i32;
1210}
1211
1212/// STAGE-2 GROUPED DECODE: 8 expert weight-block device pointers passed BY VALUE as one kernel
1213/// param (matches the CUDA `wptr8_t` struct: 8x 64-bit pointers, `#[repr(C)]` => identical
1214/// layout). The pointers are SLRU cache-slot base addresses — fixed for the engine's lifetime
1215/// (slots are never re-allocated), so passing raw values is stable across the launch.
1216#[repr(C)]
1217#[derive(Clone, Copy)]
1218pub struct WPtr8(pub [u64; 8]);
1219unsafe impl cudarc::driver::DeviceRepr for WPtr8 {}
1220
1221/// task #18 varlen GDN: per-seq args for gdn_chunk_{state,output}_mma_vl — one launch
1222/// runs all B<=8 sequences' K4/K5 (CUDA `gdnseq_t`/`gdnvl_t`, layout-identical repr(C)).
1223/// Raw addresses are valid for the launch: every referenced buffer outlives the call and
1224/// all work is on the single compute stream (same discipline as the f16 GEMM FFI).
1225#[repr(C)]
1226#[derive(Clone, Copy, Default)]
1227pub struct GdnSeqVl {
1228    pub kb16: u64,
1229    pub gcum: u64,
1230    pub beta: u64,
1231    pub u: u64,
1232    pub wb16: u64,
1233    pub y: u64,
1234    pub ssnap: u64,
1235    pub state_in: u64,
1236    pub state_out: u64,
1237    pub q: u64,
1238    pub p: u64,
1239    pub o: u64,
1240    pub k: u64,
1241    pub v: u64,
1242    pub g: u64,
1243    pub a: u64,
1244    pub w: u64,
1245    pub t: i32,
1246    pub nc: i32,
1247}
1248unsafe impl cudarc::driver::DeviceRepr for GdnSeqVl {}
1249#[repr(C)]
1250#[derive(Clone, Copy)]
1251pub struct GdnVl8(pub [GdnSeqVl; 8]);
1252unsafe impl cudarc::driver::DeviceRepr for GdnVl8 {}
1253
1254/// task #22: per-seq wgmma-fused extras (CUDA `gdnw_t`/`gdnwvl_t`) — qb16 mirror +
1255/// pre-masked Pb16, riding NEXT TO GdnSeqVl so the base struct stays untouched.
1256#[repr(C)]
1257#[derive(Clone, Copy, Default)]
1258pub struct GdnWVl {
1259    pub qb16: u64,
1260    pub pb16: u64,
1261}
1262unsafe impl cudarc::driver::DeviceRepr for GdnWVl {}
1263#[repr(C)]
1264#[derive(Clone, Copy)]
1265pub struct GdnWVl8(pub [GdnWVl; 8]);
1266unsafe impl cudarc::driver::DeviceRepr for GdnWVl8 {}
1267
1268/// task #18 increment 3: per-seq PREP/TAIL args (CUDA `gdnprep_t`/`gdnprepvl_t`).
1269#[repr(C)]
1270#[derive(Clone, Copy, Default)]
1271pub struct GdnPrepVl {
1272    pub qkv: u64,
1273    pub conv_state: u64,
1274    pub conv_out: u64,
1275    pub q_g: u64,
1276    pub k_g: u64,
1277    pub v_g: u64,
1278    pub q_l2: u64,
1279    pub k_l2: u64,
1280    pub beta_raw: u64,
1281    pub alpha: u64,
1282    pub beta: u64,
1283    pub g_log: u64,
1284    pub o: u64,
1285    pub z: u64,
1286    pub gn: u64,
1287    pub gn16: u64,
1288    pub kb16: u64,
1289    pub qb16: u64,
1290    pub t: i32,
1291    pub pad: i32,
1292}
1293unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl {}
1294#[repr(C)]
1295#[derive(Clone, Copy)]
1296pub struct GdnPrepVl8(pub [GdnPrepVl; 8]);
1297unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl8 {}
1298
1299/// task #18 (attn side): per-seq varlen FA args (CUDA `faseq_t`/`favl_t`).
1300#[repr(C)]
1301#[derive(Clone, Copy, Default)]
1302pub struct FaSeqVl {
1303    pub q: u64,
1304    pub k16: u64,
1305    pub v16: u64,
1306    pub o: u64,
1307    pub kf: u64,
1308    pub vf: u64,
1309    pub t: i32,
1310    pub pad: i32,
1311}
1312unsafe impl cudarc::driver::DeviceRepr for FaSeqVl {}
1313#[repr(C)]
1314#[derive(Clone, Copy)]
1315pub struct FaVl8(pub [FaSeqVl; 8]);
1316unsafe impl cudarc::driver::DeviceRepr for FaVl8 {}
1317
1318/// task #18 (attn pre-FA): per-seq split/norm/rope/append args (CUDA `attnpre_t`).
1319#[repr(C)]
1320#[derive(Clone, Copy, Default)]
1321pub struct AttnPreVl {
1322    pub qf: u64,
1323    pub kf: u64,
1324    pub vf: u64,
1325    pub q: u64,
1326    pub gate: u64,
1327    pub qn: u64,
1328    pub kn: u64,
1329    pub kc: u64,
1330    pub vc: u64,
1331    pub t: i32,
1332    pub pad: i32,
1333}
1334unsafe impl cudarc::driver::DeviceRepr for AttnPreVl {}
1335#[repr(C)]
1336#[derive(Clone, Copy)]
1337pub struct AttnPreVl8(pub [AttnPreVl; 8]);
1338unsafe impl cudarc::driver::DeviceRepr for AttnPreVl8 {}
1339
1340/// task #18 increment 2: one sequence's FULL chunk-buffer set (alloc-only; the
1341/// varlen K1-K5 chain fills them).
1342pub struct GdnChunkBufs {
1343    pub gcum: CudaSlice<f32>,
1344    pub a: CudaSlice<f32>,
1345    pub p: CudaSlice<f32>,
1346    pub u: CudaSlice<f32>,
1347    pub w: CudaSlice<f32>,
1348    pub kb16: CudaSlice<u8>,
1349    pub wb16: CudaSlice<u8>,
1350    pub y16: CudaSlice<u8>,
1351    pub ssnap16: CudaSlice<u8>,
1352    pub qb16: CudaSlice<u8>,
1353    pub pb16: CudaSlice<u8>,
1354    pub o: CudaSlice<f32>,
1355    pub t: usize,
1356    pub nc: usize,
1357}
1358
1359/// STAGE-2 GROUPED DECODE: the 8 routed-expert weights by value (CUDA `f32x8_t`).
1360#[repr(C)]
1361#[derive(Clone, Copy)]
1362pub struct F32x8(pub [f32; 8]);
1363unsafe impl cudarc::driver::DeviceRepr for F32x8 {}
1364
1365/// Harness timing contract: wall nanos of the LAST generate/generate_spec prompt prime on this
1366/// process. Bench binaries read it right after the call to print gen-only throughput without the
1367/// prime-subtraction hack (which amplifies prime jitter into the gen number at long prompts).
1368pub static PRIME_NANOS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1369
1370/// RAII guard from `Engine::exact_scope`: restores the pre-scope `verify_exact` value on
1371/// drop, so error propagation (`?`) can never leave the engine latched in the
1372/// decode-exact matmul program (hermes finding, fixed 2026-08-23). Holds the flag, not
1373/// the Engine, so the restoration contract is unit-testable without a GPU.
1374#[must_use = "dropping immediately ends the exact scope"]
1375pub struct ExactScope<'a> {
1376    flag: &'a std::sync::atomic::AtomicBool,
1377    prev: bool,
1378}
1379
1380impl<'a> ExactScope<'a> {
1381    pub(crate) fn set(flag: &'a std::sync::atomic::AtomicBool, on: bool) -> Self {
1382        let prev = flag.load(std::sync::atomic::Ordering::Relaxed);
1383        flag.store(on, std::sync::atomic::Ordering::Relaxed);
1384        ExactScope { flag, prev }
1385    }
1386}
1387
1388impl Drop for ExactScope<'_> {
1389    fn drop(&mut self) {
1390        self.flag
1391            .store(self.prev, std::sync::atomic::Ordering::Relaxed);
1392    }
1393}
1394
1395#[cfg(test)]
1396mod exact_scope_tests {
1397    use std::sync::atomic::{AtomicBool, Ordering};
1398
1399    #[test]
1400    fn error_path_restores_verify_exact() {
1401        // TOOTH (hermes finding, fixed 2026-08-23): dspark_spec_session_burst called
1402        // set_verify_exact(true)/(false) manually with `?`s in between — any error left
1403        // the engine latched in the decode-exact matmul program for every later request.
1404        // The RAII scope must restore across an error propagation.
1405        let flag = AtomicBool::new(false);
1406        let failing = |flag: &AtomicBool| -> Result<(), &'static str> {
1407            let _scope = super::ExactScope::set(flag, true);
1408            assert!(flag.load(Ordering::Relaxed), "scope arms the flag");
1409            Err("draft forward failed")? // the `?` exit the manual pair leaked on
1410        };
1411        assert!(failing(&flag).is_err());
1412        assert!(
1413            !flag.load(Ordering::Relaxed),
1414            "error propagation must restore the pre-scope value"
1415        );
1416        // Nested/previous-value contract: a scope entered while already ON restores ON.
1417        let flag = AtomicBool::new(true);
1418        {
1419            let _scope = super::ExactScope::set(&flag, true);
1420        }
1421        assert!(flag.load(Ordering::Relaxed));
1422        // Early drop ends the scope exactly where the manual `false` used to sit.
1423        let flag = AtomicBool::new(false);
1424        let scope = super::ExactScope::set(&flag, true);
1425        drop(scope);
1426        assert!(!flag.load(Ordering::Relaxed));
1427    }
1428}
1429
1430impl Engine {
1431    pub fn new(ordinal: usize) -> Result<Self, Box<dyn std::error::Error>> {
1432        let gpu = memra_runtime::Gpu::new(ordinal)?;
1433        // ARCH GUARD (unified dual-arch engine): the fatbins carry single-arch SASS, so a
1434        // binary/device mismatch otherwise dies at first module load with an opaque CUDA
1435        // error. Fail early with the rebuild hint instead. MEMRA_ARCH_CHECK=0 skips.
1436        if std::env::var("MEMRA_ARCH_CHECK").as_deref() != Ok("0") {
1437            use cudarc::driver::sys::CUdevice_attribute_enum as A;
1438            let (maj, min) = cudarc::driver::result::device::get(ordinal as i32)
1439                .and_then(|d| unsafe {
1440                    Ok((
1441                        cudarc::driver::result::device::get_attribute(
1442                            d,
1443                            A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
1444                        )?,
1445                        cudarc::driver::result::device::get_attribute(
1446                            d,
1447                            A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
1448                        )?,
1449                    ))
1450                })
1451                .unwrap_or((0, 0));
1452            let built = env!("MEMRA_BUILT_CUDA_ARCH");
1453            let ok = matches!(
1454                (built, maj, min),
1455                ("120a", 12, 0) | ("120a", 12, 1) | ("100a", 10, 0) | ("90a", 9, 0) | ("89", 8, 9)
1456            );
1457            if !ok {
1458                return Err(format!(
1459                    "memra was built for sm_{built} but device {ordinal} reports compute \
1460                     capability {maj}.{min}. Rebuild on this machine (MEMRA_CUDA_ARCH \
1461                     auto-detects the GPU) or set MEMRA_ARCH_CHECK=0 to bypass."
1462                )
1463                .into());
1464            }
1465        }
1466        // Default async-pool RELEASE_THRESHOLD is 0: freed blocks return to the OS at every
1467        // sync, so cuMemAllocAsync NODES inside captured graphs re-map memory on EVERY
1468        // cuGraphLaunch (measured 226us/launch on the gemma graph door, 2026-07-23 osrt).
1469        // Pinning the threshold keeps the pool cached -> alloc nodes become pointer bumps.
1470        unsafe {
1471            use cudarc::driver::sys;
1472            let dev: sys::CUdevice = ordinal as sys::CUdevice;
1473            let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1474            if sys::cuDeviceGetDefaultMemPool(&mut pool, dev) == sys::CUresult::CUDA_SUCCESS {
1475                let mut thresh: u64 = u64::MAX;
1476                let _ = sys::cuMemPoolSetAttribute(
1477                    pool,
1478                    sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
1479                    &mut thresh as *mut u64 as *mut core::ffi::c_void,
1480                );
1481            }
1482        }
1483        let module = gpu.ctx.load_module(Ptx::from_binary(FATBIN.to_vec()))?;
1484        let hybrid = gpu
1485            .ctx
1486            .load_module(Ptx::from_binary(HYBRID_FATBIN.to_vec()))?;
1487        let qmatvec = gpu
1488            .ctx
1489            .load_module(Ptx::from_binary(QMATVEC_FATBIN.to_vec()))?;
1490        let flash = gpu
1491            .ctx
1492            .load_module(Ptx::from_binary(flash_fatbin_bytes().to_vec()))?;
1493        let gemm = gpu
1494            .ctx
1495            .load_module(Ptx::from_binary(gemm_fatbin_bytes().into_owned()))?;
1496        let router = gpu
1497            .ctx
1498            .load_module(Ptx::from_binary(ROUTER_FATBIN.to_vec()))?;
1499        let sample = gpu
1500            .ctx
1501            .load_module(Ptx::from_binary(SAMPLE_FATBIN.to_vec()))?;
1502        let copy_stream = gpu.ctx.new_stream()?;
1503        // DECODE EVENT-TRACKING ELISION — DEFAULT ON (2026-07-05; MEMRA_EVT=1 = escape hatch).
1504        // cudarc is in multi-stream mode (main stream +
1505        // copy_stream are both created streams), so with tracking on EVERY launch arg records a
1506        // read/write CudaEvent and inserts cuStreamWaitEvent on prior events. On the 35B MoE decode
1507        // that is ~19k cuStreamWaitEvent + ~9k cuEventRecord + ~6k event create/destroy per token
1508        // (~7 ms/tok host time, measured nsys 2026-07-04 rtx6000), and +4.6% measured on 27B decode —
1509        // protecting NOTHING: every hot-path kernel/memcpy runs on the ONE gpu.stream.
1510        // CROSS-STREAM HAZARD AUDIT: MoeSlotCache in-memory prefetch uses copy_stream. Every
1511        // overwrite explicitly records the prior compute point and makes copy_stream wait; every
1512        // consumer explicitly waits for the copy completion event. The opt-in positioned-read
1513        // proof stays on gpu.stream and retains an explicit event solely to guard pinned-source
1514        // reuse. Graph-capture sites use only gpu.stream, so these handoffs never rely on cudarc's
1515        // implicit event tracking.
1516        // SAFETY: single-stream ordering is total; the runtime mem-pool is configured with
1517        // internal-dependency reuse (memra-runtime), so alloc reuse is stream-ordered too.
1518        if std::env::var("MEMRA_EVT")
1519            .map(|v| v == "1")
1520            .unwrap_or(false)
1521        {
1522            // escape hatch: keep cudarc's implicit cross-stream event tracking.
1523        } else {
1524            unsafe {
1525                gpu.ctx.disable_event_tracking();
1526            }
1527        }
1528        Ok(Self {
1529            gpu,
1530            module,
1531            hybrid,
1532            qmatvec,
1533            flash,
1534            flash_g: std::sync::OnceLock::new(),
1535            gemm,
1536            router,
1537            sample,
1538            moe_cache: Mutex::new(None),
1539            w8_mirrors: Mutex::new(std::collections::HashMap::new()),
1540            w8_act: Mutex::new(std::collections::HashMap::new()),
1541            moe_cache_layout: Mutex::new(None),
1542            copy_stream,
1543            capture_keep_on: std::sync::atomic::AtomicBool::new(false),
1544            verify_exact: std::sync::atomic::AtomicBool::new(false),
1545            capture_keep: Mutex::new(Vec::new()),
1546            argmax_partials: Mutex::new(None),
1547            prime_deqw_ws: Mutex::new(None),
1548            router_stage: Mutex::new(None),
1549            fp8_scratch: Mutex::new(None),
1550            fa_vf16_scratch: Mutex::new(None),
1551            fa_part_pool: Mutex::new(None),
1552            fa_part_retired: Mutex::new(Vec::new()),
1553            fn_cache: Mutex::new(Default::default()),
1554            f16_scratch: Mutex::new(None),
1555            #[cfg(memra_cutlass)]
1556            cutlass_scratch: Mutex::new(None),
1557        })
1558    }
1559
1560    pub fn ctx(&self) -> &Arc<CudaContext> {
1561        &self.gpu.ctx
1562    }
1563
1564    /// Bytes the async pool holds MAPPED but NOT LIVE (reserved - used), i.e. freed blocks
1565    /// parked in the pool because `Engine::new` pins RELEASE_THRESHOLD to u64::MAX above.
1566    ///
1567    /// Why this is a public engine surface: `mem_get_info`'s `free` DOES NOT SEE these bytes —
1568    /// they are mapped to this process, so `free` counts them as gone, yet the very next
1569    /// `alloc_u8` is satisfied from them without touching `free` at all. Any admission or
1570    /// budget decision that reads `free` alone therefore under-counts real headroom by exactly
1571    /// this amount. Effective allocatable headroom is `free + pool_cached_bytes()`.
1572    ///
1573    /// MEASURED SIZE (c=64 serve burst, 9B NVFP4 + draft, 24GB card, 2026-08-06): 34-89 MB
1574    /// during the burst — SMALL. The admission gate adds it because a term that can only ever
1575    /// under-count headroom does not belong in a gate that queues real work, but the honest
1576    /// reading of this number is that pool caching is NOT where a long-running server's VRAM
1577    /// hides on this path: reserved ~= used throughout, so the memory the driver reports as
1578    /// gone is genuinely LIVE (see `pool_reserved_used` for the diagnostic pair).
1579    ///
1580    /// Returns 0 if the pool cannot be queried (never a false-positive headroom claim).
1581    pub fn pool_cached_bytes(&self) -> usize {
1582        let (reserved, used) = self.pool_reserved_used();
1583        reserved.saturating_sub(used)
1584    }
1585
1586    /// Bytes the driver's per-device CUDA GRAPH memory pool currently holds RESERVED
1587    /// (`cuDeviceGetGraphMemAttribute` RESERVED_MEM_CURRENT) — the backing store of every
1588    /// captured alloc node, which on this engine means the dspark verify-graph pool
1589    /// (decode/step graphs bake pre-allocated buffers and own no alloc nodes). This memory
1590    /// is DISTINCT from the async pool above: `mem_get_info`'s `free` already excludes it,
1591    /// it is never released back (the vgraph pool has no eviction by design), and it GROWS
1592    /// as new (segment, vt)/(vt, rung, hi) keys capture — the growth is what
1593    /// `dspark_vg_admission_debt` charges at admission. Returns 0 if the attribute cannot
1594    /// be queried (never a false headroom claim, matching `pool_cached_bytes`).
1595    pub fn device_graph_mem_reserved(&self) -> usize {
1596        use cudarc::driver::sys as cus;
1597        let Ok(dev) = cudarc::driver::result::device::get(self.gpu.ctx.ordinal() as i32) else {
1598            return 0;
1599        };
1600        let mut bytes: u64 = 0;
1601        let rc = unsafe {
1602            cus::cuDeviceGetGraphMemAttribute(
1603                dev,
1604                cus::CUgraphMem_attribute::CU_GRAPH_MEM_ATTR_RESERVED_MEM_CURRENT,
1605                &mut bytes as *mut u64 as *mut std::ffi::c_void,
1606            )
1607        };
1608        if rc == cus::cudaError_enum::CUDA_SUCCESS {
1609            bytes as usize
1610        } else {
1611            0
1612        }
1613    }
1614
1615    /// Raw async-pool occupancy: (RESERVED_MEM_CURRENT, USED_MEM_CURRENT) in bytes. Reserved is
1616    /// what the pool has mapped from the driver; used is what is live inside it. Exposed for
1617    /// admission/VRAM diagnostics — the pair distinguishes "memory is parked in the pool and
1618    /// `free` cannot see it" (reserved >> used) from "memory is genuinely held live by some
1619    /// owner" (reserved ~= used), which are opposite bugs with opposite fixes.
1620    /// (0, 0) if the pool cannot be queried.
1621    /// Release every CACHED (freed-but-retained) block of the default async mempool
1622    /// back to the driver (deploy-headroom lane, 2026-08-27). The boot-time
1623    /// RELEASE_THRESHOLD=u64::MAX pin keeps freed blocks cached for graph-launch speed,
1624    /// which is right for steady serving and wrong at a blue/green overlap: a green
1625    /// PROCESS cannot use blue's cached pool. cuMemPoolTrimTo(0) frees only unused
1626    /// blocks — live allocations are untouched; later allocs re-map once. Returns the
1627    /// bytes released (reserved delta), 0 if the pool cannot be queried.
1628    pub fn pool_trim_to_zero(&self) -> usize {
1629        use cudarc::driver::sys;
1630        let (before, _) = self.pool_reserved_used();
1631        unsafe {
1632            let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1633            if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1634                != sys::CUresult::CUDA_SUCCESS
1635            {
1636                return 0;
1637            }
1638            let _ = sys::cuMemPoolTrimTo(pool, 0);
1639        }
1640        let (after, _) = self.pool_reserved_used();
1641        before.saturating_sub(after)
1642    }
1643
1644    pub fn pool_reserved_used(&self) -> (usize, usize) {
1645        use cudarc::driver::sys;
1646        unsafe {
1647            let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1648            if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1649                != sys::CUresult::CUDA_SUCCESS
1650            {
1651                return (0, 0);
1652            }
1653            let (mut reserved, mut used) = (0u64, 0u64);
1654            if sys::cuMemPoolGetAttribute(
1655                pool,
1656                sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,
1657                &mut reserved as *mut u64 as *mut core::ffi::c_void,
1658            ) != sys::CUresult::CUDA_SUCCESS
1659            {
1660                return (0, 0);
1661            }
1662            if sys::cuMemPoolGetAttribute(
1663                pool,
1664                sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_USED_MEM_CURRENT,
1665                &mut used as *mut u64 as *mut core::ffi::c_void,
1666            ) != sys::CUresult::CUDA_SUCCESS
1667            {
1668                return (0, 0);
1669            }
1670            (reserved as usize, used as usize)
1671        }
1672    }
1673
1674    /// Ambient stream (by value since M1-PP2 increment 2): the thread's pp2 stage stream
1675    /// when a stage scope is active, else the main compute stream — see `Gpu::stream`.
1676    pub fn stream(&self) -> Arc<CudaStream> {
1677        self.gpu.stream()
1678    }
1679    /// FP8-GLOBALS switch (MEMRA_GEMMA_GKV, default ON): gemma global (hd512) layers keep
1680    /// their KV in e4m3 — the dequant-latency arc (HANDOVER). Windowed layers stay q8_0/q5_1.
1681    pub fn gkv_on() -> bool {
1682        memra_kv::gkv_on()
1683    }
1684
1685    /// FP8-WINDOWED switch (MEMRA_GEMMA_WKV — measured 2026-07-12 in a validity-gated
1686    /// window: 1.7k 174.1-174.4 vs 168.6-169.4 default (+3%), 4.9k 158.7-160.4; vs llama
1687    /// same-window 159.5-160.2 / 140.6 = 1.09x / 1.13x): gemma windowed (hd256 SWA)
1688    /// layers hold e4m3 KV and ride the format-aware v4 lane from the kf8vf8 module.
1689    /// SERVING-MODE DEFAULT (2026-07-12, the 31B spec unlock): fp8-windowed KV GUTS the
1690    /// MTP drafter's acceptance — its single swa attention reads the windowed cache and
1691    /// e4m3 noise flips its argmaxes (31B short accept .758 -> 1.000 with q8/q5, spec 88
1692    /// -> 122.7 vs llama-mtp 112; depth .59 -> .78; 26B depth .57 -> .89). So the default
1693    /// keys on serving intent: SPEC serving (MEMRA_DRAFT set) -> OFF, plain -> ON (its
1694    /// depth-plain +3% stands). Explicit MEMRA_GEMMA_WKV always wins. GKV (globals) stays
1695    /// ON for both — no acceptance cost measured.
1696    pub fn wkv_on() -> bool {
1697        memra_kv::wkv_on()
1698    }
1699
1700    /// QWEN FP8-KV switch (MEMRA_KV_FP8 explicit; else the per-model KV_FP8_FORCE door set
1701    /// at model load; else OFF). Non-gemma full-attn layers hold e4m3 K/V via the kf8vf8
1702    /// module. Per-model verdict 2026-07-12: 9B +0.7-4% scaling with depth, 27B flat,
1703    /// 35B −2% (fp8 format-gates its v3 dp4a lane) — so the 9B class defaults ON
1704    /// (adopted 2026-07-28 with the deferred acceptance battery), others stay OFF.
1705    pub fn kv_fp8_on() -> bool {
1706        memra_kv::kv_fp8_on()
1707    }
1708
1709    /// fa kernel routed by head_dim: hd512 (gemma globals) resolves from the kf8vf8 module
1710    /// when the fp8-globals arm is on; everything else from the default flash module.
1711    fn fa_func(&self, name: &str, head_dim: usize) -> CudaFunction {
1712        if head_dim == 512 && Self::gkv_on() {
1713            self.func_g(name)
1714        } else {
1715            self.func(name)
1716        }
1717    }
1718
1719    /// Kernel from the FP8-GLOBALS (kf8vf8) flash module — gemma global-layer arm only.
1720    /// Format-AGNOSTIC kernels (e.g. fa_decode_combine_f32) are not compiled into the
1721    /// per-format fatbins; fall back to the base modules for those.
1722    fn func_g(&self, name: &str) -> CudaFunction {
1723        let m = self.flash_g.get_or_init(|| {
1724            self.gpu
1725                .ctx
1726                .load_module(cudarc::nvrtc::Ptx::from_binary(
1727                    FLASH_FATBIN_KF8VF8.to_vec(),
1728                ))
1729                .expect("load kf8vf8 flash fatbin (fp8-globals arm)")
1730        });
1731        let key = format!("g:{name}");
1732        if let Some(f) = self.fn_cache.lock().unwrap().get(&key) {
1733            return f.clone();
1734        }
1735        let f = match m.load_function(name) {
1736            Ok(f) => f,
1737            Err(_) => self.func(name),
1738        };
1739        self.fn_cache.lock().unwrap().insert(key, f.clone());
1740        f
1741    }
1742
1743    fn func(&self, name: &str) -> CudaFunction {
1744        // Resolution cache: cuModuleGetFunction fails inside a CUDA-graph capture region,
1745        // so capture-time lookups MUST be host-memory hits (warmups populate the cache).
1746        if let Some(f) = self.fn_cache.lock().unwrap().get(name) {
1747            return f.clone();
1748        }
1749        let f = self
1750            .module
1751            .load_function(name)
1752            .or_else(|_| self.hybrid.load_function(name))
1753            .or_else(|_| self.qmatvec.load_function(name))
1754            .or_else(|_| self.flash.load_function(name))
1755            .or_else(|_| self.gemm.load_function(name))
1756            .or_else(|_| self.router.load_function(name))
1757            .or_else(|_| self.sample.load_function(name))
1758            .unwrap_or_else(|_| panic!("kernel {name} not in any fatbin"));
1759        self.fn_cache
1760            .lock()
1761            .unwrap()
1762            .insert(name.to_string(), f.clone());
1763        f
1764    }
1765
1766    /// Scatter trimmed draft logits into full-vocab space: dst = -inf everywhere, then
1767    /// dst[d2t[i]] = src[i]. Two launches (fill, scatter) — no grid-wide sync needed.
1768    pub fn scatter_trim_logits(
1769        &self,
1770        src: &CudaSlice<f32>,
1771        d2t: &CudaSlice<u32>,
1772        dst: &mut CudaSlice<f32>,
1773        d_vocab: usize,
1774        n_vocab: usize,
1775    ) -> Result<(), Box<dyn std::error::Error>> {
1776        let f1 = self.func("scatter_trim_logits_f32");
1777        let f2 = self.func("scatter_trim_logits_pass2_f32");
1778        let (dv, nv) = (d_vocab as i32, n_vocab as i32);
1779        let cfg1 = LaunchConfig {
1780            grid_dim: (256, 1, 1),
1781            block_dim: (256, 1, 1),
1782            shared_mem_bytes: 0,
1783        };
1784        let __s_b1 = self.gpu.stream();
1785        let mut b1 = __s_b1.launch_builder(&f1);
1786        b1.arg(src).arg(d2t).arg(&mut *dst).arg(&dv).arg(&nv);
1787        unsafe {
1788            b1.launch(cfg1)?;
1789        }
1790        let cfg2 = LaunchConfig {
1791            grid_dim: (d_vocab.div_ceil(256) as u32, 1, 1),
1792            block_dim: (256, 1, 1),
1793            shared_mem_bytes: 0,
1794        };
1795        let __s_b2 = self.gpu.stream();
1796        let mut b2 = __s_b2.launch_builder(&f2);
1797        b2.arg(src).arg(d2t).arg(&mut *dst).arg(&dv);
1798        unsafe {
1799            b2.launch(cfg2)?;
1800        }
1801        Ok(())
1802    }
1803
1804    // ---- FILTERED-SPEC (feat/filtered-spec): top-k/p/min-p transforms applied symmetrically
1805    // to p and q — rejection sampling stays distribution-exact for the filtered target. ----
1806
1807    /// Per-row filtered-softmax stats: out[r] = (threshold_e, renorm_mass_e, row_max) for the
1808    /// filter (top_k, top_p, min_p) at `temp`. Rows index into x with row_stride f32s.
1809    #[allow(clippy::too_many_arguments)]
1810    pub fn filter_stats(
1811        &self,
1812        x: &CudaSlice<f32>,
1813        row_stride: usize,
1814        rows: &CudaSlice<i32>,
1815        out_th: &mut CudaSlice<f32>,
1816        out_z: &mut CudaSlice<f32>,
1817        out_max: &mut CudaSlice<f32>,
1818        n: usize,
1819        nrow: usize,
1820        temp: f32,
1821        top_k: i32,
1822        top_p: f32,
1823        min_p: f32,
1824    ) -> Result<(), Box<dyn std::error::Error>> {
1825        // A top-K-selection form of this kernel (3 vocab passes vs the search's ~51) was
1826        // implemented and REFUTED on 2026-08-21 (lane/moebatch-q35moe): the 248k-vocab row is
1827        // L2-resident, so the extra passes are near-free while the per-thread selection list
1828        // spills to local memory — B=8 tick 12.8/11.2 ms (cap 64/32) vs 10.4 ms for this
1829        // kernel, and serve c8 agg ~648 vs ~666. The receipts row is the record; the real
1830        // filtered-sampling win was batching the per-row launches (decode_batch.rs).
1831        //
1832        // COOPERATIVE MULTI-BLOCK FORM (lane/samplat, 2026-08-21): the surviving inefficiency
1833        // was WIDTH, not passes — one block per row left ~94% of the device idle for ~620us
1834        // per B=8 serve tick (5.9% of the tick, box4 nsys receipt). filter_stats_coop_f32
1835        // splits each row across 16 blocks with grid-synced bisection totals — same algorithm,
1836        // slice-partial f32 sums (accepted device-sampling class; sample-check arbitrates).
1837        // MEMRA_FILTER_COOP=0 is the rollback seam to the single-block form.
1838        //
1839        // DETERMINISTIC KEYING (hermes finding, fixed 2026-08-23): the old admission
1840        // `16*nrow <= sm_count` fell back to the single-block program PER CALL when a tick
1841        // carried too many rows — and the two programs are NOT bit-identical (measured
1842        // ~1e-7 rel on the renorm mass: different f32 partial-sum order), so a request's
1843        // sampling threshold arithmetic depended on how many rows shared its serve tick.
1844        // Coop is now THE program on every coop-capable device: rows are CHUNKED to the
1845        // co-residency cap (sm_count/16 rows per cooperative launch) and each row's
1846        // arithmetic uses only its own 16 slices + its own ws region, so the per-row bits
1847        // are independent of batch width by construction — the kernel-check
1848        // FILTER-COOP-CHUNK cell pins exactly that. The single-block program remains only
1849        // behind the deployment-keyed seams: MEMRA_FILTER_COOP=0, or a device with
1850        // sm_count < 16 (fixed per device class, never per call).
1851        static COOP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1852        let coop_on =
1853            *COOP_ON.get_or_init(|| std::env::var("MEMRA_FILTER_COOP").as_deref() != Ok("0"));
1854        if coop_on && self.sm_count() >= 16 {
1855            let cap = self.sm_count() as usize / 16;
1856            let mut done = 0usize;
1857            while done < nrow {
1858                let chunk = cap.min(nrow - done);
1859                self.filter_stats_coop_chunk(
1860                    x, row_stride, rows, done, out_th, out_z, out_max, n, chunk, temp, top_k,
1861                    top_p, min_p,
1862                )?;
1863                done += chunk;
1864            }
1865            return Ok(());
1866        }
1867        self.filter_stats_plain_program(
1868            x, row_stride, rows, out_th, out_z, out_max, n, nrow, temp, top_k, top_p, min_p,
1869        )
1870    }
1871
1872    /// One cooperative `filter_stats` launch over rows `row0..row0+chunk` (pub so the
1873    /// kernel-check FILTER-COOP-CHUNK cell can pin batch-width independence directly).
1874    /// The kernel indexes `rows`/outputs by blockIdx.y, so the chunk is expressed as
1875    /// sub-views at `row0` — per-row arithmetic is untouched by the offset.
1876    #[allow(clippy::too_many_arguments)]
1877    pub fn filter_stats_coop_chunk(
1878        &self,
1879        x: &CudaSlice<f32>,
1880        row_stride: usize,
1881        rows: &CudaSlice<i32>,
1882        row0: usize,
1883        out_th: &mut CudaSlice<f32>,
1884        out_z: &mut CudaSlice<f32>,
1885        out_max: &mut CudaSlice<f32>,
1886        n: usize,
1887        chunk: usize,
1888        temp: f32,
1889        top_k: i32,
1890        top_p: f32,
1891        min_p: f32,
1892    ) -> Result<(), Box<dyn std::error::Error>> {
1893        let (ni, nr, rs) = (n as i32, chunk as i32, row_stride as i64);
1894        let f = self.func("filter_stats_coop_f32");
1895        let mut ws = self.alloc_uninit::<f32>(chunk * (2 * 16 + 2))?;
1896        let cfg = LaunchConfig {
1897            grid_dim: (16, chunk as u32, 1),
1898            block_dim: (512, 1, 1),
1899            shared_mem_bytes: 0,
1900        };
1901        let rows_v = rows.slice(row0..row0 + chunk);
1902        let mut th_v = out_th.slice_mut(row0..row0 + chunk);
1903        let mut z_v = out_z.slice_mut(row0..row0 + chunk);
1904        let mut mx_v = out_max.slice_mut(row0..row0 + chunk);
1905        let __s_b = self.gpu.stream();
1906        let mut b = __s_b.launch_builder(&f);
1907        b.arg(x)
1908            .arg(&rs)
1909            .arg(&rows_v)
1910            .arg(&mut th_v)
1911            .arg(&mut z_v)
1912            .arg(&mut mx_v)
1913            .arg(&mut ws)
1914            .arg(&ni)
1915            .arg(&nr)
1916            .arg(&temp)
1917            .arg(&top_k)
1918            .arg(&top_p)
1919            .arg(&min_p);
1920        unsafe {
1921            b.launch_cooperative(cfg)?;
1922        }
1923        Ok(())
1924    }
1925
1926    /// The single-block-per-row `filter_stats` program (the pre-coop form; the
1927    /// MEMRA_FILTER_COOP=0 rollback and the occupancy fallback). Gate-callable twin of
1928    /// `filter_stats_coop_program`.
1929    #[allow(clippy::too_many_arguments)]
1930    pub fn filter_stats_plain_program(
1931        &self,
1932        x: &CudaSlice<f32>,
1933        row_stride: usize,
1934        rows: &CudaSlice<i32>,
1935        out_th: &mut CudaSlice<f32>,
1936        out_z: &mut CudaSlice<f32>,
1937        out_max: &mut CudaSlice<f32>,
1938        n: usize,
1939        nrow: usize,
1940        temp: f32,
1941        top_k: i32,
1942        top_p: f32,
1943        min_p: f32,
1944    ) -> Result<(), Box<dyn std::error::Error>> {
1945        let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1946        let f = self.func("filter_stats_f32");
1947        let cfg = LaunchConfig {
1948            grid_dim: (nrow as u32, 1, 1),
1949            block_dim: (1024, 1, 1),
1950            shared_mem_bytes: 0,
1951        };
1952        let __s_b = self.gpu.stream();
1953        let mut b = __s_b.launch_builder(&f);
1954        b.arg(x)
1955            .arg(&rs)
1956            .arg(rows)
1957            .arg(&mut *out_th)
1958            .arg(&mut *out_z)
1959            .arg(&mut *out_max)
1960            .arg(&ni)
1961            .arg(&nr)
1962            .arg(&temp)
1963            .arg(&top_k)
1964            .arg(&top_p)
1965            .arg(&min_p);
1966        unsafe {
1967            b.launch(cfg)?;
1968        }
1969        Ok(())
1970    }
1971
1972    /// out[pair] = filtered-softmax prob of ids[pair] in row rows[pair] (th/z per PAIR).
1973    #[allow(clippy::too_many_arguments)]
1974    pub fn softmax_gather_filtered(
1975        &self,
1976        x: &CudaSlice<f32>,
1977        row_stride: usize,
1978        ids: &CudaSlice<u32>,
1979        rows: &CudaSlice<i32>,
1980        th: &CudaSlice<f32>,
1981        z: &CudaSlice<f32>,
1982        out: &mut CudaSlice<f32>,
1983        n: usize,
1984        npair: usize,
1985        temp: f32,
1986    ) -> Result<(), Box<dyn std::error::Error>> {
1987        let f = self.func("softmax_gather_filtered_f32");
1988        let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1989        let cfg = LaunchConfig {
1990            grid_dim: (npair as u32, 1, 1),
1991            block_dim: (256, 1, 1),
1992            shared_mem_bytes: 0,
1993        };
1994        let __s_b = self.gpu.stream();
1995        let mut b = __s_b.launch_builder(&f);
1996        b.arg(x)
1997            .arg(&rs)
1998            .arg(ids)
1999            .arg(rows)
2000            .arg(th)
2001            .arg(z)
2002            .arg(&mut *out)
2003            .arg(&ni)
2004            .arg(&np)
2005            .arg(&temp);
2006        unsafe {
2007            b.launch(cfg)?;
2008        }
2009        Ok(())
2010    }
2011
2012    /// Filtered residual sample: token ~ norm(max(0, fp - fq)) with fp/fq the filtered softmaxes.
2013    #[allow(clippy::too_many_arguments)]
2014    pub fn residual_sample_filtered(
2015        &self,
2016        p: &CudaSlice<f32>,
2017        q: Option<&CudaSlice<f32>>,
2018        n: usize,
2019        temp: f32,
2020        seed: u64,
2021        stream_pos: u32,
2022        p_stats: (f32, f32, f32),
2023        q_stats: (f32, f32, f32),
2024        out_tok: &mut CudaSlice<u32>,
2025    ) -> Result<(), Box<dyn std::error::Error>> {
2026        let f = self.func("residual_sample_filtered_f32");
2027        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2028        let has_q: i32 = q.is_some() as i32;
2029        let qbuf = q.unwrap_or(p);
2030        let (pm, pth, pz) = p_stats;
2031        let (qm, qth, qz) = q_stats;
2032        let cfg = LaunchConfig {
2033            grid_dim: (1, 1, 1),
2034            block_dim: (1024, 1, 1),
2035            shared_mem_bytes: 0,
2036        };
2037        let __s_b = self.gpu.stream();
2038        let mut b = __s_b.launch_builder(&f);
2039        b.arg(p)
2040            .arg(qbuf)
2041            .arg(&has_q)
2042            .arg(&ni)
2043            .arg(&temp)
2044            .arg(&slo)
2045            .arg(&shi)
2046            .arg(&stream_pos)
2047            .arg(&pm)
2048            .arg(&pth)
2049            .arg(&pz)
2050            .arg(&qm)
2051            .arg(&qth)
2052            .arg(&qz)
2053            .arg(&mut *out_tok);
2054        unsafe {
2055            b.launch(cfg)?;
2056        }
2057        Ok(())
2058    }
2059
2060    /// Sparse-q filtered residual sample: token ~ norm(max(0, fp - q)) where fp is the
2061    /// FILTERED softmax of `p` (stats from `filter_stats`) and q is a PROBABILITY vector
2062    /// supported on `cand_ids` (<=32 ids — the DFlash2 selector's candidate-set proposal;
2063    /// lane/dspark-sampled-admission-20260820). Same event semantics/Philox tag as
2064    /// `residual_sample_filtered` — one uniform per (seed, stream_pos).
2065    #[allow(clippy::too_many_arguments)]
2066    pub fn residual_sample_sparse_q(
2067        &self,
2068        p: &CudaSlice<f32>,
2069        cand_ids: &CudaSlice<u32>,
2070        q_probs: &CudaSlice<f32>,
2071        n_cand: usize,
2072        n: usize,
2073        temp: f32,
2074        seed: u64,
2075        stream_pos: u32,
2076        p_stats: (f32, f32, f32),
2077        out_tok: &mut CudaSlice<u32>,
2078    ) -> Result<(), Box<dyn std::error::Error>> {
2079        assert!(
2080            n_cand >= 1 && n_cand <= 32,
2081            "residual_sample_sparse_q supports 1..=32 candidates, got {n_cand}"
2082        );
2083        let f = self.func("residual_sample_sparse_q_f32");
2084        let (ni, nc) = (n as i32, n_cand as i32);
2085        let (slo, shi) = ((seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2086        let (pm, pth, pz) = p_stats;
2087        let cfg = LaunchConfig {
2088            grid_dim: (1, 1, 1),
2089            block_dim: (1024, 1, 1),
2090            shared_mem_bytes: 0,
2091        };
2092        let __s_b = self.gpu.stream();
2093        let mut b = __s_b.launch_builder(&f);
2094        b.arg(p)
2095            .arg(cand_ids)
2096            .arg(q_probs)
2097            .arg(&nc)
2098            .arg(&ni)
2099            .arg(&temp)
2100            .arg(&slo)
2101            .arg(&shi)
2102            .arg(&stream_pos)
2103            .arg(&pm)
2104            .arg(&pth)
2105            .arg(&pz)
2106            .arg(&mut *out_tok);
2107        unsafe {
2108            b.launch(cfg)?;
2109        }
2110        Ok(())
2111    }
2112
2113    /// Gumbel-max draw from the FILTERED distribution (masked perturb; argmax after).
2114    #[allow(clippy::too_many_arguments)]
2115    pub fn gumbel_perturb_filtered(
2116        &self,
2117        x: &CudaSlice<f32>,
2118        y: &mut CudaSlice<f32>,
2119        n: usize,
2120        seed: u64,
2121        stream_pos: u32,
2122        temp: f32,
2123        row_max: f32,
2124        th: f32,
2125    ) -> Result<(), Box<dyn std::error::Error>> {
2126        let f = self.func("gumbel_perturb_filtered_f32");
2127        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2128        let cfg = LaunchConfig {
2129            grid_dim: (n.div_ceil(256) as u32, 1, 1),
2130            block_dim: (256, 1, 1),
2131            shared_mem_bytes: 0,
2132        };
2133        let __s_b = self.gpu.stream();
2134        let mut b = __s_b.launch_builder(&f);
2135        b.arg(x)
2136            .arg(&mut *y)
2137            .arg(&ni)
2138            .arg(&slo)
2139            .arg(&shi)
2140            .arg(&stream_pos)
2141            .arg(&temp)
2142            .arg(&row_max)
2143            .arg(&th);
2144        unsafe {
2145            b.launch(cfg)?;
2146        }
2147        Ok(())
2148    }
2149
2150    /// Keskar penalties applied IN PLACE to a logits buffer: history token ids get
2151    /// rep-divided/multiplied + freq*count + presence subtracted. Symmetric p/q usage keeps
2152    /// filtered rejection sampling exact for the penalized target.
2153    #[allow(clippy::too_many_arguments)]
2154    pub fn penalize_logits(
2155        &self,
2156        x: &mut CudaSlice<f32>,
2157        hist: &CudaSlice<u32>,
2158        n_hist: usize,
2159        rep: f32,
2160        freq: f32,
2161        present: f32,
2162        n: usize,
2163    ) -> Result<(), Box<dyn std::error::Error>> {
2164        if n_hist == 0 {
2165            return Ok(());
2166        }
2167        let f = self.func("penalize_logits_f32");
2168        let (nh, ni) = (n_hist as i32, n as i32);
2169        let cfg = LaunchConfig {
2170            grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
2171            block_dim: (128, 1, 1),
2172            shared_mem_bytes: 0,
2173        };
2174        let __s_b = self.gpu.stream();
2175        let mut b = __s_b.launch_builder(&f);
2176        b.arg(&mut *x)
2177            .arg(hist)
2178            .arg(&nh)
2179            .arg(&rep)
2180            .arg(&freq)
2181            .arg(&present)
2182            .arg(&ni);
2183        unsafe {
2184            b.launch(cfg)?;
2185        }
2186        Ok(())
2187    }
2188
2189    /// Rows variant: penalize `nrow` contiguous rows of length n in one launch.
2190    #[allow(clippy::too_many_arguments)]
2191    pub fn penalize_logits_rows(
2192        &self,
2193        x: &mut CudaSlice<f32>,
2194        hist: &CudaSlice<u32>,
2195        n_hist: usize,
2196        rep: f32,
2197        freq: f32,
2198        present: f32,
2199        n: usize,
2200        nrow: usize,
2201    ) -> Result<(), Box<dyn std::error::Error>> {
2202        if n_hist == 0 || nrow == 0 {
2203            return Ok(());
2204        }
2205        let f = self.func("penalize_logits_rows_f32");
2206        let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
2207        let cfg = LaunchConfig {
2208            grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
2209            block_dim: (128, 1, 1),
2210            shared_mem_bytes: 0,
2211        };
2212        let __s_b = self.gpu.stream();
2213        let mut b = __s_b.launch_builder(&f);
2214        b.arg(&mut *x)
2215            .arg(hist)
2216            .arg(&nh)
2217            .arg(&rep)
2218            .arg(&freq)
2219            .arg(&present)
2220            .arg(&ni)
2221            .arg(&nr);
2222        unsafe {
2223            b.launch(cfg)?;
2224        }
2225        Ok(())
2226    }
2227
2228    /// Heterogeneous serving-batch penalties over host-maintained sparse window counts.
2229    /// `offsets[r]..offsets[r+1]` indexes the unique positive-count `(id,count)` entries for logits row
2230    /// `rows[r]`; each row may carry independent repetition/frequency/presence coefficients.
2231    /// One thread owns one distinct logit, so the kernel needs neither atomics nor the
2232    /// history-squared dedup scan used by the speculative raw-history oracle.
2233    #[allow(clippy::too_many_arguments)]
2234    pub fn penalize_logits_sparse_rows(
2235        &self,
2236        x: &mut CudaSlice<f32>,
2237        ids: &[u32],
2238        counts: &[u32],
2239        offsets: &[i32],
2240        rows: &[i32],
2241        reps: &[f32],
2242        freqs: &[f32],
2243        presents: &[f32],
2244        n: usize,
2245    ) -> Result<(), Box<dyn std::error::Error>> {
2246        let nrow = rows.len();
2247        if nrow == 0 {
2248            return Ok(());
2249        }
2250        let _ni = i32::try_from(n).map_err(|_| "sparse penalty logits width must fit CUDA i32")?;
2251        let _nr = i32::try_from(nrow).map_err(|_| "sparse penalty row count must fit CUDA i32")?;
2252        let entry_count =
2253            i32::try_from(ids.len()).map_err(|_| "sparse penalty entry count must fit CUDA i32")?;
2254        if ids.len() != counts.len()
2255            || offsets.len() != nrow + 1
2256            || reps.len() != nrow
2257            || freqs.len() != nrow
2258            || presents.len() != nrow
2259            || offsets.first().copied() != Some(0)
2260            || offsets.last().copied() != Some(entry_count)
2261        {
2262            return Err("sparse penalty row metadata shape mismatch".into());
2263        }
2264        if counts.contains(&0) {
2265            return Err("sparse penalty counts must be positive".into());
2266        }
2267        let mut max_len = 0usize;
2268        for pair in offsets.windows(2) {
2269            if pair[0] < 0 || pair[1] < pair[0] {
2270                return Err("sparse penalty offsets must be monotonic".into());
2271            }
2272            max_len = max_len.max((pair[1] - pair[0]) as usize);
2273        }
2274        if max_len == 0 {
2275            return Ok(());
2276        }
2277
2278        let mut seen = std::collections::HashSet::with_capacity(ids.len());
2279        for (r, &row) in rows.iter().enumerate() {
2280            if row < 0 || (row as usize + 1).saturating_mul(n) > x.len() {
2281                return Err("sparse penalty row index exceeds logits shape".into());
2282            }
2283            let begin = offsets[r] as usize;
2284            let end = offsets[r + 1] as usize;
2285            for &id in &ids[begin..end] {
2286                if id as usize >= n {
2287                    return Err("sparse penalty token id exceeds logits row".into());
2288                }
2289                if !seen.insert((row, id)) {
2290                    return Err("sparse penalty entries must be unique per logits row".into());
2291                }
2292            }
2293        }
2294
2295        // SAFETY: the checks above establish every invariant of the launch-only helper.
2296        unsafe {
2297            self.penalize_logits_sparse_rows_unchecked(
2298                x, ids, counts, offsets, rows, reps, freqs, presents, n,
2299            )
2300        }
2301    }
2302
2303    /// Launch-only form for the serving hot path, whose `HashMap`-backed producer already
2304    /// guarantees unique ids and whose rows are enumerated from the live batch.
2305    ///
2306    /// # Safety
2307    ///
2308    /// Shapes must match the safe wrapper, offsets must be monotonic and in bounds, every row
2309    /// must index `x`, and each `(row,id)` pair must occur at most once. Token ids outside the
2310    /// logits row are safe no-ops because the kernel bounds-checks them before computing `x`.
2311    #[allow(clippy::too_many_arguments)]
2312    pub(crate) unsafe fn penalize_logits_sparse_rows_unchecked(
2313        &self,
2314        x: &mut CudaSlice<f32>,
2315        ids: &[u32],
2316        counts: &[u32],
2317        offsets: &[i32],
2318        rows: &[i32],
2319        reps: &[f32],
2320        freqs: &[f32],
2321        presents: &[f32],
2322        n: usize,
2323    ) -> Result<(), Box<dyn std::error::Error>> {
2324        let nrow = rows.len();
2325        if nrow == 0 {
2326            return Ok(());
2327        }
2328        let max_len = offsets
2329            .windows(2)
2330            .map(|pair| (pair[1] - pair[0]) as usize)
2331            .max()
2332            .unwrap_or(0);
2333        if max_len == 0 {
2334            return Ok(());
2335        }
2336        let ids_d = self.htod_u32_v(ids)?;
2337        let counts_d = self.htod_u32_v(counts)?;
2338        let offsets_d = self.htod_i32(offsets)?;
2339        let rows_d = self.htod_i32(rows)?;
2340        let reps_d = self.htod(reps)?;
2341        let freqs_d = self.htod(freqs)?;
2342        let presents_d = self.htod(presents)?;
2343        let f = self.func("penalize_logits_sparse_rows_f32");
2344        let ni = i32::try_from(n).map_err(|_| "sparse penalty logits width must fit CUDA i32")?;
2345        let nr = i32::try_from(nrow).map_err(|_| "sparse penalty row count must fit CUDA i32")?;
2346        let cfg = LaunchConfig {
2347            grid_dim: (max_len.div_ceil(128) as u32, nrow as u32, 1),
2348            block_dim: (128, 1, 1),
2349            shared_mem_bytes: 0,
2350        };
2351        let __s_b = self.gpu.stream();
2352        let mut b = __s_b.launch_builder(&f);
2353        b.arg(&mut *x)
2354            .arg(&ids_d)
2355            .arg(&counts_d)
2356            .arg(&offsets_d)
2357            .arg(&rows_d)
2358            .arg(&reps_d)
2359            .arg(&freqs_d)
2360            .arg(&presents_d)
2361            .arg(&ni)
2362            .arg(&nr);
2363        unsafe {
2364            b.launch(cfg)?;
2365        }
2366        Ok(())
2367    }
2368
2369    /// ROW-INCREMENTAL penalties (dspark penalized-sampled admission): row r of `x`
2370    /// penalizes over the last `min(win, n_hist0 + r)` entries of `hist[..n_hist0 + r]`,
2371    /// where `hist` = [session window (n_hist0) ++ per-row drafted tokens (nrow-1)]. This
2372    /// is the within-round evolving penalty state block drafting needs: verify row r's
2373    /// target is penalized by every token committed before it INCLUDING same-round
2374    /// accepts — `penalize_logits_rows` (one shared window) is the frozen-window
2375    /// approximation this exists to replace on the dspark route.
2376    #[allow(clippy::too_many_arguments)]
2377    pub fn penalize_logits_rows_inc(
2378        &self,
2379        x: &mut CudaSlice<f32>,
2380        hist: &CudaSlice<u32>,
2381        n_hist0: usize,
2382        rep: f32,
2383        freq: f32,
2384        present: f32,
2385        n: usize,
2386        nrow: usize,
2387        win: usize,
2388    ) -> Result<(), Box<dyn std::error::Error>> {
2389        if nrow == 0 || win == 0 || (n_hist0 == 0 && nrow == 1) {
2390            return Ok(());
2391        }
2392        debug_assert!(
2393            hist.len() >= n_hist0 + nrow - 1,
2394            "rows-inc hist must carry n_hist0 + nrow - 1 ids"
2395        );
2396        let f = self.func("penalize_logits_rows_inc_f32");
2397        let max_len = win.min(n_hist0 + nrow - 1).max(1);
2398        let (nh, ni, nr, wi) = (n_hist0 as i32, n as i32, nrow as i32, win as i32);
2399        let cfg = LaunchConfig {
2400            grid_dim: (max_len.div_ceil(128) as u32, nrow as u32, 1),
2401            block_dim: (128, 1, 1),
2402            shared_mem_bytes: 0,
2403        };
2404        let __s_b = self.gpu.stream();
2405        let mut b = __s_b.launch_builder(&f);
2406        b.arg(&mut *x)
2407            .arg(hist)
2408            .arg(&nh)
2409            .arg(&rep)
2410            .arg(&freq)
2411            .arg(&present)
2412            .arg(&ni)
2413            .arg(&nr)
2414            .arg(&wi);
2415        unsafe {
2416            b.launch(cfg)?;
2417        }
2418        Ok(())
2419    }
2420
2421    /// WEIGHT PREFETCH (SOTA item 3, 2026-07-13, DEFAULT ON): during a bandwidth-idle
2422    /// window (the fa launch reads KV, not weights) prefetch the NEXT matvec's
2423    /// decode-plane bytes into L2 so it reads L2-warm. Value-free scheduling op — same
2424    /// class as prefetch_l2 (numerics untouched by construction). Wired only where it
2425    /// measured positive: the E4B dc attn arm (+0.65%). 26B (flat — MoE ffn dominates),
2426    /// 31B (−0.2% — decode at the DRAM wall) and the ffn gate/up cascade (−1% — 29MB/layer
2427    /// floods the fill path) all probed and NOT wired. MEMRA_WPF=0 rollback seam.
2428    pub fn wpf_level() -> u32 {
2429        static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
2430        *ON.get_or_init(|| {
2431            std::env::var("MEMRA_WPF")
2432                .ok()
2433                .and_then(|v| v.parse().ok())
2434                .unwrap_or(1)
2435        })
2436    }
2437
2438    /// PDL launch arm (SOTA item 2, 2026-07-13, DEFAULT ON): the six MEMRA_PDL_ENTRY glue
2439    /// kernels launch through cuLaunchKernelEx with PROGRAMMATIC_STREAM_SERIALIZATION — the
2440    /// grid launches while the predecessor drains (~120ns/kernel back, pdl_probe), the
2441    /// kernels' entry grid-dep sync restores read order (SASS-audited: ACQBULK precedes
2442    /// every LDG in all six). Valid windows: E4B +1.0-1.2% (128 AND 384-tok gens);
2443    /// 26B/31B/qwen flat no-harm. Battery: kernel-check GREEN, run-gen tokens IDENTICAL x3
2444    /// gemma, spec 64/64 E4B K=1/4/8 + 26B/31B K=4 + qwen PASS. Works eager AND under
2445    /// capture (capture encodes native programmatic edges — the post-capture edge-REWRITE
2446    /// arm died: engine graphs hold cuMemAllocAsync alloc nodes, edge edits on those return
2447    /// CUDA_ERROR_NOT_SUPPORTED). MEMRA_PDL=0 rollback seam.
2448    /// See the `verify_exact` field. Scoped by the dflash round around its t=16 verify.
2449    /// Prefer `exact_scope` — the RAII form — anywhere a `?` can exit the scope: a manual
2450    /// true/false pair leaves the flag LATCHED engine-wide when an error propagates
2451    /// between the two calls (hermes finding on dspark_spec_session_burst, fixed
2452    /// 2026-08-23), and every later request then runs the exact-GEMM program.
2453    pub fn set_verify_exact(&self, on: bool) {
2454        self.verify_exact
2455            .store(on, std::sync::atomic::Ordering::Relaxed);
2456    }
2457    pub(crate) fn verify_exact_on(&self) -> bool {
2458        self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
2459    }
2460
2461    /// RAII scope over `verify_exact`: sets the flag to `on` now and restores the
2462    /// PREVIOUS value on drop — unwind, early `return`, and every `?` exit included.
2463    /// This is the required form for any scope an error can leave (see
2464    /// `set_verify_exact`); dropping the guard early (`drop(scope)`) ends the scope
2465    /// exactly where the manual `set_verify_exact(false)` used to sit.
2466    pub fn exact_scope(&self, on: bool) -> ExactScope<'_> {
2467        ExactScope::set(&self.verify_exact, on)
2468    }
2469
2470    /// m=1 norm+rope+append fold seam (2026-07-23): MEMRA_QKV_APPEND=0 reverts to the
2471    /// fused-norm-rope + standalone-append pair (the exact-oracle bisect arm).
2472    pub fn qkv_append_on() -> bool {
2473        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2474        *ON.get_or_init(|| {
2475            std::env::var("MEMRA_QKV_APPEND")
2476                .map(|v| v != "0")
2477                .unwrap_or(true)
2478        })
2479    }
2480
2481    /// PDL wave-B1a seam: the four dense-glue kernels (rms_norm_f32, add_rms_norm_f32,
2482    /// add_scale_rms_norm_q8_1, quantize_q8_1). MEMRA_PDL_WB=0 reverts alone.
2483    pub fn pdl_wb_on() -> bool {
2484        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2485        *ON.get_or_init(|| {
2486            std::env::var("MEMRA_PDL_WB")
2487                .map(|v| v != "0")
2488                .unwrap_or(true)
2489        })
2490    }
2491
2492    /// Trunk-kernels norm ILP seam (lane/dspark-trunk-kernels-20260820): the T-row verify
2493    /// norms (rms_norm_f32 / add_rms_norm_f32 at grid=T, block=256) are serial-latency
2494    /// chains — 20 strided scalar load->fma rounds measured 11.8-12.2us/inst (nsys-B verify
2495    /// scope: 130 inst/rd = 1.51 ms/rd). The `_v2` twins unroll the element loop 4-deep
2496    /// (independent loads in flight; SAME per-thread element order into ONE accumulator,
2497    /// reduce VERBATIM) — BIT-IDENTICAL per row at every (ncols, blockDim).
2498    /// MEMRA_NORM_ILP=0 reverts to the v1 kernels alone.
2499    pub fn norm_ilp_on() -> bool {
2500        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2501        *ON.get_or_init(|| {
2502            std::env::var("MEMRA_NORM_ILP")
2503                .map(|v| v != "0")
2504                .unwrap_or(true)
2505        })
2506    }
2507
2508    /// Trunk-kernels FFN dual seam (lane/dspark-trunk-kernels-20260820): the qwen35
2509    /// t-parallel verify FFN pair rides the PROVEN dual gate+up doors
2510    /// (`matmul_decode_exact_dual_pre` + `silu_mul_scaled_q8_1`, the q27 verify shape —
2511    /// bit-identical per (tensor,token,row), kernel-check-pinned, MEMRA_SPEC_DUAL_T
2512    /// receipts) instead of two singles + silu_mul + a standalone quantize. The doors
2513    /// existed but the qwen35 body never called them (nsys-B verify scope: gate+up singles
2514    /// = 107 launches/rd at grid 4352). MEMRA_TK_FFN_DUAL=0 reverts to the singles chain.
2515    pub fn tk_ffn_dual_on() -> bool {
2516        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2517        *ON.get_or_init(|| {
2518            std::env::var("MEMRA_TK_FFN_DUAL")
2519                .map(|v| v != "0")
2520                .unwrap_or(true)
2521        })
2522    }
2523
2524    /// PDL wave-A seam: the mmvq matvec PDL launches only (the six glue kernels keep
2525    /// their own MEMRA_PDL master seam). MEMRA_PDL_MMVQ=0 reverts wave-A alone — the
2526    /// per-model no-harm bisect knob.
2527    pub fn pdl_mmvq_on() -> bool {
2528        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2529        *ON.get_or_init(|| {
2530            std::env::var("MEMRA_PDL_MMVQ")
2531                .map(|v| v != "0")
2532                .unwrap_or(true)
2533        })
2534    }
2535
2536    pub fn pdl_on() -> bool {
2537        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2538        *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
2539    }
2540
2541    /// PDL wave-B seam (gap-diagnosis arc, GAP-DIAGNOSIS.md verdict 8): the gemma
2542    /// NVFP4mix decode chain's hot kernels — nvfp4 fused2/mr2 and the q8_0 `_rp`
2543    /// singles — join the wave-A launch class. Scheduling-only (the entry macro waits
2544    /// on the producer before any read), bit-identical by construction.
2545    /// MEMRA_PDL_NVFP4=0 reverts wave-B alone.
2546    pub fn pdl_nvfp4q8_on() -> bool {
2547        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2548        *ON.get_or_init(|| {
2549            std::env::var("MEMRA_PDL_NVFP4")
2550                .map(|v| v != "0")
2551                .unwrap_or(true)
2552        })
2553    }
2554
2555    /// Raw CUfunction for a PDL-attributed launch: the SAME kernels.fatbin loaded once more
2556    /// through the raw driver API (cudarc hides its CUfunction handles; a duplicate module
2557    /// of tiny glue kernels is free). Resolved lazily per name, cached process-wide.
2558    /// Fused t=1 q4_0 mr policy: env MEMRA_Q40_MR wins (1/2); else the per-model
2559    /// FUSED_MR1_DEFAULT (dense gemma = mr1, MoE = mr2 — see the static's doc).
2560    fn q40_mr1_on() -> bool {
2561        static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
2562        match *Q40MR.get_or_init(|| {
2563            std::env::var("MEMRA_Q40_MR")
2564                .ok()
2565                .and_then(|v| v.parse().ok())
2566        }) {
2567            Some(v) => v == 1,
2568            None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2569        }
2570    }
2571
2572    /// PDL wave-B2: flash-module PDL functions. `g` selects the kf8vf8 flavor — the
2573    /// caller MUST pass the SAME flavor its builder launch would resolve (fa_func/func_g
2574    /// mirror); the flavors differ semantically (KV byte formats), a wrong-module launch
2575    /// writes wrong bytes silently.
2576    fn pdl_func_flash(
2577        &self,
2578        g: bool,
2579        name: &'static str,
2580    ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
2581        use cudarc::driver::sys as cu;
2582        // PER-CONTEXT caches (M1-PP2 cross-device fix, 8x box 2026-08-02): CUmodule and
2583        // CUfunction handles are CONTEXT-scoped, and a remote-stage Engine
2584        // (MEMRA_PP_DEVICES=a,b) lives in the other device's primary context. The old
2585        // process-wide OnceLock cache handed stage 1 the dev-a handles, so every stage-1
2586        // launch_pdl* died CUDA_ERROR_INVALID_HANDLE. Key module + function caches by
2587        // this engine's CUcontext; single-context runs behave exactly as before.
2588        static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
2589            std::sync::Mutex::new(None);
2590        static FNS: std::sync::Mutex<
2591            Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
2592        > = std::sync::Mutex::new(None);
2593        let ctx_key = self.ctx().cu_ctx() as usize;
2594        if let Some(&f) = FNS
2595            .lock()
2596            .unwrap()
2597            .get_or_insert_with(Default::default)
2598            .get(&(ctx_key, g, name))
2599        {
2600            return Ok(f as cu::CUfunction);
2601        }
2602        let module = {
2603            let mut mods = MODS.lock().unwrap();
2604            let map = mods.get_or_insert_with(Default::default);
2605            match map.get(&(ctx_key, g)) {
2606                Some(&m) => m,
2607                None => {
2608                    let m = self.pdl_load_module_in_ctx(if g {
2609                        FLASH_FATBIN_KF8VF8
2610                    } else {
2611                        FLASH_FATBIN
2612                    })?;
2613                    map.insert((ctx_key, g), m);
2614                    m
2615                }
2616            }
2617        };
2618        let cname = std::ffi::CString::new(name)?;
2619        let mut f: cu::CUfunction = std::ptr::null_mut();
2620        let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
2621        if r != cu::CUresult::CUDA_SUCCESS {
2622            return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
2623        }
2624        FNS.lock()
2625            .unwrap()
2626            .get_or_insert_with(Default::default)
2627            .insert((ctx_key, g, name), f as usize);
2628        Ok(f)
2629    }
2630
2631    /// Load a fatbin as a raw CUmodule IN THIS ENGINE'S CONTEXT. `cuModuleLoadData` binds
2632    /// the module to the thread's CURRENT context — a remote-stage engine must not
2633    /// inherit the primary's (the INVALID_HANDLE class above). Restores the caller's
2634    /// current context before returning.
2635    fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
2636        use cudarc::driver::sys as cu;
2637        let mut prev: cu::CUcontext = std::ptr::null_mut();
2638        unsafe {
2639            cu::cuCtxGetCurrent(&mut prev).result()?;
2640        }
2641        self.ctx().bind_to_thread()?;
2642        let mut m: cu::CUmodule = std::ptr::null_mut();
2643        let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
2644        let restore = if prev.is_null() {
2645            cu::CUresult::CUDA_SUCCESS
2646        } else {
2647            unsafe { cu::cuCtxSetCurrent(prev) }
2648        };
2649        if r != cu::CUresult::CUDA_SUCCESS {
2650            return Err(format!("pdl module load: {r:?}").into());
2651        }
2652        if restore != cu::CUresult::CUDA_SUCCESS {
2653            return Err(format!("pdl module load: ctx restore {restore:?}").into());
2654        }
2655        Ok(m as usize)
2656    }
2657
2658    /// Raw CUfunction for prebuilt-args dispatch experiments (M4 probe): same duplicate
2659    /// raw-module loading as the PDL path, WITHOUT the PDL launch attribute.
2660    pub fn raw_kernel_function(
2661        &self,
2662        name: &'static str,
2663    ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
2664        self.pdl_func(name)
2665    }
2666
2667    fn pdl_func(
2668        &self,
2669        name: &'static str,
2670    ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
2671        use cudarc::driver::sys as cu;
2672        // PER-CONTEXT caches — same M1-PP2 cross-device fix as pdl_func_flash (handles
2673        // are context-scoped; key everything by this engine's CUcontext).
2674        static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
2675            std::sync::Mutex::new(None);
2676        // PDL wave-A: the mmvq kernels live in the qmatvec fatbin, not kernels.cu — second
2677        // duplicate module, loaded lazily on the first kernels-module miss.
2678        static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
2679            std::sync::Mutex::new(None);
2680        static FNS: std::sync::Mutex<
2681            Option<std::collections::HashMap<(usize, &'static str), usize>>,
2682        > = std::sync::Mutex::new(None);
2683        let ctx_key = self.ctx().cu_ctx() as usize;
2684        if let Some(&f) = FNS
2685            .lock()
2686            .unwrap()
2687            .get_or_insert_with(Default::default)
2688            .get(&(ctx_key, name))
2689        {
2690            return Ok(f as cu::CUfunction);
2691        }
2692        let module = {
2693            let mut mods = MODULES.lock().unwrap();
2694            let map = mods.get_or_insert_with(Default::default);
2695            match map.get(&ctx_key) {
2696                Some(&m) => m,
2697                None => {
2698                    let m = self.pdl_load_module_in_ctx(FATBIN)?;
2699                    map.insert(ctx_key, m);
2700                    m
2701                }
2702            }
2703        };
2704        let cname = std::ffi::CString::new(name)?;
2705        let mut f: cu::CUfunction = std::ptr::null_mut();
2706        let mut r =
2707            unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
2708        if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
2709            let qmodule = {
2710                let mut mods = QMODULES.lock().unwrap();
2711                let map = mods.get_or_insert_with(Default::default);
2712                match map.get(&ctx_key) {
2713                    Some(&m) => m,
2714                    None => {
2715                        let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
2716                        map.insert(ctx_key, m);
2717                        m
2718                    }
2719                }
2720            };
2721            r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
2722        }
2723        if r != cu::CUresult::CUDA_SUCCESS {
2724            return Err(format!("pdl_func {name}: {r:?}").into());
2725        }
2726        FNS.lock()
2727            .unwrap()
2728            .get_or_insert_with(Default::default)
2729            .insert((ctx_key, name), f as usize);
2730        Ok(f)
2731    }
2732
2733    /// cuLaunchKernelEx with CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION on the
2734    /// compute stream. ONLY legal for kernels whose entry carries MEMRA_PDL_ENTRY.
2735    ///
2736    /// # Safety
2737    /// `params` must match the kernel's exact parameter list (order, types, count) —
2738    /// a mismatch corrupts the launch silently.
2739    /// Flash-module twin of `launch_pdl` — `g` picks the kf8vf8 flavor (must mirror the
2740    /// builder path's fa_func/func_g choice exactly).
2741    ///
2742    /// # Safety
2743    /// Same contract as `launch_pdl`.
2744    unsafe fn launch_pdl_flash(
2745        &self,
2746        g: bool,
2747        name: &'static str,
2748        grid: (u32, u32, u32),
2749        block: (u32, u32, u32),
2750        smem: u32,
2751        params: &mut [*mut std::ffi::c_void],
2752    ) -> Result<(), Box<dyn std::error::Error>> {
2753        use cudarc::driver::sys as cu;
2754        let f = self.pdl_func_flash(g, name)?;
2755        if smem > 0 {
2756            // mirror the builder path's opt-in ceiling (idempotent host-side set).
2757            let r =
2758                unsafe {
2759                    cu::cuFuncSetAttribute(f,
2760                cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
2761                smem as i32)
2762                };
2763            if r != cu::CUresult::CUDA_SUCCESS {
2764                return Err(format!("pdl smem attr {name}: {r:?}").into());
2765            }
2766        }
2767        let mut attr = cu::CUlaunchAttribute {
2768            id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
2769            pad: [0; 4],
2770            value: cu::CUlaunchAttributeValue {
2771                programmaticStreamSerializationAllowed: 1,
2772            },
2773        };
2774        let cfg = cu::CUlaunchConfig {
2775            gridDimX: grid.0,
2776            gridDimY: grid.1,
2777            gridDimZ: grid.2,
2778            blockDimX: block.0,
2779            blockDimY: block.1,
2780            blockDimZ: block.2,
2781            sharedMemBytes: smem,
2782            hStream: self.gpu.stream().cu_stream(),
2783            attrs: &mut attr,
2784            numAttrs: 1,
2785        };
2786        let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
2787        if r != cu::CUresult::CUDA_SUCCESS {
2788            return Err(format!("launch_pdl_flash {name}: {r:?}").into());
2789        }
2790        Ok(())
2791    }
2792
2793    unsafe fn launch_pdl(
2794        &self,
2795        name: &'static str,
2796        grid: (u32, u32, u32),
2797        block: (u32, u32, u32),
2798        params: &mut [*mut std::ffi::c_void],
2799    ) -> Result<(), Box<dyn std::error::Error>> {
2800        use cudarc::driver::sys as cu;
2801        let f = self.pdl_func(name)?;
2802        let mut attr = cu::CUlaunchAttribute {
2803            id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
2804            pad: [0; 4],
2805            value: cu::CUlaunchAttributeValue {
2806                programmaticStreamSerializationAllowed: 1,
2807            },
2808        };
2809        let cfg = cu::CUlaunchConfig {
2810            gridDimX: grid.0,
2811            gridDimY: grid.1,
2812            gridDimZ: grid.2,
2813            blockDimX: block.0,
2814            blockDimY: block.1,
2815            blockDimZ: block.2,
2816            sharedMemBytes: 0,
2817            hStream: self.gpu.stream().cu_stream(),
2818            attrs: &mut attr,
2819            numAttrs: 1,
2820        };
2821        let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
2822        if r != cu::CUresult::CUDA_SUCCESS {
2823            return Err(format!("launch_pdl {name}: {r:?}").into());
2824        }
2825        Ok(())
2826    }
2827
2828    /// L2-prefetch a quant weight's DECODE plane (the rp4 split-plane mirror when present —
2829    /// that is what the m<=8 dispatch reads — else the raw block bytes). No-op on float arms.
2830    pub fn prefetch_weight_l2(
2831        &self,
2832        w: &crate::model::GpuTensor,
2833    ) -> Result<(), Box<dyn std::error::Error>> {
2834        if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
2835            let p = rp4.as_ref().unwrap_or(bytes);
2836            self.prefetch_l2(p, p.len())?;
2837        }
2838        Ok(())
2839    }
2840
2841    /// DSpark markov chain ops (dflash lane): gather one bf16 row of a [V, rank] table
2842    /// by the DEVICE token id at tok[idx] into f32.
2843    pub fn gather_row_bf16(
2844        &self,
2845        table: &CudaSlice<u8>,
2846        tok: &CudaSlice<u32>,
2847        idx: usize,
2848        dst: &mut CudaSlice<f32>,
2849        ncols: usize,
2850    ) -> Result<(), Box<dyn std::error::Error>> {
2851        let f = self.func("gather_row_bf16_f32");
2852        let cfg = LaunchConfig {
2853            grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2854            block_dim: (256, 1, 1),
2855            shared_mem_bytes: 0,
2856        };
2857        let (nc, ix) = (ncols as i32, idx as i32);
2858        let __s_b = self.gpu.stream();
2859        let mut b = __s_b.launch_builder(&f);
2860        b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2861        unsafe {
2862            b.launch(cfg)?;
2863        }
2864        Ok(())
2865    }
2866
2867    /// DFlash2 grouped dynamic causal conv (dflash lane, DFLASH2-EVAL-20260820.md):
2868    /// out[p,c] = sum_{o<ksize, o<=p} (base[half][o][c] + dyn[p][half][o][group(c)])
2869    /// * x[p-o][c]. `dyn_` is the kernel_projection GEMM output [rows, 2*ksize*groups];
2870    /// `base` is base_kernel [2, ksize, hidden] flattened; `half` picks prepare(0) /
2871    /// finish(1).
2872    #[allow(clippy::too_many_arguments)]
2873    pub fn dflash2_dynconv(
2874        &self,
2875        x: &CudaSlice<f32>,
2876        dyn_: &CudaSlice<f32>,
2877        base: &CudaSlice<f32>,
2878        out: &mut CudaSlice<f32>,
2879        rows: usize,
2880        hidden: usize,
2881        group_size: usize,
2882        ksize: usize,
2883        half: usize,
2884    ) -> Result<(), Box<dyn std::error::Error>> {
2885        assert_eq!(hidden % group_size, 0, "hidden % group_size != 0");
2886        let f = self.func("dflash2_dynconv_f32");
2887        let n = rows * hidden;
2888        let cfg = LaunchConfig {
2889            grid_dim: (n.div_ceil(256) as u32, 1, 1),
2890            block_dim: (256, 1, 1),
2891            shared_mem_bytes: 0,
2892        };
2893        let (ri, hi, gi, ki, hf) = (
2894            rows as i32,
2895            hidden as i32,
2896            group_size as i32,
2897            ksize as i32,
2898            half as i32,
2899        );
2900        let __s_b = self.gpu.stream();
2901        let mut b = __s_b.launch_builder(&f);
2902        b.arg(x)
2903            .arg(dyn_)
2904            .arg(base)
2905            .arg(out)
2906            .arg(&ri)
2907            .arg(&hi)
2908            .arg(&gi)
2909            .arg(&ki)
2910            .arg(&hf);
2911        unsafe {
2912            b.launch(cfg)?;
2913        }
2914        Ok(())
2915    }
2916
2917    /// Per-row top-k (k <= 32) over a [n_rows, n_cols] logits matrix (DFlash2
2918    /// candidate selector). Returns (values [n_rows, k], column indices [n_rows, k]),
2919    /// value-descending, ties to the lower index.
2920    pub fn topk_rows(
2921        &self,
2922        logits: &CudaSlice<f32>,
2923        n_rows: usize,
2924        n_cols: usize,
2925        k: usize,
2926    ) -> Result<(CudaSlice<f32>, CudaSlice<u32>), Box<dyn std::error::Error>> {
2927        assert!(k <= 32 && k >= 1, "topk_rows supports 1..=32, got {k}");
2928        assert!(k <= n_cols, "topk_rows: k {k} > n_cols {n_cols}");
2929        let f = self.func("topk_rows_f32");
2930        let nth = 256usize;
2931        let mut vals = self.uninit(n_rows * k)?;
2932        let mut idxs = self.gpu.stream().alloc_zeros::<u32>(n_rows * k)?;
2933        let cfg = LaunchConfig {
2934            grid_dim: (n_rows as u32, 1, 1),
2935            block_dim: (nth as u32, 1, 1),
2936            shared_mem_bytes: (nth * k * 8) as u32,
2937        };
2938        let (nr, nc, ki) = (n_rows as i32, n_cols as i32, k as i32);
2939        let __s_b = self.gpu.stream();
2940        let mut b = __s_b.launch_builder(&f);
2941        b.arg(logits)
2942            .arg(&nr)
2943            .arg(&nc)
2944            .arg(&ki)
2945            .arg(&mut vals)
2946            .arg(&mut idxs);
2947        unsafe {
2948            b.launch(cfg)?;
2949        }
2950        Ok((vals, idxs))
2951    }
2952
2953    /// logits[row_off .. row_off+n] += bias[0..n] (in place, one row).
2954    pub fn add_row_inplace(
2955        &self,
2956        logits: &mut CudaSlice<f32>,
2957        bias: &CudaSlice<f32>,
2958        n: usize,
2959        row_off: usize,
2960    ) -> Result<(), Box<dyn std::error::Error>> {
2961        let f = self.func("add_row_inplace_f32");
2962        let cfg = LaunchConfig {
2963            grid_dim: (n.div_ceil(256) as u32, 1, 1),
2964            block_dim: (256, 1, 1),
2965            shared_mem_bytes: 0,
2966        };
2967        let (ni, off) = (n as i32, row_off as i64);
2968        let __s_b = self.gpu.stream();
2969        let mut b = __s_b.launch_builder(&f);
2970        b.arg(logits).arg(bias).arg(&ni).arg(&off);
2971        unsafe {
2972            b.launch(cfg)?;
2973        }
2974        Ok(())
2975    }
2976
2977    /// L2 prefetch of a device byte range (latency-hiding arc; value-free scheduling op).
2978    pub fn prefetch_l2(
2979        &self,
2980        p: &CudaSlice<u8>,
2981        n: usize,
2982    ) -> Result<(), Box<dyn std::error::Error>> {
2983        let f = self.func("prefetch_l2_bytes");
2984        let lines = n.div_ceil(128);
2985        let ni = n as i64;
2986        let cfg = LaunchConfig {
2987            grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2988            block_dim: (256, 1, 1),
2989            shared_mem_bytes: 0,
2990        };
2991        let __s_b = self.gpu.stream();
2992        let mut b = __s_b.launch_builder(&f);
2993        b.arg(p).arg(&ni);
2994        unsafe {
2995            b.launch(cfg)?;
2996        }
2997        Ok(())
2998    }
2999
3000    /// MoE router GEMV (MEMRA_ROUTER_KERNEL): deterministic warp-per-(expert,token) f32 dot.
3001    /// Different FP order than the cuBLAS path it replaces — battery-gated numeric config.
3002    pub fn router_gemv(
3003        &self,
3004        w: &CudaSlice<f32>,
3005        x: &CudaSlice<f32>,
3006        n_embd: usize,
3007        n_experts: usize,
3008        t: usize,
3009    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3010        // float4 v2 probed 2026-07-14: +0.25% but flips near-tie routing (new FP order,
3011        // stream differs) — too small to justify a numeric config change; deleted.
3012        // w8 twin (2026-07-31): on the 132-SM H100 the lone-warp form is 14.8% of the q35
3013        // decode step (latency-bound) — the calculus flipped. MEMRA_ROUTER_V2=0 reverts to
3014        // the warp form (rollback seam; new FP order, battery-arbitrated per model).
3015        let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
3016            Ok("0") => false,
3017            Ok(_) => true,
3018            Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
3019        };
3020        // FAST-ROUTER batch twin (lane/fast-router, 2026-08-02): at prefill m the per-(e,tok)
3021        // w8 form re-streams both operand rows per output (GEMV program at GEMM shape — the
3022        // concat-prime exactness fix paid -10% q35 board-2048 prefill through it). The batch
3023        // twin (8x8 expert-x-token register tile) is BIT-IDENTICAL per row (same k order,
3024        // same tree, same fold — kernel-check sweeps m=1..2048 on real router weights), so
3025        // the crossover is pure perf, not a numeric config. MIN_T from the on-box sweep
3026        // (research/fast-router-20260802/crossover-router*.jsonl); decode t=1 and small-t
3027        // spec verify keep the plain w8 form. MEMRA_ROUTER_BATCH=0: rollback seam
3028        // (perf-only, bits equal).
3029        let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
3030        self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
3031    }
3032
3033    /// Form-explicit router GEMV launch (kernel-check bit-identity gate + crossover bench
3034    /// force both forms; `batch` requires `w8`).
3035    pub fn router_gemv_form(
3036        &self,
3037        w: &CudaSlice<f32>,
3038        x: &CudaSlice<f32>,
3039        n_embd: usize,
3040        n_experts: usize,
3041        t: usize,
3042        w8: bool,
3043        batch: bool,
3044    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3045        debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
3046        let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
3047        let f = if batch {
3048            self.func("router_gemv_f32_w8_batch")
3049        } else if w8 {
3050            self.func("router_gemv_f32_w8")
3051        } else {
3052            self.func("router_gemv_f32")
3053        };
3054        let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
3055        let cfg = if batch {
3056            LaunchConfig {
3057                grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
3058                block_dim: (32, 8, 1),
3059                shared_mem_bytes: 0,
3060            }
3061        } else {
3062            LaunchConfig {
3063                grid_dim: (n_experts as u32, t as u32, 1),
3064                block_dim: (32, if w8 { 8 } else { 1 }, 1),
3065                shared_mem_bytes: 0,
3066            }
3067        };
3068        let __s_b = self.gpu.stream();
3069        let mut b = __s_b.launch_builder(&f);
3070        b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
3071        unsafe {
3072            b.launch(cfg)?;
3073        }
3074        Ok(y)
3075    }
3076
3077    /// `router_gemv` (decode form selection) writing into a caller-owned [t*n_experts]
3078    /// buffer — token-graph alloc-free.
3079    pub fn router_gemv_into(
3080        &self,
3081        w: &CudaSlice<f32>,
3082        x: &CudaSlice<f32>,
3083        y: &mut CudaSlice<f32>,
3084        n_embd: usize,
3085        n_experts: usize,
3086        t: usize,
3087    ) -> Result<(), Box<dyn std::error::Error>> {
3088        if y.len() < t * n_experts {
3089            return Err("router_gemv_into output too small".into());
3090        }
3091        let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
3092            Ok("0") => false,
3093            Ok(_) => true,
3094            Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
3095        };
3096        let f = if w8 {
3097            self.func("router_gemv_f32_w8")
3098        } else {
3099            self.func("router_gemv_f32")
3100        };
3101        let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
3102        let cfg = LaunchConfig {
3103            grid_dim: (n_experts as u32, t as u32, 1),
3104            block_dim: (32, if w8 { 8 } else { 1 }, 1),
3105            shared_mem_bytes: 0,
3106        };
3107        let __s_b = self.gpu.stream();
3108        let mut b = __s_b.launch_builder(&f);
3109        b.arg(w).arg(x).arg(&mut *y).arg(&ne).arg(&nx).arg(&ti);
3110        unsafe {
3111            b.launch(cfg)?;
3112        }
3113        Ok(())
3114    }
3115
3116    /// f32 row permute: dst[idx[i], :] = src[i, :] (grouped-GEMM CSR -> pair-id reorder).
3117    pub fn rows_permute(
3118        &self,
3119        src: &CudaSlice<f32>,
3120        idx: &CudaSlice<i32>,
3121        nrows: usize,
3122        ncols: usize,
3123    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3124        let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
3125        let f = self.func("rows_permute_f32");
3126        let (nc, nr) = (ncols as i32, nrows as i32);
3127        let cfg = LaunchConfig {
3128            grid_dim: (nrows as u32, 1, 1),
3129            block_dim: (256, 1, 1),
3130            shared_mem_bytes: 0,
3131        };
3132        let __s_b = self.gpu.stream();
3133        let mut b = __s_b.launch_builder(&f);
3134        b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
3135        unsafe {
3136            b.launch(cfg)?;
3137        }
3138        Ok(dst)
3139    }
3140
3141    /// shexp gate fused dot: g[tok] = sigmoid(dot(x[tok,:], w)) — replaces the per-layer
3142    /// cuBLASLt m=1 GEMM + separate sigmoid launch on the qwen35moe decode path (the
3143    /// splitKreduce x40/step dig, 2026-07-31). One fold order for every t, so the t=1
3144    /// decode chain and the small-t spec-verify chain match per row by construction.
3145    pub fn sigmoid_dot_rows(
3146        &self,
3147        x: &CudaSlice<f32>,
3148        w: &CudaSlice<f32>,
3149        n_embd: usize,
3150        t: usize,
3151    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3152        // MEMRA_SHEXP_DOT=0: rollback seam to the cuBLASLt linear + sigmoid pair (numeric
3153        // config; same class as MEMRA_ROUTER_V2).
3154        static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3155        if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
3156            let gs = self.linear(x, w, t, n_embd, 1)?;
3157            let mut g = self.uninit(t)?;
3158            self.sigmoid(&gs, &mut g, t)?;
3159            return Ok(g);
3160        }
3161        // FAST-ROUTER lane note (2026-08-02): a register-tiled 8-token batch twin of this
3162        // kernel was built, proven bit-identical, and measured SLOWER at every prefill t on
3163        // the 5090 (0.62-0.89x — launch-latency-bound op, ~7us/layer at m=2048;
3164        // research/fast-router-20260802/crossover-router.jsonl). Dispatch arm killed per
3165        // flags doctrine; this per-token form serves every t.
3166        let mut g = self.alloc_uninit::<f32>(t)?;
3167        let f = self.func("sigmoid_dot_rows_f32");
3168        let (ne, ti) = (n_embd as i32, t as i32);
3169        let cfg = LaunchConfig {
3170            grid_dim: (t as u32, 1, 1),
3171            block_dim: (32, 8, 1),
3172            shared_mem_bytes: 0,
3173        };
3174        let __s_b = self.gpu.stream();
3175        let mut b = __s_b.launch_builder(&f);
3176        b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
3177        unsafe {
3178            b.launch(cfg)?;
3179        }
3180        Ok(g)
3181    }
3182
3183    /// `sigmoid_dot_rows` writing into a caller-owned [t] buffer (token-graph alloc-free).
3184    pub fn sigmoid_dot_rows_into(
3185        &self,
3186        x: &CudaSlice<f32>,
3187        w: &CudaSlice<f32>,
3188        g: &mut CudaSlice<f32>,
3189        n_embd: usize,
3190        t: usize,
3191    ) -> Result<(), Box<dyn std::error::Error>> {
3192        if g.len() < t {
3193            return Err("sigmoid_dot_rows_into output too small".into());
3194        }
3195        let f = self.func("sigmoid_dot_rows_f32");
3196        let (ne, ti) = (n_embd as i32, t as i32);
3197        let cfg = LaunchConfig {
3198            grid_dim: (t as u32, 1, 1),
3199            block_dim: (32, 8, 1),
3200            shared_mem_bytes: 0,
3201        };
3202        let __s_b = self.gpu.stream();
3203        let mut b = __s_b.launch_builder(&f);
3204        b.arg(x).arg(w).arg(&mut *g).arg(&ne).arg(&ti);
3205        unsafe {
3206            b.launch(cfg)?;
3207        }
3208        Ok(())
3209    }
3210
3211    /// ROUND-STREAM stream rollback: all counters <- pos_start + base + n_acc.
3212    pub fn spec_rollback_stream(
3213        &self,
3214        len_ptrs: &CudaSlice<u64>,
3215        pos_start: &CudaSlice<i32>,
3216        acc: &CudaSlice<u32>,
3217        base: usize,
3218        n_rows: usize,
3219    ) -> Result<(), Box<dyn std::error::Error>> {
3220        let f = self.func("spec_rollback_stream");
3221        let (b, nr) = (base as i32, n_rows as i32);
3222        let cfg = LaunchConfig {
3223            grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
3224            block_dim: (64, 1, 1),
3225            shared_mem_bytes: 0,
3226        };
3227        let __s_bl = self.gpu.stream();
3228        let mut bl = __s_bl.launch_builder(&f);
3229        bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
3230        unsafe {
3231            bl.launch(cfg)?;
3232        }
3233        Ok(())
3234    }
3235
3236    /// PLAIN-DECODE GRAPH ring store: ring[(pos_start - base) % cap] = vam[0].
3237    pub fn plain_tok_ring(
3238        &self,
3239        vam: &CudaSlice<u32>,
3240        pos_start: &CudaSlice<i32>,
3241        base: usize,
3242        ring: &mut CudaSlice<u32>,
3243    ) -> Result<(), Box<dyn std::error::Error>> {
3244        let f = self.func("plain_tok_ring");
3245        let (b, cap) = (base as i32, ring.len() as i32);
3246        let cfg = LaunchConfig {
3247            grid_dim: (1, 1, 1),
3248            block_dim: (32, 1, 1),
3249            shared_mem_bytes: 0,
3250        };
3251        let __s_bl = self.gpu.stream();
3252        let mut bl = __s_bl.launch_builder(&f);
3253        bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
3254        unsafe {
3255            bl.launch(cfg)?;
3256        }
3257        Ok(())
3258    }
3259
3260    /// ROUND-STREAM stage (c) 4 epilogue: ring commit + tiny counter copies.
3261    pub fn spec_ring_commit(
3262        &self,
3263        vtok: &CudaSlice<u32>,
3264        acc: &CudaSlice<u32>,
3265        brk: &CudaSlice<u32>,
3266        ring: &mut CudaSlice<u32>,
3267        pend: &mut CudaSlice<u32>,
3268    ) -> Result<(), Box<dyn std::error::Error>> {
3269        let f = self.func("spec_ring_commit");
3270        let cfg = LaunchConfig {
3271            grid_dim: (1, 1, 1),
3272            block_dim: (32, 1, 1),
3273            shared_mem_bytes: 0,
3274        };
3275        let __s_b = self.gpu.stream();
3276        let mut b = __s_b.launch_builder(&f);
3277        b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
3278        unsafe {
3279            b.launch(cfg)?;
3280        }
3281        Ok(())
3282    }
3283    pub fn i32_copy_add(
3284        &self,
3285        src: &CudaSlice<i32>,
3286        dst: &mut CudaSlice<i32>,
3287        delta: i32,
3288    ) -> Result<(), Box<dyn std::error::Error>> {
3289        let f = self.func("i32_copy_add");
3290        let cfg = LaunchConfig {
3291            grid_dim: (1, 1, 1),
3292            block_dim: (32, 1, 1),
3293            shared_mem_bytes: 0,
3294        };
3295        let __s_b = self.gpu.stream();
3296        let mut b = __s_b.launch_builder(&f);
3297        b.arg(src).arg(dst).arg(&delta);
3298        unsafe {
3299            b.launch(cfg)?;
3300        }
3301        Ok(())
3302    }
3303    pub fn u32_copy(
3304        &self,
3305        src: &CudaSlice<u32>,
3306        dst: &mut CudaSlice<u32>,
3307    ) -> Result<(), Box<dyn std::error::Error>> {
3308        let f = self.func("u32_copy");
3309        let cfg = LaunchConfig {
3310            grid_dim: (1, 1, 1),
3311            block_dim: (32, 1, 1),
3312            shared_mem_bytes: 0,
3313        };
3314        let __s_b = self.gpu.stream();
3315        let mut b = __s_b.launch_builder(&f);
3316        b.arg(src).arg(dst);
3317        unsafe {
3318            b.launch(cfg)?;
3319        }
3320        Ok(())
3321    }
3322
3323    /// ROUND-GRAPH adaptive depth: brk[0] <- clamp(acc[0] + 1, floor, cap) — the host
3324    /// adaptive policy as a captured device op (policy-identical: the accept walk depth
3325    /// caps acceptance exactly like drafting fewer tokens).
3326    pub fn spec_adapt_k(
3327        &self,
3328        acc: &CudaSlice<u32>,
3329        brk: &mut CudaSlice<u32>,
3330        floor: usize,
3331        cap: usize,
3332    ) -> Result<(), Box<dyn std::error::Error>> {
3333        let f = self.func("spec_adapt_k");
3334        let (fl, cp) = (floor as i32, cap as i32);
3335        let cfg = LaunchConfig {
3336            grid_dim: (1, 1, 1),
3337            block_dim: (32, 1, 1),
3338            shared_mem_bytes: 0,
3339        };
3340        let __s_b = self.gpu.stream();
3341        let mut b = __s_b.launch_builder(&f);
3342        b.arg(acc).arg(brk).arg(&fl).arg(&cp);
3343        unsafe {
3344            b.launch(cfg)?;
3345        }
3346        Ok(())
3347    }
3348
3349    /// ROUND-STREAM stage (c) 3: accept walk fully device-driven (brk + assembled vtok).
3350    pub fn spec_accept_greedy_dc(
3351        &self,
3352        preds: &CudaSlice<u32>,
3353        vtok: &CudaSlice<u32>,
3354        last_pred: &CudaSlice<u32>,
3355        brk: &CudaSlice<u32>,
3356        out: &mut CudaSlice<u32>,
3357    ) -> Result<(), Box<dyn std::error::Error>> {
3358        let f = self.func("spec_accept_greedy_dc");
3359        let cfg = LaunchConfig {
3360            grid_dim: (1, 1, 1),
3361            block_dim: (32, 1, 1),
3362            shared_mem_bytes: 0,
3363        };
3364        let __s_b = self.gpu.stream();
3365        let mut b = __s_b.launch_builder(&f);
3366        b.arg(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
3367        unsafe {
3368            b.launch(cfg)?;
3369        }
3370        Ok(())
3371    }
3372
3373    /// ROUND-STREAM stage (c) 2: verify-chain device-pos entries.
3374    pub fn pos_iota(
3375        &self,
3376        pos0: &CudaSlice<i32>,
3377        out: &mut CudaSlice<i32>,
3378        t: usize,
3379    ) -> Result<(), Box<dyn std::error::Error>> {
3380        let f = self.func("pos_iota_i32");
3381        let ti = t as i32;
3382        let cfg = LaunchConfig {
3383            grid_dim: (1, 1, 1),
3384            block_dim: (t.max(1) as u32, 1, 1),
3385            shared_mem_bytes: 0,
3386        };
3387        let __s_b = self.gpu.stream();
3388        let mut b = __s_b.launch_builder(&f);
3389        b.arg(pos0).arg(out).arg(&ti);
3390        unsafe {
3391            b.launch(cfg)?;
3392        }
3393        Ok(())
3394    }
3395    #[allow(clippy::too_many_arguments)]
3396    pub fn append_kv_quantized_rows_dc(
3397        &self,
3398        k_rows: &CudaSlice<f32>,
3399        v_rows: &CudaSlice<f32>,
3400        kc: &mut CudaSlice<u8>,
3401        vc: &mut CudaSlice<u8>,
3402        t0_dev: &CudaSlice<i32>,
3403        t: usize,
3404        kv_dim_k: usize,
3405        kv_dim_v: usize,
3406        k_tok_bytes: usize,
3407        v_tok_bytes: usize,
3408        g: bool,
3409    ) -> Result<(), Box<dyn std::error::Error>> {
3410        let f = if g {
3411            self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
3412        } else {
3413            self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
3414        };
3415        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3416        let cfg = LaunchConfig {
3417            grid_dim: (nblk, t as u32, 1),
3418            block_dim: (32, 1, 1),
3419            shared_mem_bytes: 0,
3420        };
3421        let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3422        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3423        let __s_b = self.gpu.stream();
3424        let mut b = __s_b.launch_builder(&f);
3425        b.arg(k_rows)
3426            .arg(v_rows)
3427            .arg(kc)
3428            .arg(vc)
3429            .arg(t0_dev)
3430            .arg(&kdk)
3431            .arg(&kdv)
3432            .arg(&ktb)
3433            .arg(&vtb);
3434        unsafe {
3435            b.launch(cfg)?;
3436        }
3437        Ok(())
3438    }
3439
3440    /// t=1 dc append with a FUSED len_d increment (wave 5c) — one launch replaces
3441    /// append_rows_dc + inc_seqlen. Single block (read-before-inc ordering).
3442    #[allow(clippy::too_many_arguments)]
3443    pub fn append_kv_quantized_row_dc_inc(
3444        &self,
3445        k_row: &CudaSlice<f32>,
3446        v_row: &CudaSlice<f32>,
3447        kc: &mut CudaSlice<u8>,
3448        vc: &mut CudaSlice<u8>,
3449        t0_dev: &mut CudaSlice<i32>,
3450        kv_dim_k: usize,
3451        kv_dim_v: usize,
3452        k_tok_bytes: usize,
3453        v_tok_bytes: usize,
3454        g: bool,
3455    ) -> Result<(), Box<dyn std::error::Error>> {
3456        let f = if g {
3457            self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
3458        } else {
3459            self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
3460        };
3461        let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
3462        let cfg = LaunchConfig {
3463            grid_dim: (1, 1, 1),
3464            block_dim: (nthreads, 1, 1),
3465            shared_mem_bytes: 0,
3466        };
3467        let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3468        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3469        let __s_b = self.gpu.stream();
3470        let mut b = __s_b.launch_builder(&f);
3471        b.arg(k_row)
3472            .arg(v_row)
3473            .arg(kc)
3474            .arg(vc)
3475            .arg(t0_dev)
3476            .arg(&kdk)
3477            .arg(&kdv)
3478            .arg(&ktb)
3479            .arg(&vtb);
3480        unsafe {
3481            b.launch(cfg)?;
3482        }
3483        Ok(())
3484    }
3485
3486    /// ROUND-STREAM: draft-chain pack + in-graph d2t remap (see kernels.cu headers).
3487    pub fn pack_tok_p(
3488        &self,
3489        tok: &CudaSlice<u32>,
3490        p: &CudaSlice<f32>,
3491        out: &mut CudaSlice<u32>,
3492        slot: usize,
3493    ) -> Result<(), Box<dyn std::error::Error>> {
3494        let f = self.func("pack_tok_p");
3495        let sl = slot as i32;
3496        let cfg = LaunchConfig {
3497            grid_dim: (1, 1, 1),
3498            block_dim: (32, 1, 1),
3499            shared_mem_bytes: 0,
3500        };
3501        let __s_b = self.gpu.stream();
3502        let mut b = __s_b.launch_builder(&f);
3503        b.arg(tok).arg(p).arg(out).arg(&sl);
3504        unsafe {
3505            b.launch(cfg)?;
3506        }
3507        Ok(())
3508    }
3509    pub fn tok_map_u32(
3510        &self,
3511        tok: &mut CudaSlice<u32>,
3512        map: &CudaSlice<u32>,
3513    ) -> Result<(), Box<dyn std::error::Error>> {
3514        let f = self.func("tok_map_u32");
3515        let cfg = LaunchConfig {
3516            grid_dim: (1, 1, 1),
3517            block_dim: (32, 1, 1),
3518            shared_mem_bytes: 0,
3519        };
3520        let __s_b = self.gpu.stream();
3521        let mut b = __s_b.launch_builder(&f);
3522        b.arg(tok).arg(map);
3523        unsafe {
3524            b.launch(cfg)?;
3525        }
3526        Ok(())
3527    }
3528
3529    /// ROUND-STREAM stage (c) 1: device verify-token assembly + p-min break derivation.
3530    #[allow(clippy::too_many_arguments)]
3531    pub fn spec_assemble_verify(
3532        &self,
3533        tokp: &CudaSlice<u32>,
3534        pend: &CudaSlice<u32>,
3535        d2t: Option<&CudaSlice<u32>>,
3536        vtok: &mut CudaSlice<u32>,
3537        brk: &mut CudaSlice<u32>,
3538        p_min: f32,
3539        k: usize,
3540        pmin0: bool,
3541    ) -> Result<(), Box<dyn std::error::Error>> {
3542        let f = self.func("spec_assemble_verify");
3543        let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
3544        let cfg = LaunchConfig {
3545            grid_dim: (1, 1, 1),
3546            block_dim: (32, 1, 1),
3547            shared_mem_bytes: 0,
3548        };
3549        let __s_b = self.gpu.stream();
3550        let mut b = __s_b.launch_builder(&f);
3551        match d2t {
3552            Some(m) => {
3553                b.arg(tokp)
3554                    .arg(pend)
3555                    .arg(m)
3556                    .arg(vtok)
3557                    .arg(brk)
3558                    .arg(&p_min)
3559                    .arg(&ki)
3560                    .arg(&pm);
3561                unsafe {
3562                    b.launch(cfg)?;
3563                }
3564            }
3565            None => {
3566                let null: u64 = 0;
3567                b.arg(tokp)
3568                    .arg(pend)
3569                    .arg(&null)
3570                    .arg(vtok)
3571                    .arg(brk)
3572                    .arg(&p_min)
3573                    .arg(&ki)
3574                    .arg(&pm);
3575                unsafe {
3576                    b.launch(cfg)?;
3577                }
3578            }
3579        }
3580        Ok(())
3581    }
3582
3583    /// ROUND-STREAM stage (b) 3b: recur-restore twins with device-j (see hybrid.cu headers).
3584    #[allow(clippy::too_many_arguments)]
3585    pub fn ssm_conv_ring_rebuild_dc(
3586        &self,
3587        qkv_tm: &CudaSlice<f32>,
3588        ring_old: &CudaSlice<f32>,
3589        conv_state: &mut CudaSlice<f32>,
3590        conv_dim: usize,
3591        acc: &CudaSlice<u32>,
3592        base: usize,
3593        t_v: usize,
3594        d_conv: usize,
3595    ) -> Result<(), Box<dyn std::error::Error>> {
3596        let f = self.func("ssm_conv_ring_rebuild_f32_dc");
3597        let n = conv_dim * (d_conv - 1);
3598        let cfg = LaunchConfig::for_num_elems(n as u32);
3599        let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
3600        let __s_b = self.gpu.stream();
3601        let mut b = __s_b.launch_builder(&f);
3602        b.arg(qkv_tm)
3603            .arg(ring_old)
3604            .arg(conv_state)
3605            .arg(&cd)
3606            .arg(acc)
3607            .arg(&b0)
3608            .arg(&tv)
3609            .arg(&dc);
3610        unsafe {
3611            b.launch(cfg)?;
3612        }
3613        Ok(())
3614    }
3615    #[allow(clippy::too_many_arguments)]
3616    pub fn gdn_scan_s128_dc(
3617        &self,
3618        q: &CudaSlice<f32>,
3619        k: &CudaSlice<f32>,
3620        v: &CudaSlice<f32>,
3621        g: &CudaSlice<f32>,
3622        beta: &CudaSlice<f32>,
3623        state_in: &CudaSlice<f32>,
3624        state_out: &mut CudaSlice<f32>,
3625        o: &mut CudaSlice<f32>,
3626        n_head: usize,
3627        acc: &CudaSlice<u32>,
3628        base: usize,
3629        t_v: usize,
3630        scale: f32,
3631    ) -> Result<(), Box<dyn std::error::Error>> {
3632        let f = self.func("gdn_scan_s128_dc");
3633        const S_V: u32 = 128;
3634        const WARP: u32 = 32;
3635        const COLS_PER_BLOCK: u32 = 4;
3636        let cfg = LaunchConfig {
3637            grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
3638            block_dim: (WARP, COLS_PER_BLOCK, 1),
3639            shared_mem_bytes: 0,
3640        };
3641        let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
3642        let __s_b = self.gpu.stream();
3643        let mut b = __s_b.launch_builder(&f);
3644        b.arg(q)
3645            .arg(k)
3646            .arg(v)
3647            .arg(g)
3648            .arg(beta)
3649            .arg(state_in)
3650            .arg(state_out)
3651            .arg(o)
3652            .arg(&h)
3653            .arg(acc)
3654            .arg(&b0)
3655            .arg(&tv)
3656            .arg(&scale);
3657        unsafe {
3658            b.launch(cfg)?;
3659        }
3660        Ok(())
3661    }
3662
3663    /// ROUND-STREAM stage (b) 3a: device per-layer KV-len rollback (see spec_rollback_kv).
3664    pub fn spec_rollback_kv(
3665        &self,
3666        len_ptrs: &CudaSlice<u64>,
3667        saved: &CudaSlice<i32>,
3668        acc: &CudaSlice<u32>,
3669        base: usize,
3670        n_layer: usize,
3671    ) -> Result<(), Box<dyn std::error::Error>> {
3672        let f = self.func("spec_rollback_kv");
3673        let (b, nl) = (base as i32, n_layer as i32);
3674        let cfg = LaunchConfig {
3675            grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
3676            block_dim: (64, 1, 1),
3677            shared_mem_bytes: 0,
3678        };
3679        let __s_bl = self.gpu.stream();
3680        let mut bl = __s_bl.launch_builder(&f);
3681        bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
3682        unsafe {
3683            bl.launch(cfg)?;
3684        }
3685        Ok(())
3686    }
3687
3688    /// OPTIPIPE increment 1: derive the K=1 successor-valid bit on device.
3689    pub fn spec_fork_valid(
3690        &self,
3691        acc: &CudaSlice<u32>,
3692        optimistic_pending: u32,
3693        valid: &mut CudaSlice<u32>,
3694    ) -> Result<(), Box<dyn std::error::Error>> {
3695        let f = self.func("spec_fork_valid");
3696        let cfg = LaunchConfig {
3697            grid_dim: (1, 1, 1),
3698            block_dim: (1, 1, 1),
3699            shared_mem_bytes: 0,
3700        };
3701        let __s_bl = self.gpu.stream();
3702        let mut bl = __s_bl.launch_builder(&f);
3703        bl.arg(acc).arg(&optimistic_pending).arg(valid);
3704        unsafe {
3705            bl.launch(cfg)?;
3706        }
3707        Ok(())
3708    }
3709
3710    /// OPTIPIPE increment 1: leave stage-local KV lengths on hit, restore them on miss.
3711    pub fn spec_fork_reconcile_kv(
3712        &self,
3713        len_ptrs: &CudaSlice<u64>,
3714        saved: &CudaSlice<i32>,
3715        acc: &CudaSlice<u32>,
3716        valid: &CudaSlice<u32>,
3717        base: usize,
3718        n_layer: usize,
3719    ) -> Result<(), Box<dyn std::error::Error>> {
3720        let f = self.func("spec_fork_reconcile_kv");
3721        let (b, nl) = (base as i32, n_layer as i32);
3722        let cfg = LaunchConfig {
3723            grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
3724            block_dim: (64, 1, 1),
3725            shared_mem_bytes: 0,
3726        };
3727        let __s_bl = self.gpu.stream();
3728        let mut bl = __s_bl.launch_builder(&f);
3729        bl.arg(len_ptrs)
3730            .arg(saved)
3731            .arg(acc)
3732            .arg(valid)
3733            .arg(&b)
3734            .arg(&nl);
3735        unsafe {
3736            bl.launch(cfg)?;
3737        }
3738        Ok(())
3739    }
3740
3741    /// OPTIPIPE increment 1: conditionally restore one stage-owned recurrent-state buffer.
3742    pub fn spec_fork_restore_f32(
3743        &self,
3744        snapshot: &CudaSlice<f32>,
3745        state: &mut CudaSlice<f32>,
3746        valid: &CudaSlice<u32>,
3747    ) -> Result<(), Box<dyn std::error::Error>> {
3748        assert_eq!(
3749            snapshot.len(),
3750            state.len(),
3751            "fork recurrent snapshot shape mismatch"
3752        );
3753        let f = self.func("spec_fork_restore_f32");
3754        let n = state.len() as i32;
3755        let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
3756        let cfg = LaunchConfig {
3757            grid_dim: (blocks, 1, 1),
3758            block_dim: (256, 1, 1),
3759            shared_mem_bytes: 0,
3760        };
3761        let __s_bl = self.gpu.stream();
3762        let mut bl = __s_bl.launch_builder(&f);
3763        bl.arg(snapshot).arg(state).arg(valid).arg(&n);
3764        unsafe {
3765            bl.launch(cfg)?;
3766        }
3767        Ok(())
3768    }
3769
3770    /// ROUND-STREAM stage (b): device next-round seed gather (see spec_seed_gather header).
3771    /// Caller D2Ds h_seed into fill_prev after (both slots carry the same value in every arm).
3772    pub fn spec_seed_gather(
3773        &self,
3774        vx: &CudaSlice<f32>,
3775        fill_prev: &CudaSlice<f32>,
3776        acc: &CudaSlice<u32>,
3777        h_seed: &mut CudaSlice<f32>,
3778        base: usize,
3779        n_embd: usize,
3780    ) -> Result<(), Box<dyn std::error::Error>> {
3781        let f = self.func("spec_seed_gather");
3782        let (b, ne) = (base as i32, n_embd as i32);
3783        let cfg = LaunchConfig {
3784            grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
3785            block_dim: (256, 1, 1),
3786            shared_mem_bytes: 0,
3787        };
3788        let __s_bl = self.gpu.stream();
3789        let mut bl = __s_bl.launch_builder(&f);
3790        bl.arg(vx)
3791            .arg(fill_prev)
3792            .arg(acc)
3793            .arg(h_seed)
3794            .arg(&b)
3795            .arg(&ne);
3796        unsafe {
3797            bl.launch(cfg)?;
3798        }
3799        Ok(())
3800    }
3801
3802    /// ROUND-STREAM stage (a): device greedy accept walk (see spec_accept_greedy header).
3803    pub fn spec_accept_greedy(
3804        &self,
3805        preds: &CudaSlice<u32>,
3806        draft: &CudaSlice<u32>,
3807        last_pred: u32,
3808        base: usize,
3809        k_round: usize,
3810        out: &mut CudaSlice<u32>,
3811    ) -> Result<(), Box<dyn std::error::Error>> {
3812        let f = self.func("spec_accept_greedy");
3813        let (b, k) = (base as i32, k_round as i32);
3814        let cfg = LaunchConfig {
3815            grid_dim: (1, 1, 1),
3816            block_dim: (32, 1, 1),
3817            shared_mem_bytes: 0,
3818        };
3819        let __s_bl = self.gpu.stream();
3820        let mut bl = __s_bl.launch_builder(&f);
3821        bl.arg(preds)
3822            .arg(draft)
3823            .arg(&last_pred)
3824            .arg(&b)
3825            .arg(&k)
3826            .arg(out);
3827        unsafe {
3828            bl.launch(cfg)?;
3829        }
3830        Ok(())
3831    }
3832
3833    // ================= SAMPLED-SPEC PRIMITIVES (spec_sample.cu, piece A) =================
3834    // Counter-based randomness: every call takes (seed, stream_pos) — the caller owns the
3835    // event counter (one per sampled token). temp <= 0 arms are exact greedy limits.
3836
3837    /// y = x/temp + Gumbel(Philox(seed, stream_pos)) over n logits (then run device argmax on y
3838    /// = one categorical sample at temperature `temp`). temp<=0: y = x (pure copy).
3839    pub fn gumbel_perturb(
3840        &self,
3841        x: &CudaSlice<f32>,
3842        y: &mut CudaSlice<f32>,
3843        n: usize,
3844        seed: u64,
3845        stream_pos: u32,
3846        temp: f32,
3847    ) -> Result<(), Box<dyn std::error::Error>> {
3848        let f = self.func("gumbel_perturb_f32");
3849        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3850        let cfg = LaunchConfig {
3851            grid_dim: (n.div_ceil(256) as u32, 1, 1),
3852            block_dim: (256, 1, 1),
3853            shared_mem_bytes: 0,
3854        };
3855        let __s_b = self.gpu.stream();
3856        let mut b = __s_b.launch_builder(&f);
3857        b.arg(x)
3858            .arg(&mut *y)
3859            .arg(&ni)
3860            .arg(&slo)
3861            .arg(&shi)
3862            .arg(&stream_pos)
3863            .arg(&temp);
3864        unsafe {
3865            b.launch(cfg)?;
3866        }
3867        Ok(())
3868    }
3869
3870    /// GRAMMAR TOKEN MASK (constrained decoding, lane/constrained-full): ban every vocab id
3871    /// whose bit is unset in the packed llguidance bitset, IN PLACE on row `col` of a stacked
3872    /// [B, n_vocab] logits buffer. `mask` = the SimpleVob u32 words H2D'd verbatim
3873    /// (~n_vocab/8 bytes/step — trivial on PCIe); ids >= 32*mask_words (padded lm_head tail)
3874    /// are banned too, the device twin of constrained::apply_mask. Banned value -FLT_MAX ==
3875    /// the argmax/gumbel kernels' init sentinel, so a fully-banned tail can never win and
3876    /// ordering matches the host -inf mask bit-for-bit for every finite logit.
3877    pub fn mask_logits_col(
3878        &self,
3879        logits: &mut CudaSlice<f32>,
3880        mask: &CudaSlice<u32>,
3881        col: usize,
3882        n: usize,
3883        mask_words: usize,
3884    ) -> Result<(), Box<dyn std::error::Error>> {
3885        let f = self.func("mask_logits_f32");
3886        let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
3887        let cfg = LaunchConfig {
3888            grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
3889            block_dim: (256, 1, 1),
3890            shared_mem_bytes: 0,
3891        };
3892        let __s_b = self.gpu.stream();
3893        let mut b = __s_b.launch_builder(&f);
3894        b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
3895        unsafe {
3896            b.launch(cfg)?;
3897        }
3898        Ok(())
3899    }
3900
3901    /// Column-`col` twin of `gumbel_perturb` over stacked logits [B, n_vocab] (the batched
3902    /// serving tick's device sampler): y = x[col]/temp + gumbel(seed, stream_pos, lane).
3903    /// SAME kernel/Philox mapping as `gumbel_perturb` — bit-identical perturbation for the
3904    /// same (seed, stream_pos, temp) regardless of which batch column the row sits in
3905    /// (the lane index is the in-row position; `col` only moves the input pointer). That
3906    /// pointer-invariance IS the serving isolation contract for sampled rows.
3907    pub fn gumbel_perturb_col(
3908        &self,
3909        x: &CudaSlice<f32>,
3910        col: usize,
3911        y: &mut CudaSlice<f32>,
3912        n: usize,
3913        seed: u64,
3914        stream_pos: u32,
3915        temp: f32,
3916    ) -> Result<(), Box<dyn std::error::Error>> {
3917        let f = self.func("gumbel_perturb_f32");
3918        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3919        let col_view = x.slice(col * n..(col + 1) * n);
3920        let cfg = LaunchConfig {
3921            grid_dim: (n.div_ceil(256) as u32, 1, 1),
3922            block_dim: (256, 1, 1),
3923            shared_mem_bytes: 0,
3924        };
3925        let __s_b = self.gpu.stream();
3926        let mut b = __s_b.launch_builder(&f);
3927        b.arg(&col_view)
3928            .arg(&mut *y)
3929            .arg(&ni)
3930            .arg(&slo)
3931            .arg(&shi)
3932            .arg(&stream_pos)
3933            .arg(&temp);
3934        unsafe {
3935            b.launch(cfg)?;
3936        }
3937        Ok(())
3938    }
3939
3940    /// Filtered twin of `gumbel_perturb_col`: the per-row (row_max, th) floor comes from
3941    /// DEVICE buffers (`filter_stats` output slots at `stat_idx`) — one filtered draw from
3942    /// the top-k/top-p/min-p-truncated softmax with no stat D2H and no row copy. Same
3943    /// Philox mapping as every gumbel kernel (pointer-invariant across batch columns —
3944    /// the serving isolation contract for sampled rows).
3945    #[allow(clippy::too_many_arguments)]
3946    pub fn gumbel_perturb_filtered_col(
3947        &self,
3948        x: &CudaSlice<f32>,
3949        col: usize,
3950        y: &mut CudaSlice<f32>,
3951        n: usize,
3952        seed: u64,
3953        stream_pos: u32,
3954        temp: f32,
3955        stat_max: &CudaSlice<f32>,
3956        stat_th: &CudaSlice<f32>,
3957        stat_idx: usize,
3958    ) -> Result<(), Box<dyn std::error::Error>> {
3959        let f = self.func("gumbel_perturb_filtered_col_f32");
3960        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3961        let (ci, si) = (col as i32, stat_idx as i32);
3962        let cfg = LaunchConfig {
3963            grid_dim: (n.div_ceil(256) as u32, 1, 1),
3964            block_dim: (256, 1, 1),
3965            shared_mem_bytes: 0,
3966        };
3967        let __s_b = self.gpu.stream();
3968        let mut b = __s_b.launch_builder(&f);
3969        b.arg(x)
3970            .arg(&ci)
3971            .arg(&mut *y)
3972            .arg(&ni)
3973            .arg(&slo)
3974            .arg(&shi)
3975            .arg(&stream_pos)
3976            .arg(&temp)
3977            .arg(stat_max)
3978            .arg(stat_th)
3979            .arg(&si);
3980        unsafe {
3981            b.launch(cfg)?;
3982        }
3983        Ok(())
3984    }
3985
3986    /// In-graph sampling-event counter bump (spec_sample.cu kernel 5): ctr[0] += 1. The sampled
3987    /// graph-draft chain replays with FIXED kernel args, so the Philox event counter must be
3988    /// DEVICE data — the host seeds it once per round; every replay bumps it before the perturb
3989    /// reads it (counter is data, not state — graph-replay-safe).
3990    pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
3991        let f = self.func("memra_sctr_inc");
3992        let cfg = LaunchConfig {
3993            grid_dim: (1, 1, 1),
3994            block_dim: (1, 1, 1),
3995            shared_mem_bytes: 0,
3996        };
3997        let __s_b = self.gpu.stream();
3998        let mut b = __s_b.launch_builder(&f);
3999        b.arg(&mut *ctr);
4000        unsafe {
4001            b.launch(cfg)?;
4002        }
4003        Ok(())
4004    }
4005
4006    /// Graph-capturable `gumbel_perturb`: the sampling-event counter comes from DEVICE memory
4007    /// (`ctr[0]`) instead of a host scalar. Identical math to `gumbel_perturb` at
4008    /// stream_pos == ctr[0] (same Philox call, same lane mapping) — the eager and graph sampled
4009    /// chains produce bit-identical perturbations for the same (seed, counter, temp).
4010    pub fn gumbel_perturb_ctr(
4011        &self,
4012        x: &CudaSlice<f32>,
4013        y: &mut CudaSlice<f32>,
4014        n: usize,
4015        seed: u64,
4016        ctr: &CudaSlice<u32>,
4017        temp: f32,
4018    ) -> Result<(), Box<dyn std::error::Error>> {
4019        let f = self.func("gumbel_perturb_ctr_f32");
4020        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
4021        let cfg = LaunchConfig {
4022            grid_dim: (n.div_ceil(256) as u32, 1, 1),
4023            block_dim: (256, 1, 1),
4024            shared_mem_bytes: 0,
4025        };
4026        let __s_b = self.gpu.stream();
4027        let mut b = __s_b.launch_builder(&f);
4028        b.arg(x)
4029            .arg(&mut *y)
4030            .arg(&ni)
4031            .arg(&slo)
4032            .arg(&shi)
4033            .arg(ctr)
4034            .arg(&temp);
4035        unsafe {
4036            b.launch(cfg)?;
4037        }
4038        Ok(())
4039    }
4040
4041    /// Graph-capturable `gumbel_perturb_filtered` (lane/step37-draft-graph-serving): the
4042    /// sampling-event counter comes from DEVICE memory (`ctr[0]`) and the filter stats
4043    /// (row_max, th) from DEVICE slots — the `filter_stats` outputs of the same captured
4044    /// body. Identical math (same Philox call, same lane mapping, same e0 filter test) to
4045    /// `gumbel_perturb_filtered` at stream_pos == ctr[0], row_max == mx[0], th == th_d[0]:
4046    /// the eager and graph FILTERED sampled chains produce bit-identical perturbations for
4047    /// the same (seed, counter, stats). Launch geometry mirrors the host-scalar wrapper.
4048    #[allow(clippy::too_many_arguments)]
4049    pub fn gumbel_perturb_filtered_ctr(
4050        &self,
4051        x: &CudaSlice<f32>,
4052        y: &mut CudaSlice<f32>,
4053        n: usize,
4054        seed: u64,
4055        ctr: &CudaSlice<u32>,
4056        temp: f32,
4057        stat_max: &CudaSlice<f32>,
4058        stat_th: &CudaSlice<f32>,
4059    ) -> Result<(), Box<dyn std::error::Error>> {
4060        let f = self.func("gumbel_perturb_filtered_ctr_f32");
4061        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
4062        let cfg = LaunchConfig {
4063            grid_dim: (n.div_ceil(256) as u32, 1, 1),
4064            block_dim: (256, 1, 1),
4065            shared_mem_bytes: 0,
4066        };
4067        let __s_b = self.gpu.stream();
4068        let mut b = __s_b.launch_builder(&f);
4069        b.arg(x)
4070            .arg(&mut *y)
4071            .arg(&ni)
4072            .arg(&slo)
4073            .arg(&shi)
4074            .arg(ctr)
4075            .arg(&temp)
4076            .arg(stat_max)
4077            .arg(stat_th);
4078        unsafe {
4079            b.launch(cfg)?;
4080        }
4081        Ok(())
4082    }
4083
4084    /// out[pair] = softmax_temp(x[rows[pair]])[ids[pair]] for npair (row, id) pairs; rows index
4085    /// into x with `row_stride` f32s per row. temp<=0: out = 1.0 iff id is the row argmax
4086    /// (smallest-index tie-break — matches the argmax-gate contract).
4087    pub fn softmax_gather(
4088        &self,
4089        x: &CudaSlice<f32>,
4090        row_stride: usize,
4091        ids: &CudaSlice<u32>,
4092        rows: &CudaSlice<i32>,
4093        out: &mut CudaSlice<f32>,
4094        n: usize,
4095        npair: usize,
4096        temp: f32,
4097    ) -> Result<(), Box<dyn std::error::Error>> {
4098        let f = self.func("softmax_gather_f32");
4099        let (ni, rs) = (n as i32, row_stride as i64);
4100        let np = npair as i32;
4101        let cfg = LaunchConfig {
4102            grid_dim: (npair as u32, 1, 1),
4103            block_dim: (256, 1, 1),
4104            shared_mem_bytes: 0,
4105        };
4106        let __s_b = self.gpu.stream();
4107        let mut b = __s_b.launch_builder(&f);
4108        b.arg(x)
4109            .arg(&rs)
4110            .arg(ids)
4111            .arg(rows)
4112            .arg(&mut *out)
4113            .arg(&ni)
4114            .arg(&np)
4115            .arg(&temp);
4116        unsafe {
4117            b.launch(cfg)?;
4118        }
4119        Ok(())
4120    }
4121
4122    /// Sample token from norm(max(0, softmax_temp(p) - softmax_temp(q))) (q = None -> plain
4123    /// categorical from softmax_temp(p)). Row stats (max, sumexp at temp) must be precomputed
4124    /// (softmax_gather's pass-1 values; see spec.rs caller). Deterministic fixed-order CDF walk.
4125    pub fn residual_sample(
4126        &self,
4127        p: &CudaSlice<f32>,
4128        q: Option<&CudaSlice<f32>>,
4129        n: usize,
4130        temp: f32,
4131        seed: u64,
4132        stream_pos: u32,
4133        out_tok: &mut CudaSlice<u32>,
4134    ) -> Result<(), Box<dyn std::error::Error>> {
4135        let f = self.func("residual_sample_f32");
4136        let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
4137        let nth = 1024u32;
4138        let cfg = LaunchConfig {
4139            grid_dim: (1, 1, 1),
4140            block_dim: (nth, 1, 1),
4141            shared_mem_bytes: 0,
4142        };
4143        let has_q: i32 = q.is_some() as i32;
4144        let qbuf = q.unwrap_or(p); // dummy when absent; kernel gates on has_q
4145        let __s_b = self.gpu.stream();
4146        let mut b = __s_b.launch_builder(&f);
4147        b.arg(p)
4148            .arg(qbuf)
4149            .arg(&has_q)
4150            .arg(&ni)
4151            .arg(&temp)
4152            .arg(&slo)
4153            .arg(&shi)
4154            .arg(&stream_pos)
4155            .arg(&mut *out_tok);
4156        unsafe {
4157            b.launch(cfg)?;
4158        }
4159        Ok(())
4160    }
4161
4162    /// Access the shared MoE residency cache (EDGE-1 §B), building it on first use under
4163    /// MEMRA_MOE_CACHE. The closure runs while the lock is held — keep it to lookup/admit/issue, not
4164    /// the GEMM. `max_block_bytes` sizes the slots (largest of gate/up/down). Returns the closure's
4165    /// result. If MEMRA_MOE_CACHE is unset this is never called (the caller checks the env first).
4166    pub fn with_moe_cache<R>(
4167        &self,
4168        max_block_bytes: usize,
4169        f: impl FnOnce(
4170            &mut crate::moe_cache::MoeSlotCache,
4171            &Engine,
4172        ) -> Result<R, Box<dyn std::error::Error>>,
4173    ) -> Result<R, Box<dyn std::error::Error>> {
4174        let mut guard = self.moe_cache.lock().unwrap();
4175        if guard.is_none() {
4176            *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
4177        }
4178        let cache = guard.as_mut().unwrap();
4179        f(cache, self)
4180    }
4181
4182    /// Freeze the already-built MoE residency set. This never constructs a cache: callers use it
4183    /// only after a real prefill has populated the machine-specific CPU/GPU working set.
4184    pub fn freeze_moe_cache(&self) {
4185        if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
4186            cache.freeze();
4187        }
4188    }
4189
4190    /// The current residency set as (layer, proj, ex) triples, or None if no cache was built.
4191    /// Never constructs a cache.
4192    pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
4193        self.moe_cache
4194            .lock()
4195            .unwrap()
4196            .as_ref()
4197            .map(crate::moe_cache::MoeSlotCache::export_residency)
4198    }
4199
4200    pub(crate) fn moe_cache_frozen(&self) -> bool {
4201        self.moe_cache
4202            .lock()
4203            .unwrap()
4204            .as_ref()
4205            .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
4206    }
4207
4208    /// A frozen heterogeneous CPU/GPU expert split cannot use Hy3's ordinary batched prefill
4209    /// efficiently: T>=PRIME_MIN_T bypasses the CPU backend and transiently rereads every missing
4210    /// expert through the GPU spill path. Replay the short prompt through decode after freezing,
4211    /// while leaving the profiling warmup's established batched behavior untouched.
4212    /// (`pub`: run-gen's #46 batched-prime gate skips itself when generation will take the
4213    /// tokenwise arm anyway.)
4214    pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
4215        crate::cpu_experts::configured()
4216            && self.moe_cache_frozen()
4217            && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
4218    }
4219
4220    /// Install the loaded model's exact retained expert-block inventory before lazy cache build.
4221    pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
4222        assert!(
4223            self.moe_cache.lock().unwrap().is_none(),
4224            "MoE cache layout configured after cache construction"
4225        );
4226        *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
4227    }
4228
4229    pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
4230        self.moe_cache_layout.lock().unwrap().clone()
4231    }
4232
4233    /// True if the MoE residency cache is enabled (MEMRA_MOE_CACHE set).
4234    pub fn moe_cache_enabled() -> bool {
4235        std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
4236    }
4237
4238    /// Snapshot the MoE cache counters (hits, misses, staged_bytes, n_slots) for the §D.4 PCIe gate.
4239    /// Returns None if the cache was never built (disabled or no MoE forward ran).
4240    pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
4241        let guard = self.moe_cache.lock().unwrap();
4242        guard
4243            .as_ref()
4244            .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
4245    }
4246
4247    /// Experimental CPU expert backend counters: completed layer calls, experts served, and the
4248    /// sum of backend wall nanoseconds. The timer includes explicit disk->RAM fills on cache misses;
4249    /// callers compare a before/after snapshot around a decode window.
4250    pub fn cpu_expert_stats(
4251        &self,
4252    ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
4253        crate::cpu_experts::configured().then(crate::cpu_experts::stats)
4254    }
4255
4256    /// Caller-blocked nanoseconds at CPU expert joins. Compare before/after snapshots to measure
4257    /// the backend tail that resident-GPU expert work did not hide.
4258    pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
4259        crate::cpu_experts::predictor_stats()
4260    }
4261
4262    pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
4263        crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
4264    }
4265
4266    /// CPU-routed expert selections grouped by how many of their three projections were already
4267    /// resident in HBM. This makes otherwise-stranded partial residency visible to tuning runs.
4268    pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
4269        crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
4270    }
4271
4272    /// Positioned-read proof-backend counters:
4273    /// `(reads, bytes, read_errors, short_reads, mmap_fallbacks, buffer_waits, ring_full)`.
4274    pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
4275        let guard = self.moe_cache.lock().unwrap();
4276        guard
4277            .as_ref()
4278            .and_then(|cache| cache.pread_stats())
4279            .map(|stats| {
4280                (
4281                    stats.reads,
4282                    stats.bytes,
4283                    stats.read_errors,
4284                    stats.short_reads,
4285                    stats.fallbacks,
4286                    stats.buffer_waits,
4287                    stats.ring_full,
4288                )
4289            })
4290    }
4291
4292    /// Spill configuration values that warned and substituted their documented defaults.
4293    pub fn spill_config_fallbacks(&self) -> u64 {
4294        crate::spill_pread::config_fallbacks()
4295    }
4296
4297    /// Reset the MoE cache perf counters (to separate warmup from steady-state windows).
4298    pub fn moe_cache_reset_counters(&self) {
4299        if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
4300            c.reset_counters();
4301        }
4302    }
4303
4304    pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4305        Ok(self.gpu.stream().clone_htod(v)?)
4306    }
4307
4308    /// `htod_bytes` with a mapped (uninit) tail pad: the wide-load expert dots read up to 6B
4309    /// past the final q4_0 block through their aligned window — the bytes never reach a
4310    /// result (funnelshift discards them) but must be mapped memory.
4311    pub fn htod_bytes_padded(
4312        &self,
4313        v: &[u8],
4314        pad: usize,
4315    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4316        let mut d = self.alloc_u8_uninit(v.len() + pad)?;
4317        {
4318            let mut view = d.slice_mut(0..v.len());
4319            self.gpu.stream().memcpy_htod(v, &mut view)?;
4320        }
4321        Ok(d)
4322    }
4323
4324    /// Device-to-device copy of `src` into `dst[off..off+len]` (f32). For in-place KV append.
4325    pub fn copy_into(
4326        &self,
4327        dst: &mut CudaSlice<f32>,
4328        off: usize,
4329        src: &CudaSlice<f32>,
4330        len: usize,
4331    ) -> Result<(), Box<dyn std::error::Error>> {
4332        let mut view = dst.slice_mut(off..off + len);
4333        self.gpu
4334            .stream()
4335            .memcpy_dtod(&src.slice(0..len), &mut view)?;
4336        Ok(())
4337    }
4338
4339    /// D2D copy with an offset on BOTH sides. `copy_into` always reads the source from 0,
4340    /// which cannot express "copy the TAIL of this buffer" — the shape a sliding-window draft
4341    /// KV export needs (lane/dspark-draft-plane-20260827).
4342    pub fn copy_range_into(
4343        &self,
4344        dst: &mut CudaSlice<f32>,
4345        dst_off: usize,
4346        src: &CudaSlice<f32>,
4347        src_off: usize,
4348        len: usize,
4349    ) -> Result<(), Box<dyn std::error::Error>> {
4350        let mut view = dst.slice_mut(dst_off..dst_off + len);
4351        self.gpu
4352            .stream()
4353            .memcpy_dtod(&src.slice(src_off..src_off + len), &mut view)?;
4354        Ok(())
4355    }
4356
4357    /// View a sub-range of a device buffer (for attending over [0..len) of a KV cache).
4358    /// u8 twin of copy_into (D2D byte-range copy at an offset).
4359    pub fn copy_u8_into(
4360        &self,
4361        dst: &mut CudaSlice<u8>,
4362        off: usize,
4363        src: &CudaSlice<u8>,
4364        len: usize,
4365    ) -> Result<(), Box<dyn std::error::Error>> {
4366        // try_slice_mut, not slice_mut: an out-of-bounds range here panics the GPU worker
4367        // thread and takes the whole server with it (2026-08-29 warm-turn-at-40k incident).
4368        // A bounds miss is a caller bug, but it must fail the request, not the fleet.
4369        let cap = dst.len();
4370        let mut view = dst.try_slice_mut(off..off + len).ok_or_else(|| {
4371            format!(
4372                "copy_u8_into dst range [{off},{}) exceeds capacity {cap}",
4373                off + len,
4374            )
4375        })?;
4376        self.gpu
4377            .stream()
4378            .memcpy_dtod(&src.slice(0..len), &mut view)?;
4379        Ok(())
4380    }
4381
4382    /// D2D byte-range copy with explicit source and destination offsets.
4383    pub fn copy_u8_range_into(
4384        &self,
4385        dst: &mut CudaSlice<u8>,
4386        dst_off: usize,
4387        src: &CudaSlice<u8>,
4388        src_off: usize,
4389        len: usize,
4390    ) -> Result<(), Box<dyn std::error::Error>> {
4391        // try_slice_mut for the same reason as copy_u8_into: bounds misses fail the request,
4392        // never panic the worker.
4393        let cap = dst.len();
4394        let mut dst_view = dst.try_slice_mut(dst_off..dst_off + len).ok_or_else(|| {
4395            format!(
4396                "copy_u8_range_into dst range [{dst_off},{}) exceeds capacity {cap}",
4397                dst_off + len,
4398            )
4399        })?;
4400        self.gpu
4401            .stream()
4402            .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
4403        Ok(())
4404    }
4405
4406    /// Resolve an absolute append slot to the Step35 SWA layer's physical rows. At wrap, copy
4407    /// only the aligned live prefix through temporary device storage and rebase it at row zero,
4408    /// keeping the audited attention range contiguous without changing its absolute start.
4409    /// #[track_caller]: every ring-backed append that REBASES sets the plane's `base`, and a
4410    /// later append or rewind that needs a lower row is then refused. Three attempts at the
4411    /// SWA-ring lap failed because the writer that actually moved `base` was never the site being
4412    /// patched — the bare "SWA ring lapped required rows" message named neither the caller nor
4413    /// what it retained. Cost of the annotation is nothing; cost of not having it was two wrong
4414    /// fixes on hardware.
4415    #[track_caller]
4416    pub fn prepare_kv_append(
4417        &self,
4418        kv: &mut crate::cache::KvLayer,
4419        retain_from: usize,
4420        append_rows: usize,
4421    ) -> Result<usize, Box<dyn std::error::Error>> {
4422        let caller = std::panic::Location::caller();
4423        let base_before = kv.ring.as_ref().map(|r| r.base());
4424        let Some(plan) = kv
4425            .ring
4426            .as_ref()
4427            .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
4428            .transpose()
4429            .map_err(|err| -> Box<dyn std::error::Error> {
4430                format!(
4431                    "{err} [append len={} retain_from={retain_from} append_rows={append_rows}                      base={base_before:?} called from {caller}]",
4432                    kv.len
4433                )
4434                .into()
4435            })?
4436        else {
4437            return Ok(kv.len);
4438        };
4439        match plan {
4440            crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
4441            crate::cache::KvRingAppend::Rebase {
4442                src_row,
4443                keep_rows,
4444                new_base,
4445                write_row,
4446            } => {
4447                if keep_rows > 0 {
4448                    let k_len = keep_rows * kv.k_tok_bytes;
4449                    let v_len = keep_rows * kv.v_tok_bytes;
4450                    let mut k_tmp = self.alloc_u8_uninit(k_len)?;
4451                    let mut v_tmp = self.alloc_u8_uninit(v_len)?;
4452                    self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
4453                    self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
4454                    self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
4455                    self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
4456                }
4457                // One line per distinct (caller, new_base) so the writers that move `base` are
4458                // enumerable from a single run instead of inferred from which error fires.
4459                if std::env::var("MEMRA_KV_REBASE_TRACE").as_deref() == Ok("1") {
4460                    eprintln!(
4461                        "[kv-rebase] new_base={new_base} keep_rows={keep_rows} len={} \
4462                         retain_from={retain_from} called from {caller}",
4463                        kv.len
4464                    );
4465                }
4466                kv.ring.as_mut().unwrap().apply_rebase(new_base);
4467                // The dcw draft arm's device mirror of the ring base (see KvLayer::base_d).
4468                // Rebase is the ONLY writer of `base`, and rebases run host-side outside any
4469                // captured region, so this one line keeps the device view exact.
4470                if let Some(base_d) = kv.base_d.as_mut() {
4471                    self.set_i32_one(base_d, new_base as i32)?;
4472                }
4473                Ok(write_row)
4474            }
4475        }
4476    }
4477
4478    /// H2D write of `src` into `dst[off..off+src.len()]` (u8). In-place row updates for the
4479    /// adaptive trim head: no realloc, so captured graphs keep their baked addresses.
4480    pub fn htod_u8_into(
4481        &self,
4482        dst: &mut CudaSlice<u8>,
4483        off: usize,
4484        src: &[u8],
4485    ) -> Result<(), Box<dyn std::error::Error>> {
4486        let mut view = dst.slice_mut(off..off + src.len());
4487        self.gpu.stream().memcpy_htod(src, &mut view)?;
4488        Ok(())
4489    }
4490
4491    pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
4492        b.slice(0..len)
4493    }
4494
4495    /// View the first `len` BYTES of a u8 device buffer (quantized KV cache: [0..t_kv*tok_bytes)).
4496    /// Byte-range view (gemma4 R6 window offset into the quantized KV stream).
4497    pub fn view_u8_range<'a>(
4498        &self,
4499        b: &'a CudaSlice<u8>,
4500        start: usize,
4501        end: usize,
4502    ) -> cudarc::driver::CudaView<'a, u8> {
4503        b.slice(start..end)
4504    }
4505    pub fn view_u8<'a>(
4506        &self,
4507        b: &'a CudaSlice<u8>,
4508        len: usize,
4509    ) -> cudarc::driver::CudaView<'a, u8> {
4510        b.slice(0..len)
4511    }
4512
4513    /// Append-quantize ONE token's post-RoPE K (q8_0) and V (q5_1) into the resident byte caches at
4514    /// token index `t` (KVQUANT-PLAN §C). One CTA (one warp) per 32-element block; the kernel writes
4515    /// the f16 scale(s) + packed quants for K and V. k_row/v_row are f32 [kv_dim_k]/[kv_dim_v].
4516    pub fn append_kv_quantized(
4517        &self,
4518        k_row: &CudaSlice<f32>,
4519        v_row: &CudaSlice<f32>,
4520        kc: &mut CudaSlice<u8>,
4521        vc: &mut CudaSlice<u8>,
4522        t: usize,
4523        kv_dim_k: usize,
4524        kv_dim_v: usize,
4525        k_tok_bytes: usize,
4526        v_tok_bytes: usize,
4527        g: bool,
4528    ) -> Result<(), Box<dyn std::error::Error>> {
4529        let f = if g {
4530            self.func_g("append_quantize_kv_q8_0_q5_1")
4531        } else {
4532            self.func("append_quantize_kv_q8_0_q5_1")
4533        };
4534        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
4535        let cfg = LaunchConfig {
4536            grid_dim: (nblk, 1, 1),
4537            block_dim: (32, 1, 1),
4538            shared_mem_bytes: 0,
4539        };
4540        let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
4541        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
4542        let __s_b = self.gpu.stream();
4543        let mut b = __s_b.launch_builder(&f);
4544        b.arg(k_row)
4545            .arg(v_row)
4546            .arg(kc)
4547            .arg(vc)
4548            .arg(&ti)
4549            .arg(&kdk)
4550            .arg(&kdv)
4551            .arg(&ktb)
4552            .arg(&vtb);
4553        unsafe {
4554            b.launch(cfg)?;
4555        }
4556        Ok(())
4557    }
4558
4559    /// Device-counter variant of `append_kv_quantized` (CUDA-GRAPH-PLAN Phase 2): the write slot
4560    /// `t` is read from `t_dev[0]` (a resident device i32[1]) instead of a host int arg, so the
4561    /// launch args are FIXED across decode steps (graph-capturable). Identical quant math.
4562    pub fn append_kv_quantized_dc(
4563        &self,
4564        k_row: &CudaSlice<f32>,
4565        v_row: &CudaSlice<f32>,
4566        kc: &mut CudaSlice<u8>,
4567        vc: &mut CudaSlice<u8>,
4568        t_dev: &CudaSlice<i32>,
4569        kv_dim_k: usize,
4570        kv_dim_v: usize,
4571        k_tok_bytes: usize,
4572        v_tok_bytes: usize,
4573        g: bool,
4574    ) -> Result<(), Box<dyn std::error::Error>> {
4575        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
4576        let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
4577        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
4578        // PDL wave-B2: flash-module flavor mirrors the builder path's g flag exactly.
4579        if Self::pdl_on() && Self::pdl_wb_on() {
4580            use cudarc::driver::{DevicePtr, DevicePtrMut};
4581            let s = &self.gpu.stream();
4582            let (pk, _g0) = k_row.device_ptr(s);
4583            let (pv, _g1) = v_row.device_ptr(s);
4584            let (pkc, _g2) = kc.device_ptr_mut(s);
4585            let (pvc, _g3) = vc.device_ptr_mut(s);
4586            let (pt, _g4) = t_dev.device_ptr(s);
4587            let mut ps = [
4588                &pk as *const _ as *mut std::ffi::c_void,
4589                &pv as *const _ as *mut _,
4590                &pkc as *const _ as *mut _,
4591                &pvc as *const _ as *mut _,
4592                &pt as *const _ as *mut _,
4593                &kdk as *const _ as *mut _,
4594                &kdv as *const _ as *mut _,
4595                &ktb as *const _ as *mut _,
4596                &vtb as *const _ as *mut _,
4597            ];
4598            unsafe {
4599                self.launch_pdl_flash(
4600                    g,
4601                    "append_quantize_kv_q8_0_q5_1_dc",
4602                    (nblk, 1, 1),
4603                    (32, 1, 1),
4604                    0,
4605                    &mut ps,
4606                )?;
4607            }
4608            return Ok(());
4609        }
4610        let f = if g {
4611            self.func_g("append_quantize_kv_q8_0_q5_1_dc")
4612        } else {
4613            self.func("append_quantize_kv_q8_0_q5_1_dc")
4614        };
4615        let cfg = LaunchConfig {
4616            grid_dim: (nblk, 1, 1),
4617            block_dim: (32, 1, 1),
4618            shared_mem_bytes: 0,
4619        };
4620        let __s_b = self.gpu.stream();
4621        let mut b = __s_b.launch_builder(&f);
4622        b.arg(k_row)
4623            .arg(v_row)
4624            .arg(kc)
4625            .arg(vc)
4626            .arg(t_dev)
4627            .arg(&kdk)
4628            .arg(&kdv)
4629            .arg(&ktb)
4630            .arg(&vtb);
4631        unsafe {
4632            b.launch(cfg)?;
4633        }
4634        Ok(())
4635    }
4636
4637    /// Append-quantize T token rows in one shot (BATCHED PROMPT PRIME). k_rows/v_rows are
4638    /// token-major [T, kv_dim] post-RoPE f32; rows land at cache slots t0..t0+T. Default = the
4639    /// batched `_rows` kernel: one (nblk, T) launch whose per-(block,token) warp program is the
4640    /// per-token append kernel verbatim -> every written row is BIT-IDENTICAL to T sequential
4641    /// `append_kv_quantized_view` calls (kernel_check pins the bytes). MEMRA_PRIME_APPEND_LOOP=1
4642    /// forces the T-launch per-row loop (the A/B seam that measured the launch overhead).
4643    #[allow(clippy::too_many_arguments)]
4644    pub fn append_kv_quantized_rows(
4645        &self,
4646        k_rows: &CudaSlice<f32>,
4647        v_rows: &CudaSlice<f32>,
4648        kc: &mut CudaSlice<u8>,
4649        vc: &mut CudaSlice<u8>,
4650        t0: usize,
4651        t: usize,
4652        kv_dim_k: usize,
4653        kv_dim_v: usize,
4654        k_tok_bytes: usize,
4655        v_tok_bytes: usize,
4656        g: bool,
4657    ) -> Result<(), Box<dyn std::error::Error>> {
4658        if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
4659            for i in 0..t {
4660                let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
4661                let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
4662                self.append_kv_quantized_view(
4663                    &k_row,
4664                    &v_row,
4665                    kc,
4666                    vc,
4667                    t0 + i,
4668                    kv_dim_k,
4669                    kv_dim_v,
4670                    k_tok_bytes,
4671                    v_tok_bytes,
4672                    g,
4673                )?;
4674            }
4675            return Ok(());
4676        }
4677        let f = if g {
4678            self.func_g("append_quantize_kv_q8_0_q5_1_rows")
4679        } else {
4680            self.func("append_quantize_kv_q8_0_q5_1_rows")
4681        };
4682        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
4683        let cfg = LaunchConfig {
4684            grid_dim: (nblk, t as u32, 1),
4685            block_dim: (32, 1, 1),
4686            shared_mem_bytes: 0,
4687        };
4688        let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
4689        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
4690        let __s_b = self.gpu.stream();
4691        let mut b = __s_b.launch_builder(&f);
4692        b.arg(k_rows)
4693            .arg(v_rows)
4694            .arg(kc)
4695            .arg(vc)
4696            .arg(&t0i)
4697            .arg(&kdk)
4698            .arg(&kdv)
4699            .arg(&ktb)
4700            .arg(&vtb);
4701        unsafe {
4702            b.launch(cfg)?;
4703        }
4704        Ok(())
4705    }
4706
4707    /// Increment a device i32[1] counter in place (p[0] += 1) via the resident `inc_i32` kernel.
4708    /// Used to advance the device-resident seqlen/pos counters inside the decode-dc path (and,
4709    /// later, inside a captured graph) without a host round-trip.
4710    pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
4711        let f = self.func("inc_i32");
4712        let cfg = LaunchConfig {
4713            grid_dim: (1, 1, 1),
4714            block_dim: (1, 1, 1),
4715            shared_mem_bytes: 0,
4716        };
4717        let __s_b = self.gpu.stream();
4718        let mut b = __s_b.launch_builder(&f);
4719        b.arg(p);
4720        unsafe {
4721            b.launch(cfg)?;
4722        }
4723        Ok(())
4724    }
4725
4726    /// Like `append_kv_quantized` but k_row/v_row are CudaViews (one token's row sliced out of a
4727    /// token-major [T, kv_dim] activation buffer — the MTP verify path appends T tokens).
4728    pub fn append_kv_quantized_view(
4729        &self,
4730        k_row: &cudarc::driver::CudaView<f32>,
4731        v_row: &cudarc::driver::CudaView<f32>,
4732        kc: &mut CudaSlice<u8>,
4733        vc: &mut CudaSlice<u8>,
4734        t: usize,
4735        kv_dim_k: usize,
4736        kv_dim_v: usize,
4737        k_tok_bytes: usize,
4738        v_tok_bytes: usize,
4739        g: bool,
4740    ) -> Result<(), Box<dyn std::error::Error>> {
4741        let stream = self.gpu.stream();
4742        ensure_tensor_stream_device(k_row, &stream, "append_kv_quantized_view.k_row")?;
4743        ensure_tensor_stream_device(v_row, &stream, "append_kv_quantized_view.v_row")?;
4744        ensure_tensor_stream_device(kc, &stream, "append_kv_quantized_view.k_cache")?;
4745        ensure_tensor_stream_device(vc, &stream, "append_kv_quantized_view.v_cache")?;
4746        let f = if g {
4747            self.func_g("append_quantize_kv_q8_0_q5_1")
4748        } else {
4749            self.func("append_quantize_kv_q8_0_q5_1")
4750        };
4751        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
4752        let cfg = LaunchConfig {
4753            grid_dim: (nblk, 1, 1),
4754            block_dim: (32, 1, 1),
4755            shared_mem_bytes: 0,
4756        };
4757        let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
4758        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
4759        let mut b = stream.launch_builder(&f);
4760        b.arg(k_row)
4761            .arg(v_row)
4762            .arg(kc)
4763            .arg(vc)
4764            .arg(&ti)
4765            .arg(&kdk)
4766            .arg(&kdv)
4767            .arg(&ktb)
4768            .arg(&vtb);
4769        unsafe {
4770            b.launch(cfg)?;
4771        }
4772        Ok(())
4773    }
4774
4775    /// Device-to-device copy of a CudaView `src` into `dst[off..off+len]` (f32). Like `copy_into`
4776    /// but the source is a sub-view (e.g. one column of a token-major activation buffer).
4777    pub fn copy_view_into(
4778        &self,
4779        dst: &mut CudaSlice<f32>,
4780        off: usize,
4781        src: &cudarc::driver::CudaView<f32>,
4782        len: usize,
4783    ) -> Result<(), Box<dyn std::error::Error>> {
4784        let mut view = dst.slice_mut(off..off + len);
4785        self.gpu
4786            .stream()
4787            .memcpy_dtod(&src.slice(0..len), &mut view)?;
4788        Ok(())
4789    }
4790
4791    /// Real device-to-device COPY of `src` into a freshly allocated buffer (NOT an Arc clone).
4792    /// Used for cache snapshots (MTP-PLAN §D.4): `CudaSlice::clone()` only bumps a refcount and
4793    /// would alias the live buffer; this allocs new device memory and memcpy_dtod's the contents.
4794    pub fn clone_dtod(
4795        &self,
4796        src: &CudaSlice<f32>,
4797    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4798        let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
4799        self.gpu.stream().memcpy_dtod(src, &mut dst)?;
4800        Ok(dst)
4801    }
4802
4803    /// D2D row extraction: copy a view (e.g. one row of a [B, n] batch buffer) into `dst`.
4804    /// Stream-ordered, async — decode_batch's per-sequence row plumbing.
4805    pub fn dtod_copy_view(
4806        &self,
4807        src: &cudarc::driver::CudaView<f32>,
4808        dst: &mut CudaSlice<f32>,
4809    ) -> Result<(), Box<dyn std::error::Error>> {
4810        self.gpu.stream().memcpy_dtod(src, dst)?;
4811        Ok(())
4812    }
4813
4814    /// D2D i8 twin of `dtod_copy_view` (q8_1 activation rows).
4815    pub fn dtod_copy_view_i8(
4816        &self,
4817        src: &cudarc::driver::CudaView<i8>,
4818        dst: &mut CudaSlice<i8>,
4819    ) -> Result<(), Box<dyn std::error::Error>> {
4820        self.gpu.stream().memcpy_dtod(src, dst)?;
4821        Ok(())
4822    }
4823
4824    /// D2D row placement: copy `src` into `dst[offset .. offset+src.len()]`.
4825    pub fn dtod_copy_into(
4826        &self,
4827        src: &CudaSlice<f32>,
4828        dst: &mut CudaSlice<f32>,
4829        offset: usize,
4830    ) -> Result<(), Box<dyn std::error::Error>> {
4831        let n = src.len();
4832        let mut dv = dst.slice_mut(offset..offset + n);
4833        self.gpu.stream().memcpy_dtod(src, &mut dv)?;
4834        Ok(())
4835    }
4836
4837    /// Batched uniform-size D2D copy (engine-bundle slice 1, DSF-ROUNDCOST-20260820 §1.1):
4838    /// `n` disjoint regions of `words` f32 each; `table` = [src_0..src_{n-1}, dst_0..dst_{n-1}]
4839    /// raw device pointers. ONE kernel launch replaces `n` memcpy_dtod dispatches — the dspark
4840    /// round's snap/commit copy dribble (~0.9 ms/round of dispatch serialization measured).
4841    /// Bytes and stream order are identical to the memcpy sequence it replaces.
4842    pub fn copy_batch_uniform_f32(
4843        &self,
4844        table: &CudaSlice<u64>,
4845        n: usize,
4846        words: usize,
4847    ) -> Result<(), Box<dyn std::error::Error>> {
4848        if n == 0 || words == 0 {
4849            return Ok(());
4850        }
4851        debug_assert!(
4852            table.len() >= 2 * n,
4853            "pointer table must hold n srcs + n dsts"
4854        );
4855        let f = self.func("copy_batch_uniform_f32");
4856        // Enough blocks to stream a multi-MB region, few enough that (chunks x n) stays a
4857        // sane grid: 512K-word ssm regions get 48 grid-striding blocks each.
4858        let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
4859        let (ni, wi) = (n as i32, words as i32);
4860        let cfg = LaunchConfig {
4861            grid_dim: (chunks, n as u32, 1),
4862            block_dim: (256, 1, 1),
4863            shared_mem_bytes: 0,
4864        };
4865        let __s = self.gpu.stream();
4866        let mut b = __s.launch_builder(&f);
4867        b.arg(table).arg(&ni).arg(&wi);
4868        unsafe {
4869            b.launch(cfg)?;
4870        }
4871        Ok(())
4872    }
4873
4874    /// H2D refresh of an EXISTING u64 pointer table IN PLACE (stable pointer — the batched
4875    /// state-copy tables are refreshed per round because the GDN ssm handles ping-pong).
4876    pub fn htod_u64_into(
4877        &self,
4878        v: &[u64],
4879        dst: &mut CudaSlice<u64>,
4880    ) -> Result<(), Box<dyn std::error::Error>> {
4881        let mut view = dst.slice_mut(0..v.len());
4882        self.gpu.stream().memcpy_htod(v, &mut view)?;
4883        Ok(())
4884    }
4885
4886    /// Indirect-source copy (engine-bundle slice 3): the src ADDRESS is loaded from a
4887    /// device pointer-table entry at run time, so a captured graph follows the gdn
4888    /// ping-pong through the same table its scan kernels read — a baked memcpy node
4889    /// would keep the capture-time physical buffer. `dst_off`/`words` in f32 elements.
4890    pub fn copy_indirect_src_f32(
4891        &self,
4892        src_entry: &cudarc::driver::CudaView<u64>,
4893        dst: &mut CudaSlice<f32>,
4894        dst_off: usize,
4895        words: usize,
4896    ) -> Result<(), Box<dyn std::error::Error>> {
4897        let f = self.func("copy_indirect_src_f32");
4898        let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
4899        let wi = words as i32;
4900        let cfg = LaunchConfig {
4901            grid_dim: (chunks, 1, 1),
4902            block_dim: (256, 1, 1),
4903            shared_mem_bytes: 0,
4904        };
4905        let mut dv = dst.slice_mut(dst_off..dst_off + words);
4906        let __s = self.gpu.stream();
4907        let mut b = __s.launch_builder(&f);
4908        b.arg(src_entry).arg(&mut dv).arg(&wi);
4909        unsafe {
4910            b.launch(cfg)?;
4911        }
4912        Ok(())
4913    }
4914
4915    /// Uninitialized i8 device buffer (decode_batch q8_1 row scratch).
4916    pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
4917        self.alloc_uninit::<i8>(n)
4918    }
4919
4920    /// Resident-quantized linear (Stage-A: f32 dequant-in-kernel). y[m,out]=x[m,in]@W[out,in]^T.
4921    pub fn qmatvec(
4922        &self,
4923        w: &CudaSlice<u8>,
4924        x: &CudaSlice<f32>,
4925        m: usize,
4926        in_f: usize,
4927        out_f: usize,
4928        qtype: i32,
4929        row_bytes: usize,
4930    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4931        let f = self.func("qmatvec_f32");
4932        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
4933        let cfg = LaunchConfig {
4934            grid_dim: (out_f as u32, m as u32, 1),
4935            block_dim: (256, 1, 1),
4936            shared_mem_bytes: 0,
4937        };
4938        let (inf, outf, mi, qt, rb) =
4939            (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4940        let __s_b = self.gpu.stream();
4941        let mut b = __s_b.launch_builder(&f);
4942        b.arg(w)
4943            .arg(x)
4944            .arg(&mut y)
4945            .arg(&inf)
4946            .arg(&outf)
4947            .arg(&mi)
4948            .arg(&qt)
4949            .arg(&rb);
4950        unsafe {
4951            b.launch(cfg)?;
4952        }
4953        Ok(y)
4954    }
4955
4956    /// Allocate a reusable u8 GPU scratch buffer (for staged expert weights).
4957    pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4958        let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
4959        self.keep_if_capturing(&s);
4960        Ok(s)
4961    }
4962
4963    /// Uninitialized u8 scratch — skips alloc_zeros' memset. ONLY for staging buffers whose read
4964    /// range is fully overwritten by a stage_expert H2D before any kernel reads it (LAUNCH-STRUCTURE
4965    /// STAGE 2: the per-layer MoE scratch trio was 3 dead ~1MB memsets per layer per decode token).
4966    pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4967        let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
4968        self.keep_if_capturing(&s);
4969        Ok(s)
4970    }
4971
4972    /// Zero a SUB-RANGE of an f32 buffer (CudaViewMut) — the row-sized memset the moe_out
4973    /// memset-elision uses for tokens that fall off the gdec fast path (LAUNCH-STRUCTURE STAGE 2).
4974    pub fn memset_zeros_view(
4975        &self,
4976        dst: &mut cudarc::driver::CudaViewMut<f32>,
4977    ) -> Result<(), Box<dyn std::error::Error>> {
4978        self.gpu.stream().memset_zeros(dst)?;
4979        Ok(())
4980    }
4981
4982    /// EDGE-1 staging: copy `host_bytes` (a sub-slice of a HostExps buffer) into `scratch`
4983    /// at byte offset `off` (async H2D on the default stream). Length is host_bytes.len().
4984    /// The qmatvec_view that reads `scratch[off..]` is enqueued on the SAME stream after this,
4985    /// so ordering is guaranteed without an explicit sync (Stage-1; Stage-2 prefetch on a 2nd
4986    /// stream would require an event).
4987    pub fn stage_expert(
4988        &self,
4989        host_bytes: &[u8],
4990        scratch: &mut CudaSlice<u8>,
4991        off: usize,
4992    ) -> Result<(), Box<dyn std::error::Error>> {
4993        let mut dst = scratch.slice_mut(off..off + host_bytes.len()); // CudaViewMut<u8>
4994        self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; // accepts &[u8] HostSlice src
4995        Ok(())
4996    }
4997
4998    /// EDGE-1 §A: fused MoE router. `logits` is the router output [t, n_expert] (device, f32, the
4999    /// `gate_inp @ z` result). Returns (sel_idx [t, n_used] i32, sel_w [t, n_used] f32): the top-k
5000    /// expert ids (DESC by prob, ascending-index tiebreak) and renormalized weights. Replaces the
5001    /// host dtoh + softmax-256 + stable DESC top-8 sort + renorm (hybrid_forward.rs ~281-298).
5002    /// One CTA per token row, 256 threads (one per expert).
5003    pub fn moe_router_topk(
5004        &self,
5005        logits: &CudaSlice<f32>,
5006        t: usize,
5007        n_expert: usize,
5008        n_used: usize,
5009    ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
5010        let f = self.func("moe_router_topk_f32");
5011        let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?; // kernel fully overwrites
5012        let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?; // kernel fully overwrites
5013        let cfg = LaunchConfig {
5014            grid_dim: (t as u32, 1, 1),
5015            block_dim: (n_expert as u32, 1, 1),
5016            shared_mem_bytes: 0,
5017        };
5018        let (ne, nu) = (n_expert as i32, n_used as i32);
5019        let __s_b = self.gpu.stream();
5020        let mut b = __s_b.launch_builder(&f);
5021        b.arg(logits)
5022            .arg(&mut sel_idx)
5023            .arg(&mut sel_w)
5024            .arg(&ne)
5025            .arg(&nu);
5026        unsafe {
5027            b.launch(cfg)?;
5028        }
5029        Ok((sel_idx, sel_w))
5030    }
5031
5032    /// gemma4 twin: per-expert output scale folded into the topk renorm write (replaces the
5033    /// separate moe_w_exscale launch; value chain identical: (w/ws) * s[sel]).
5034    pub fn moe_router_topk_scaled(
5035        &self,
5036        logits: &CudaSlice<f32>,
5037        t: usize,
5038        n_expert: usize,
5039        n_used: usize,
5040        ex_scale: &CudaSlice<f32>,
5041    ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
5042        // barrier-lean v2 twin (per-warp top-k + one-warp merge) FALSIFIED 2026-07-14:
5043        // bit-identical streams but −1.4% (26B plain N=3 interleaved) — at t=1 the grid is
5044        // ONE block, so the 6.6us is launch/dependency overhead, not the barrier chain;
5045        // fewer barriers bought nothing and the merge structure cost. jsonl is the record.
5046        let f = self.func("moe_router_topk_scaled_f32");
5047        let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
5048        let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
5049        let cfg = LaunchConfig {
5050            grid_dim: (t as u32, 1, 1),
5051            block_dim: (n_expert as u32, 1, 1),
5052            shared_mem_bytes: 0,
5053        };
5054        let (ne, nu) = (n_expert as i32, n_used as i32);
5055        let __s_b = self.gpu.stream();
5056        let mut b = __s_b.launch_builder(&f);
5057        b.arg(logits)
5058            .arg(&mut sel_idx)
5059            .arg(&mut sel_w)
5060            .arg(&ne)
5061            .arg(&nu)
5062            .arg(ex_scale);
5063        unsafe {
5064            b.launch(cfg)?;
5065        }
5066        Ok((sel_idx, sel_w))
5067    }
5068
5069    /// LAUNCH-STRUCTURE STAGE 1 (2026-07-05): fused router + SINGLE-SYNC host readback. The old
5070    /// MEMRA_FUSED_ROUTER path lost 2% at t=1 because it paid TWO full stream syncs (dtoh_i32 then
5071    /// dtoh, each = clone_dtoh + synchronize) + two alloc_zeros memsets per MoE layer, where the
5072    /// host route pays ONE sync on the 1KB logits dtoh. This variant: uninit outputs (kernel fully
5073    /// overwrites), both DtoH copies issued ASYNC into a persistent PINNED host staging buffer
5074    /// (flags=0 — cacheable, NOT cudarc's WRITECOMBINED default, so the host-side reads of sel/w
5075    /// stay cached), then ONE synchronize. Numerics identical to `moe_router_topk` (same kernel).
5076    pub fn moe_router_topk_host(
5077        &self,
5078        logits: &CudaSlice<f32>,
5079        t: usize,
5080        n_expert: usize,
5081        n_used: usize,
5082    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
5083        let f = self.func("moe_router_topk_f32");
5084        let n = t * n_used;
5085        let mut sel_idx = self.alloc_uninit::<i32>(n)?;
5086        let mut sel_w = self.alloc_uninit::<f32>(n)?;
5087        let cfg = LaunchConfig {
5088            grid_dim: (t as u32, 1, 1),
5089            block_dim: (n_expert as u32, 1, 1),
5090            shared_mem_bytes: 0,
5091        };
5092        let (ne, nu) = (n_expert as i32, n_used as i32);
5093        let __s_b = self.gpu.stream();
5094        let mut b = __s_b.launch_builder(&f);
5095        b.arg(logits)
5096            .arg(&mut sel_idx)
5097            .arg(&mut sel_w)
5098            .arg(&ne)
5099            .arg(&nu);
5100        unsafe {
5101            b.launch(cfg)?;
5102        }
5103        // single-sync readback: sel (i32) at offset 0, w (f32) at offset n*4 of the pinned stage.
5104        let bytes = n * 8;
5105        let mut guard = self.router_stage.lock().unwrap();
5106        if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
5107            *guard = Some(PinnedStage::new(bytes.max(4096))?);
5108        }
5109        let stage = guard.as_mut().unwrap();
5110        let (si, sw) = unsafe {
5111            (
5112                std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
5113                std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
5114            )
5115        };
5116        self.gpu.stream().memcpy_dtoh(&sel_idx, si)?; // async (pinned dst)
5117        self.gpu.stream().memcpy_dtoh(&sel_w, sw)?; // async (pinned dst)
5118        self.gpu.stream().synchronize()?; // ONE sync for both
5119        Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
5120    }
5121
5122    /// Device sigmoid router for Step-3.7 / DeepSeek-V3-class MoEs. `correction_bias` is added
5123    /// only to the top-k key; returned weights use the un-biased sigmoid score. `active` masks
5124    /// original expert ids before top-k. Exact key ties choose the smaller original id.
5125    #[allow(clippy::too_many_arguments)]
5126    pub fn moe_router_sigmoid_topk(
5127        &self,
5128        logits: &CudaSlice<f32>,
5129        t: usize,
5130        n_expert: usize,
5131        n_used: usize,
5132        active_count: usize,
5133        correction_bias: &CudaSlice<f32>,
5134        active: &CudaSlice<u8>,
5135        scaling_factor: f32,
5136        route_norm: bool,
5137    ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
5138        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
5139        if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
5140            return Err(format!(
5141                "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
5142            )
5143            .into());
5144        }
5145        if logits.len() < t * n_expert
5146            || correction_bias.len() != n_expert
5147            || active.len() != n_expert
5148        {
5149            return Err(format!(
5150                "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
5151                logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
5152            ).into());
5153        }
5154        let f = self.func(crate::sigmoid_topk_kernel(
5155            crate::sig_expf_dev_on(),
5156            crate::topk_fast_on(),
5157            n_used,
5158        ));
5159        let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
5160        let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
5161        let threads = n_expert.div_ceil(32) * 32;
5162        let cfg = LaunchConfig {
5163            grid_dim: (t as u32, 1, 1),
5164            block_dim: (threads as u32, 1, 1),
5165            shared_mem_bytes: 0,
5166        };
5167        let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
5168        let __s_b = self.gpu.stream();
5169        let mut b = __s_b.launch_builder(&f);
5170        b.arg(logits)
5171            .arg(correction_bias)
5172            .arg(active)
5173            .arg(&mut sel_idx)
5174            .arg(&mut sel_w)
5175            .arg(&ne)
5176            .arg(&nu)
5177            .arg(&scaling_factor)
5178            .arg(&rn);
5179        unsafe {
5180            b.launch(cfg)?;
5181        }
5182        Ok((sel_idx, sel_w))
5183    }
5184
5185    /// `moe_router_sigmoid_topk` writing into caller-owned buffers (alloc-free: child graphs
5186    /// cannot contain mem nodes, so the token-graph e-sections pre-own every output).
5187    #[allow(clippy::too_many_arguments)]
5188    /// Ring a doorbell flag at a RAW device address (see `memra_ring_flag`): one store of
5189    /// `value`, fenced. Used by a peer rank to signal join readiness into root memory, where
5190    /// the model engine can wait on it with a same-device stream memop.
5191    pub fn ring_flag_raw(&self, ptr: u64, value: u32) -> Result<(), Box<dyn std::error::Error>> {
5192        if ptr == 0 {
5193            return Err("ring_flag_raw: unarmed flag".into());
5194        }
5195        let f = self.func("memra_ring_flag");
5196        let cfg = LaunchConfig {
5197            grid_dim: (1, 1, 1),
5198            block_dim: (32, 1, 1),
5199            shared_mem_bytes: 0,
5200        };
5201        let __s_b = self.gpu.stream();
5202        let mut b = __s_b.launch_builder(&f);
5203        b.arg(&ptr).arg(&value);
5204        unsafe {
5205            b.launch(cfg)?;
5206        }
5207        Ok(())
5208    }
5209
5210    /// One-launch mirror of a routed selection (`sel` int32 + `route_w` f32) — see
5211    /// `moe_sel_w_mirror`. Replaces the two tiny D2D copies the rank pull used to issue.
5212    pub fn moe_sel_w_mirror(
5213        &self,
5214        sel_src: &CudaSlice<i32>,
5215        w_src: &CudaSlice<f32>,
5216        sel_dst: &mut CudaSlice<i32>,
5217        w_dst: &mut CudaSlice<f32>,
5218        n: usize,
5219    ) -> Result<(), Box<dyn std::error::Error>> {
5220        if n == 0
5221            || n > 32
5222            || sel_src.len() < n
5223            || w_src.len() < n
5224            || sel_dst.len() < n
5225            || w_dst.len() < n
5226        {
5227            return Err(format!("moe_sel_w_mirror geometry n={n}").into());
5228        }
5229        let f = self.func("moe_sel_w_mirror");
5230        let cfg = LaunchConfig {
5231            grid_dim: (1, 1, 1),
5232            block_dim: (32, 1, 1),
5233            shared_mem_bytes: 0,
5234        };
5235        let ni = n as i32;
5236        let __s_b = self.gpu.stream();
5237        let mut b = __s_b.launch_builder(&f);
5238        b.arg(sel_src).arg(w_src).arg(sel_dst).arg(w_dst).arg(&ni);
5239        unsafe {
5240            b.launch(cfg)?;
5241        }
5242        Ok(())
5243    }
5244
5245    pub fn moe_router_sigmoid_topk_into(
5246        &self,
5247        logits: &CudaSlice<f32>,
5248        t: usize,
5249        n_expert: usize,
5250        n_used: usize,
5251        active_count: usize,
5252        correction_bias: &CudaSlice<f32>,
5253        active: &CudaSlice<u8>,
5254        scaling_factor: f32,
5255        route_norm: bool,
5256        sel_idx: &mut CudaSlice<i32>,
5257        sel_w: &mut CudaSlice<f32>,
5258    ) -> Result<(), Box<dyn std::error::Error>> {
5259        crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
5260        if n_expert == 0
5261            || n_expert > 1024
5262            || n_used == 0
5263            || n_used > 32 // the kernels' shared pick cache (s_pick_w) is sized 32
5264            || n_used > n_expert
5265            || logits.len() < t * n_expert
5266            || correction_bias.len() != n_expert
5267            || active.len() != n_expert
5268            || sel_idx.len() < t * n_used
5269            || sel_w.len() < t * n_used
5270        {
5271            return Err("sigmoid router _into geometry mismatch".into());
5272        }
5273        let f = self.func(crate::sigmoid_topk_kernel(
5274            crate::sig_expf_dev_on(),
5275            crate::topk_fast_on(),
5276            n_used,
5277        ));
5278        let threads = n_expert.div_ceil(32) * 32;
5279        let cfg = LaunchConfig {
5280            grid_dim: (t as u32, 1, 1),
5281            block_dim: (threads as u32, 1, 1),
5282            shared_mem_bytes: 0,
5283        };
5284        let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
5285        let __s_b = self.gpu.stream();
5286        let mut b = __s_b.launch_builder(&f);
5287        b.arg(logits)
5288            .arg(correction_bias)
5289            .arg(active)
5290            .arg(&mut *sel_idx)
5291            .arg(&mut *sel_w)
5292            .arg(&ne)
5293            .arg(&nu)
5294            .arg(&scaling_factor)
5295            .arg(&rn);
5296        unsafe {
5297            b.launch(cfg)?;
5298        }
5299        Ok(())
5300    }
5301
5302    /// Single-sync pinned readback twin of `moe_router_sigmoid_topk`. This preserves the existing
5303    /// grouped/staged dispatch contract while replacing the full-logit DtoH plus host sigmoid/sort.
5304    #[allow(clippy::too_many_arguments)]
5305    pub fn moe_router_sigmoid_topk_host(
5306        &self,
5307        logits: &CudaSlice<f32>,
5308        t: usize,
5309        n_expert: usize,
5310        n_used: usize,
5311        active_count: usize,
5312        correction_bias: &CudaSlice<f32>,
5313        active: &CudaSlice<u8>,
5314        scaling_factor: f32,
5315        route_norm: bool,
5316    ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
5317        let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
5318            logits,
5319            t,
5320            n_expert,
5321            n_used,
5322            active_count,
5323            correction_bias,
5324            active,
5325            scaling_factor,
5326            route_norm,
5327        )?;
5328        let n = t * n_used;
5329        let bytes = n * 8;
5330        let mut guard = self.router_stage.lock().unwrap();
5331        if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
5332            *guard = Some(PinnedStage::new(bytes.max(4096))?);
5333        }
5334        let stage = guard.as_mut().unwrap();
5335        let (si, sw) = unsafe {
5336            (
5337                std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
5338                std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
5339            )
5340        };
5341        self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
5342        self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
5343        self.gpu.stream().synchronize()?;
5344        Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
5345    }
5346
5347    /// EDGE-1 §C.2: async H2D of `host_bytes` into `scratch[off..]` on the COPY stream, returning a
5348    /// recorded event the compute stream can `wait` on before the dependent GEMM. Used for in-token
5349    /// expert prefetch (pipeline by one). `host_bytes` should be pinned for a true DMA (§C.1).
5350    pub fn stage_expert_async(
5351        &self,
5352        host_bytes: &[u8],
5353        scratch: &mut CudaSlice<u8>,
5354        off: usize,
5355    ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
5356        let mut dst = scratch.slice_mut(off..off + host_bytes.len());
5357        self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
5358        Ok(self.copy_stream.record_event(None)?)
5359    }
5360
5361    /// Make the compute stream wait for an async copy event (the consumer side of `stage_expert_async`).
5362    pub fn compute_wait(
5363        &self,
5364        ev: &cudarc::driver::CudaEvent,
5365    ) -> Result<(), Box<dyn std::error::Error>> {
5366        self.gpu.stream().wait(ev)?;
5367        Ok(())
5368    }
5369
5370    /// qmatvec over a byte sub-range of a (resident/scratch) CudaSlice<u8> holding ONE expert
5371    /// matrix. x is a CudaView<f32> (a sliced row of z, or a sliced activation). Reuses the
5372    /// validated qmatvec_f32 dequant path (NOT a fast path — the correctness gate). The
5373    /// CudaView base+offset pointer is honored by the launch arg.
5374    pub fn qmatvec_view(
5375        &self,
5376        w: &CudaSlice<u8>,
5377        range: std::ops::Range<usize>,
5378        x: &cudarc::driver::CudaView<f32>,
5379        m: usize,
5380        in_f: usize,
5381        out_f: usize,
5382        qtype: i32,
5383        row_bytes: usize,
5384    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5385        let f = self.func("qmatvec_f32");
5386        let wv = w.slice(range); // CudaView<u8>, offset honored
5387        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
5388        let cfg = LaunchConfig {
5389            grid_dim: (out_f as u32, m as u32, 1),
5390            block_dim: (256, 1, 1),
5391            shared_mem_bytes: 0,
5392        };
5393        let (inf, outf, mi, qt, rb) =
5394            (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
5395        let __s_b = self.gpu.stream();
5396        let mut b = __s_b.launch_builder(&f);
5397        b.arg(&wv)
5398            .arg(x)
5399            .arg(&mut y)
5400            .arg(&inf)
5401            .arg(&outf)
5402            .arg(&mi)
5403            .arg(&qt)
5404            .arg(&rb);
5405        unsafe {
5406            b.launch(cfg)?;
5407        }
5408        Ok(y)
5409    }
5410
5411    /// STAGE-2 GROUPED DECODE (2026-07-04): one MoE layer's gate+up+SiLU for all `n_used` routed
5412    /// experts of ONE token in ONE launch (replaces 8x qmatvec(gate) + 8x qmatvec(up) + 8x
5413    /// silu_mul = 24 launches). `gp`/`up` are the 8 expert weight-block device pointers (SLRU
5414    /// cache slots — fixed-address, stable for the launch). Returns act [n_used, n_ff].
5415    /// BIT-IDENTICAL to the sequential chain: each dot reproduces qmatvec_f32's exact 256-thread
5416    /// reduction; the SiLU epilogue is silu_mul_f32's exact expression (see kernel header).
5417    #[allow(clippy::too_many_arguments)]
5418    /// dp4a q8 twins (MoE expert dp4a arc, 2026-07-06): same contract as the _f32 versions but
5419    /// consume a PRE-QUANTIZED q8_1 activation. FP-order differs from _f32 (int dot + warp tree)
5420    /// — the argmax/stream-identity battery arbitrates; MEMRA_MOE_Q8=0 restores f32.
5421    pub fn moe_gate_up_silu8_q8(
5422        &self,
5423        gp: WPtr8,
5424        up: WPtr8,
5425        aq: &CudaSlice<i8>,
5426        ad: &CudaSlice<f32>,
5427        in_f: usize,
5428        n_ff: usize,
5429        n_used: usize,
5430        qt_g: i32,
5431        qt_u: i32,
5432        rb_g: usize,
5433        rb_u: usize,
5434    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5435        let f = self.func("moe_gate_up_silu8_q8");
5436        let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5437        let cfg = LaunchConfig {
5438            grid_dim: (n_ff as u32, n_used as u32, 1),
5439            block_dim: (32, 1, 1),
5440            shared_mem_bytes: 0,
5441        };
5442        let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
5443        let __s_b = self.gpu.stream();
5444        let mut b = __s_b.launch_builder(&f);
5445        b.arg(&gp)
5446            .arg(&up)
5447            .arg(aq)
5448            .arg(ad)
5449            .arg(&mut act)
5450            .arg(&inf)
5451            .arg(&nff)
5452            .arg(&qt_g)
5453            .arg(&qt_u)
5454            .arg(&rbg)
5455            .arg(&rbu);
5456        unsafe {
5457            b.launch(cfg)?;
5458        }
5459        Ok(act)
5460    }
5461
5462    #[allow(clippy::too_many_arguments)]
5463    pub fn moe_down8_fma_q8(
5464        &self,
5465        dp: WPtr8,
5466        w: F32x8,
5467        aq2: &CudaSlice<i8>,
5468        ad2: &CudaSlice<f32>,
5469        dst: &mut cudarc::driver::CudaViewMut<f32>,
5470        in_f: usize,
5471        out_f: usize,
5472        n_used: usize,
5473        qt: i32,
5474        rb: usize,
5475    ) -> Result<(), Box<dyn std::error::Error>> {
5476        let f = self.func("moe_down8_fma_q8");
5477        let cfg = LaunchConfig {
5478            grid_dim: (out_f as u32, 1, 1),
5479            block_dim: (32, 1, 1),
5480            shared_mem_bytes: 0,
5481        };
5482        let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
5483        let __s_b = self.gpu.stream();
5484        let mut b = __s_b.launch_builder(&f);
5485        b.arg(&dp)
5486            .arg(&w)
5487            .arg(aq2)
5488            .arg(ad2)
5489            .arg(dst)
5490            .arg(&inf)
5491            .arg(&outf)
5492            .arg(&nu)
5493            .arg(&qt)
5494            .arg(&rbi);
5495        unsafe {
5496            b.launch(cfg)?;
5497        }
5498        Ok(())
5499    }
5500
5501    /// q8 sequential expert matvec (staged path twin of qmatvec_view for IQ3_S/IQ4_XS).
5502    pub fn qmatvec_expert_q8(
5503        &self,
5504        w: &CudaSlice<u8>,
5505        range: std::ops::Range<usize>,
5506        aq: &CudaSlice<i8>,
5507        ad: &CudaSlice<f32>,
5508        m: usize,
5509        in_f: usize,
5510        out_f: usize,
5511        qtype: i32,
5512        row_bytes: usize,
5513    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5514        let f = self.func("qmatvec_expert_q8");
5515        let wv = w.slice(range);
5516        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
5517        const ROWS: u32 = 4; // MEMRA_MMVQ_ROWS
5518        let cfg = LaunchConfig {
5519            grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
5520            block_dim: (32, ROWS, 1),
5521            shared_mem_bytes: 0,
5522        };
5523        let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
5524        let __s_b = self.gpu.stream();
5525        let mut b = __s_b.launch_builder(&f);
5526        b.arg(&wv)
5527            .arg(aq)
5528            .arg(ad)
5529            .arg(&mut y)
5530            .arg(&inf)
5531            .arg(&outf)
5532            .arg(&mi)
5533            .arg(&qtype)
5534            .arg(&rbi);
5535        unsafe {
5536            b.launch(cfg)?;
5537        }
5538        Ok(y)
5539    }
5540
5541    pub fn moe_gate_up_silu8(
5542        &self,
5543        gp: WPtr8,
5544        up: WPtr8,
5545        x: &cudarc::driver::CudaView<f32>,
5546        in_f: usize,
5547        n_ff: usize,
5548        n_used: usize,
5549        qt_g: i32,
5550        qt_u: i32,
5551        rb_g: usize,
5552        rb_u: usize,
5553    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5554        let f = self.func("moe_gate_up_silu8_f32");
5555        let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; // fully overwritten
5556        let cfg = LaunchConfig {
5557            grid_dim: (n_ff as u32, n_used as u32, 1),
5558            block_dim: (256, 1, 1),
5559            shared_mem_bytes: 0,
5560        };
5561        let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
5562        let __s_b = self.gpu.stream();
5563        let mut b = __s_b.launch_builder(&f);
5564        b.arg(&gp)
5565            .arg(&up)
5566            .arg(x)
5567            .arg(&mut act)
5568            .arg(&inf)
5569            .arg(&nff)
5570            .arg(&qt_g)
5571            .arg(&qt_u)
5572            .arg(&rbg)
5573            .arg(&rbu);
5574        unsafe {
5575            b.launch(cfg)?;
5576        }
5577        Ok(act)
5578    }
5579
5580    /// STAGE-2 GROUPED DECODE: one MoE layer's down-proj + weighted accumulation for all `n_used`
5581    /// routed experts in ONE launch (replaces 8x qmatvec(down) + 8x axpy = 16 launches), writing
5582    /// the token's moe_out row DIRECTLY (`dst` is the zeroed row; the in-kernel slot-ordered
5583    /// __fmaf_rn chain starting at 0.0f reproduces the sequential axpy_f32 accumulation into the
5584    /// zeroed row bit-for-bit — the A2 byte-identity scheme at m=1).
5585    #[allow(clippy::too_many_arguments)]
5586    pub fn moe_down8_fma_into(
5587        &self,
5588        dp: WPtr8,
5589        w: F32x8,
5590        act: &CudaSlice<f32>,
5591        dst: &mut cudarc::driver::CudaViewMut<f32>,
5592        in_f: usize,
5593        out_f: usize,
5594        n_used: usize,
5595        qt: i32,
5596        rb: usize,
5597    ) -> Result<(), Box<dyn std::error::Error>> {
5598        let f = self.func("moe_down8_fma_f32");
5599        let cfg = LaunchConfig {
5600            grid_dim: (out_f as u32, 1, 1),
5601            block_dim: (256, 1, 1),
5602            shared_mem_bytes: 0,
5603        };
5604        let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
5605        let __s_b = self.gpu.stream();
5606        let mut b = __s_b.launch_builder(&f);
5607        b.arg(&dp)
5608            .arg(&w)
5609            .arg(act)
5610            .arg(dst)
5611            .arg(&inf)
5612            .arg(&outf)
5613            .arg(&nu)
5614            .arg(&qt)
5615            .arg(&rbv);
5616        unsafe {
5617            b.launch(cfg)?;
5618        }
5619        Ok(())
5620    }
5621
5622    /// LAUNCH-STRUCTURE STAGE 3: device-dispatch twin of `moe_gate_up_silu8` for FULLY-RESIDENT
5623    /// layers. The expert ids come from the router kernel's DEVICE `sel` output (no DtoH) and the
5624    /// weight pointers from the per-layer device table `[3, n_expert]` of slot base addresses.
5625    /// BIT-IDENTICAL math (same grid/block/reduction; only the pointer/id source differs).
5626    #[allow(clippy::too_many_arguments)]
5627    /// dp4a q8 twin of the _dev pair (resident-experts arc).
5628    ///
5629    /// GEOMETRY VARIANTS (multirow/occupancy arc 2026-07-05): all outputs are BIT-IDENTICAL to
5630    /// the base one-warp-per-(row,slot) kernel (same expert_dot_g g-order + warp tree per row;
5631    /// down's FMA chain stays slot-ordered serial). Seams:
5632    ///   MEMRA_MOE_DEVQ8_GU   = 0(base) | 1 | 2 | 4 -> _r{1,2,4} multirow twin (RPW rows/warp)
5633    ///                       | s2 (gate/up warp split) | s2z (s2 + WPB rows packed per block)
5634    ///                       | gs4 (gate/up x low/high-group 4-warp split, nsb==64 only)
5635    ///                       | u64 (nsb==64 unrolled ILP twin, geometry unchanged)
5636    ///   MEMRA_MOE_DEVQ8_WPB  = warps per block for _r twins / z-rows for s2z (default 4)
5637    ///   MEMRA_MOE_DEVQ8_DOWN = auto(default: w8h2 when in_f==512 & n_used<=8 — measured +3.8%
5638    ///                       decode on 35B/rtx6000) | 0 (base one-warp serial-slot) | 1 | 2 | 4 ->
5639    ///                       _w8r{1,2,4} slot-parallel twin | h2 (half-warp dual-row, nsb==16
5640    ///                       only) | w8h2 (h2 x slot-parallel)
5641    #[allow(clippy::too_many_arguments)]
5642    /// MoE PREFILL pair-batch matvec: one launch covers all (token,expert) pairs for one proj.
5643    #[allow(clippy::too_many_arguments)]
5644    pub fn moe_pairs_matvec_q8(
5645        &self,
5646        table: &CudaSlice<u64>,
5647        proj: i32,
5648        pair_tok: &CudaSlice<i32>,
5649        pair_ex: &CudaSlice<i32>,
5650        aq: &CudaSlice<i8>,
5651        ad: &CudaSlice<f32>,
5652        in_f: usize,
5653        out_f: usize,
5654        n_expert: usize,
5655        n_pairs: usize,
5656        qtype: i32,
5657        row_bytes: usize,
5658    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5659        let f = self.func("moe_pairs_matvec_q8");
5660        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
5661        const ROWS: u32 = 4;
5662        let cfg = LaunchConfig {
5663            grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
5664            block_dim: (32, ROWS, 1),
5665            shared_mem_bytes: 0,
5666        };
5667        let (inf, outf, ne, np, rbi) = (
5668            in_f as i32,
5669            out_f as i32,
5670            n_expert as i32,
5671            n_pairs as i32,
5672            row_bytes as i64,
5673        );
5674        let __s_b = self.gpu.stream();
5675        let mut b = __s_b.launch_builder(&f);
5676        b.arg(table)
5677            .arg(&proj)
5678            .arg(pair_tok)
5679            .arg(pair_ex)
5680            .arg(aq)
5681            .arg(ad)
5682            .arg(&mut y)
5683            .arg(&inf)
5684            .arg(&outf)
5685            .arg(&ne)
5686            .arg(&np)
5687            .arg(&qtype)
5688            .arg(&rbi);
5689        unsafe {
5690            b.launch(cfg)?;
5691        }
5692        Ok(y)
5693    }
5694
5695    /// Expert-major pair matvec (weight-reuse across each expert's token group).
5696    #[allow(clippy::too_many_arguments)]
5697    pub fn moe_pairs_matvec_q8_em(
5698        &self,
5699        table: &CudaSlice<u64>,
5700        proj: i32,
5701        ex_ids: &CudaSlice<i32>,
5702        ex_off: &CudaSlice<i32>,
5703        ex_pairs: &CudaSlice<i32>,
5704        pair_tok: &CudaSlice<i32>,
5705        aq: &CudaSlice<i8>,
5706        ad: &CudaSlice<f32>,
5707        in_f: usize,
5708        out_f: usize,
5709        n_expert: usize,
5710        n_active: usize,
5711        n_pairs: usize,
5712        qtype: i32,
5713        row_bytes: usize,
5714    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5715        let f = self.func("moe_pairs_matvec_q8_em");
5716        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
5717        const ROWS: u32 = 4;
5718        let cfg = LaunchConfig {
5719            grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
5720            block_dim: (32, ROWS, 1),
5721            shared_mem_bytes: 0,
5722        };
5723        let (inf, outf, ne, na, rbi) = (
5724            in_f as i32,
5725            out_f as i32,
5726            n_expert as i32,
5727            n_active as i32,
5728            row_bytes as i64,
5729        );
5730        let __s_b = self.gpu.stream();
5731        let mut b = __s_b.launch_builder(&f);
5732        b.arg(table)
5733            .arg(&proj)
5734            .arg(ex_ids)
5735            .arg(ex_off)
5736            .arg(ex_pairs)
5737            .arg(pair_tok)
5738            .arg(aq)
5739            .arg(ad)
5740            .arg(&mut y)
5741            .arg(&inf)
5742            .arg(&outf)
5743            .arg(&ne)
5744            .arg(&na)
5745            .arg(&qtype)
5746            .arg(&rbi);
5747        unsafe {
5748            b.launch(cfg)?;
5749        }
5750        Ok(y)
5751    }
5752
5753    // Decode-once expert-major MMQ (rung 3). Same CSR inputs/geometry as _em; kernel dequants each
5754    // weight group once per (row,group) then dp4a's across the expert's token group.
5755    #[allow(clippy::too_many_arguments)]
5756    pub fn moe_pairs_matvec_q8_dec(
5757        &self,
5758        table: &CudaSlice<u64>,
5759        proj: i32,
5760        ex_ids: &CudaSlice<i32>,
5761        ex_off: &CudaSlice<i32>,
5762        ex_pairs: &CudaSlice<i32>,
5763        pair_tok: &CudaSlice<i32>,
5764        aq: &CudaSlice<i8>,
5765        ad: &CudaSlice<f32>,
5766        in_f: usize,
5767        out_f: usize,
5768        n_expert: usize,
5769        n_active: usize,
5770        n_pairs: usize,
5771        qtype: i32,
5772        row_bytes: usize,
5773    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5774        let f = self.func("moe_pairs_matvec_q8_dec");
5775        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
5776        const ROWS: u32 = 4;
5777        let cfg = LaunchConfig {
5778            grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
5779            block_dim: (32, ROWS, 1),
5780            shared_mem_bytes: 0,
5781        };
5782        let (inf, outf, ne, na, rbi) = (
5783            in_f as i32,
5784            out_f as i32,
5785            n_expert as i32,
5786            n_active as i32,
5787            row_bytes as i64,
5788        );
5789        let __s_b = self.gpu.stream();
5790        let mut b = __s_b.launch_builder(&f);
5791        b.arg(table)
5792            .arg(&proj)
5793            .arg(ex_ids)
5794            .arg(ex_off)
5795            .arg(ex_pairs)
5796            .arg(pair_tok)
5797            .arg(aq)
5798            .arg(ad)
5799            .arg(&mut y)
5800            .arg(&inf)
5801            .arg(&outf)
5802            .arg(&ne)
5803            .arg(&na)
5804            .arg(&qtype)
5805            .arg(&rbi);
5806        unsafe {
5807            b.launch(cfg)?;
5808        }
5809        Ok(y)
5810    }
5811
5812    pub fn moe_pairs_gelu_mul(
5813        &self,
5814        gate: &CudaSlice<f32>,
5815        up: &CudaSlice<f32>,
5816        n: usize,
5817    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5818        let f = self.func("moe_pairs_gelu_mul");
5819        let mut act = self.alloc_uninit::<f32>(n)?;
5820        let cfg = LaunchConfig::for_num_elems(n as u32);
5821        let nl = n as i64;
5822        let __s_b = self.gpu.stream();
5823        let mut b = __s_b.launch_builder(&f);
5824        b.arg(gate).arg(up).arg(&mut act).arg(&nl);
5825        unsafe {
5826            b.launch(cfg)?;
5827        }
5828        Ok(act)
5829    }
5830
5831    pub fn moe_pairs_silu_mul(
5832        &self,
5833        gate: &CudaSlice<f32>,
5834        up: &CudaSlice<f32>,
5835        n: usize,
5836    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5837        let f = self.func("moe_pairs_silu_mul");
5838        let mut act = self.alloc_uninit::<f32>(n)?;
5839        let cfg = LaunchConfig::for_num_elems(n as u32);
5840        let nl = n as i64;
5841        let __s_b = self.gpu.stream();
5842        let mut b = __s_b.launch_builder(&f);
5843        b.arg(gate).arg(up).arg(&mut act).arg(&nl);
5844        unsafe {
5845            b.launch(cfg)?;
5846        }
5847        Ok(act)
5848    }
5849
5850    #[allow(clippy::too_many_arguments)]
5851    pub fn moe_pairs_scatter(
5852        &self,
5853        y_down: &CudaSlice<f32>,
5854        pair_w: &CudaSlice<f32>,
5855        tok_pair_off: &CudaSlice<i32>,
5856        tok_pair_ids: &CudaSlice<i32>,
5857        moe_out: &mut CudaSlice<f32>,
5858        t: usize,
5859        n_embd: usize,
5860    ) -> Result<(), Box<dyn std::error::Error>> {
5861        let f = self.func("moe_pairs_scatter");
5862        let cfg = LaunchConfig {
5863            grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
5864            block_dim: (256, 1, 1),
5865            shared_mem_bytes: 0,
5866        };
5867        let ne = n_embd as i32;
5868        let __s_b = self.gpu.stream();
5869        let mut b = __s_b.launch_builder(&f);
5870        b.arg(y_down)
5871            .arg(pair_w)
5872            .arg(tok_pair_off)
5873            .arg(tok_pair_ids)
5874            .arg(moe_out)
5875            .arg(&ne);
5876        unsafe {
5877            b.launch(cfg)?;
5878        }
5879        Ok(())
5880    }
5881
5882    /// gemma4 GELU twin of moe_gate_up_silu8_dev_q8 (base geometry — slot-packed j8/j8r2
5883    /// twins probed 2026-08-01 g26 decode dig: bit-identical rows, -2.5%/-2.9% whole-model
5884    /// decode x3 interleaved -> refuted and killed; research/g26-decode-20260801/receipts.md).
5885    #[allow(clippy::too_many_arguments)]
5886    pub fn moe_gate_up_gelu8_dev_q8(
5887        &self,
5888        table: &CudaSlice<u64>,
5889        sel: &cudarc::driver::CudaView<i32>,
5890        aq: &CudaSlice<i8>,
5891        ad: &CudaSlice<f32>,
5892        in_f: usize,
5893        n_ff: usize,
5894        n_used: usize,
5895        n_expert: usize,
5896        qt_g: i32,
5897        qt_u: i32,
5898        rb_g: usize,
5899        rb_u: usize,
5900    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5901        let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5902        let (inf, nff, ne, rbg, rbu) = (
5903            in_f as i32,
5904            n_ff as i32,
5905            n_expert as i32,
5906            rb_g as i64,
5907            rb_u as i64,
5908        );
5909        let f = self.func("moe_gate_up_gelu8_dev_q8");
5910        let cfg = LaunchConfig {
5911            grid_dim: (n_ff as u32, n_used as u32, 1),
5912            block_dim: (32, 1, 1),
5913            shared_mem_bytes: 0,
5914        };
5915        let __s_b = self.gpu.stream();
5916        let mut b = __s_b.launch_builder(&f);
5917        b.arg(table)
5918            .arg(sel)
5919            .arg(aq)
5920            .arg(ad)
5921            .arg(&mut act)
5922            .arg(&inf)
5923            .arg(&nff)
5924            .arg(&ne)
5925            .arg(&qt_g)
5926            .arg(&qt_u)
5927            .arg(&rbg)
5928            .arg(&rbu);
5929        unsafe {
5930            b.launch(cfg)?;
5931        }
5932        Ok(act)
5933    }
5934
5935    /// gemma4 GELU rows twin (verify): one launch over (n_ff, n_used, t).
5936    #[allow(clippy::too_many_arguments)]
5937    pub fn moe_gate_up_gelu8_dev_q8_rows(
5938        &self,
5939        table: &CudaSlice<u64>,
5940        sel: &CudaSlice<i32>,
5941        aq: &CudaSlice<i8>,
5942        ad: &CudaSlice<f32>,
5943        t: usize,
5944        in_f: usize,
5945        n_ff: usize,
5946        n_used: usize,
5947        n_expert: usize,
5948        qt_g: i32,
5949        qt_u: i32,
5950        rb_g: usize,
5951        rb_u: usize,
5952    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5953        let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5954        let (inf, nff, ne, rbg, rbu, nu) = (
5955            in_f as i32,
5956            n_ff as i32,
5957            n_expert as i32,
5958            rb_g as i64,
5959            rb_u as i64,
5960            n_used as i32,
5961        );
5962        let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
5963        let cfg = LaunchConfig {
5964            grid_dim: (n_ff as u32, n_used as u32, t as u32),
5965            block_dim: (32, 1, 1),
5966            shared_mem_bytes: 0,
5967        };
5968        let __s_b = self.gpu.stream();
5969        let mut b = __s_b.launch_builder(&f);
5970        b.arg(table)
5971            .arg(sel)
5972            .arg(aq)
5973            .arg(ad)
5974            .arg(&mut act)
5975            .arg(&inf)
5976            .arg(&nff)
5977            .arg(&ne)
5978            .arg(&qt_g)
5979            .arg(&qt_u)
5980            .arg(&rbg)
5981            .arg(&rbu)
5982            .arg(&nu);
5983        unsafe {
5984            b.launch(cfg)?;
5985        }
5986        Ok(act)
5987    }
5988
5989    /// gemma4 GELU CSR twin (verify dedup: owner block serves every pair of its expert).
5990    #[allow(clippy::too_many_arguments)]
5991    pub fn moe_gate_up_gelu8_dev_q8_csr(
5992        &self,
5993        table: &CudaSlice<u64>,
5994        sel: &CudaSlice<i32>,
5995        aq: &CudaSlice<i8>,
5996        ad: &CudaSlice<f32>,
5997        n_pairs: usize,
5998        in_f: usize,
5999        n_ff: usize,
6000        n_used: usize,
6001        n_expert: usize,
6002        qt_g: i32,
6003        qt_u: i32,
6004        rb_g: usize,
6005        rb_u: usize,
6006    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6007        let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
6008        let (inf, nff, ne, rbg, rbu, nu, npi) = (
6009            in_f as i32,
6010            n_ff as i32,
6011            n_expert as i32,
6012            rb_g as i64,
6013            rb_u as i64,
6014            n_used as i32,
6015            n_pairs as i32,
6016        );
6017        let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
6018        let cfg = LaunchConfig {
6019            grid_dim: (n_ff as u32, n_pairs as u32, 1),
6020            block_dim: (32, 1, 1),
6021            shared_mem_bytes: 0,
6022        };
6023        let __s_b = self.gpu.stream();
6024        let mut b = __s_b.launch_builder(&f);
6025        b.arg(table)
6026            .arg(sel)
6027            .arg(aq)
6028            .arg(ad)
6029            .arg(&mut act)
6030            .arg(&inf)
6031            .arg(&nff)
6032            .arg(&ne)
6033            .arg(&qt_g)
6034            .arg(&qt_u)
6035            .arg(&rbg)
6036            .arg(&rbu)
6037            .arg(&nu)
6038            .arg(&npi);
6039        unsafe {
6040            b.launch(cfg)?;
6041        }
6042        Ok(act)
6043    }
6044
6045    /// gemma4 generic down rows twin (verify): one launch over (out_f, 1, t).
6046    #[allow(clippy::too_many_arguments)]
6047    pub fn moe_down8_fma_dev_q8_rows_g(
6048        &self,
6049        table: &CudaSlice<u64>,
6050        sel: &CudaSlice<i32>,
6051        w: &CudaSlice<f32>,
6052        aq2: &CudaSlice<i8>,
6053        ad2: &CudaSlice<f32>,
6054        dst: &mut CudaSlice<f32>,
6055        t: usize,
6056        in_f: usize,
6057        out_f: usize,
6058        n_used: usize,
6059        n_expert: usize,
6060        qt: i32,
6061        rb: usize,
6062    ) -> Result<(), Box<dyn std::error::Error>> {
6063        let (inf, outf, nu, ne, rbi) = (
6064            in_f as i32,
6065            out_f as i32,
6066            n_used as i32,
6067            n_expert as i32,
6068            rb as i64,
6069        );
6070        // Exact Step-3.7 B=1 shape: expose the eight independent slot dots as
6071        // eight warps, then replay the original slot-ordered FMA chain. Every
6072        // other shape retains the generic one-warp rows kernel.
6073        let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
6074        let f = self.func(if step_b1_w8 {
6075            "moe_down8_fma_dev_q8_rows_w8"
6076        } else {
6077            "moe_down8_fma_dev_q8_rows_g"
6078        });
6079        let cfg = LaunchConfig {
6080            grid_dim: (out_f as u32, 1, t as u32),
6081            block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
6082            shared_mem_bytes: 0,
6083        };
6084        let __s_b = self.gpu.stream();
6085        let mut b = __s_b.launch_builder(&f);
6086        b.arg(table)
6087            .arg(sel)
6088            .arg(w)
6089            .arg(aq2)
6090            .arg(ad2)
6091            .arg(dst)
6092            .arg(&inf)
6093            .arg(&outf)
6094            .arg(&nu)
6095            .arg(&ne)
6096            .arg(&qt)
6097            .arg(&rbi);
6098        unsafe {
6099            b.launch(cfg)?;
6100        }
6101        Ok(())
6102    }
6103
6104    /// rp_q4 microprobe (2026-07-10 verify-trunk lever): b4 GGUF-block layout vs the Q4_0
6105    /// split-plane twin on the wq-class shape. Returns (blk_us, rp_us) after asserting bitwise
6106    /// identity. Bench-only surface (rp_q4_probe bin); no production dispatch reads this.
6107    pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
6108        let (out_f, in_f) = (2048usize, 2816usize);
6109        let nblk = in_f / 32;
6110        let mut seed = 0x9E3779B97F4A7C15u64;
6111        let mut rng = move || {
6112            seed = seed
6113                .wrapping_mul(6364136223846793005)
6114                .wrapping_add(1442695040888963407);
6115            (seed >> 33) as u8
6116        };
6117        let mut w = vec![0u8; out_f * nblk * 18];
6118        for b in w.iter_mut() {
6119            *b = rng();
6120        }
6121        for r in 0..out_f {
6122            for g in 0..nblk {
6123                let off = (r * nblk + g) * 18;
6124                w[off] = 0x00;
6125                w[off + 1] = 0x2C; // sane half d
6126            }
6127        }
6128        let qplane = out_f * nblk * 16;
6129        let mut wrp = vec![0u8; w.len()];
6130        for r in 0..out_f {
6131            for g in 0..nblk {
6132                let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
6133                wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
6134                    .copy_from_slice(&src[0..2]);
6135                wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
6136            }
6137        }
6138        let w_d = self.htod_bytes(&w)?;
6139        let wrp_d = self.htod_bytes(&wrp)?;
6140        let mut aq = vec![0i8; m * in_f];
6141        for v in aq.iter_mut() {
6142            *v = rng() as i8;
6143        }
6144        let aq_d = self.htod_i8(&aq)?;
6145        let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
6146        let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
6147        let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
6148        const RPB: u32 = 4;
6149        let cfg = LaunchConfig {
6150            grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
6151            block_dim: (32, RPB, 1),
6152            shared_mem_bytes: 0,
6153        };
6154        let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
6155        let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
6156        let fb = self.func("qmatvec_q4_0_mmvq_b4");
6157        let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
6158        {
6159            let __s_b = self.gpu.stream();
6160            let mut b = __s_b.launch_builder(&fb);
6161            b.arg(&w_d)
6162                .arg(&aq_d)
6163                .arg(&ad_d)
6164                .arg(&mut y0)
6165                .arg(&inf)
6166                .arg(&outf)
6167                .arg(&mi)
6168                .arg(&rb);
6169            unsafe {
6170                b.launch(cfg)?;
6171            }
6172            let __s_b = self.gpu.stream();
6173            let mut b = __s_b.launch_builder(&fr);
6174            b.arg(&wrp_d)
6175                .arg(&aq_d)
6176                .arg(&ad_d)
6177                .arg(&mut y1)
6178                .arg(&inf)
6179                .arg(&outf)
6180                .arg(&mi)
6181                .arg(&qp);
6182            unsafe {
6183                b.launch(cfg)?;
6184            }
6185        }
6186        self.gpu.stream().synchronize()?;
6187        let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
6188        let nd = h0
6189            .iter()
6190            .zip(&h1)
6191            .filter(|(a, b)| a.to_bits() != b.to_bits())
6192            .count();
6193        if nd != 0 {
6194            return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
6195        }
6196        let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
6197            self.gpu.stream().synchronize()?;
6198            let t0 = std::time::Instant::now();
6199            for _ in 0..500 {
6200                if rp {
6201                    let __s_b = self.gpu.stream();
6202                    let mut b = __s_b.launch_builder(&fr);
6203                    b.arg(&wrp_d)
6204                        .arg(&aq_d)
6205                        .arg(&ad_d)
6206                        .arg(&mut y1)
6207                        .arg(&inf)
6208                        .arg(&outf)
6209                        .arg(&mi)
6210                        .arg(&qp);
6211                    unsafe {
6212                        b.launch(cfg)?;
6213                    }
6214                } else {
6215                    let __s_b = self.gpu.stream();
6216                    let mut b = __s_b.launch_builder(&fb);
6217                    b.arg(&w_d)
6218                        .arg(&aq_d)
6219                        .arg(&ad_d)
6220                        .arg(&mut y0)
6221                        .arg(&inf)
6222                        .arg(&outf)
6223                        .arg(&mi)
6224                        .arg(&rb);
6225                    unsafe {
6226                        b.launch(cfg)?;
6227                    }
6228                }
6229            }
6230            self.gpu.stream().synchronize()?;
6231            Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
6232        };
6233        let _ = time(false)?;
6234        let _ = time(true)?; // warm
6235        Ok((time(false)?, time(true)?))
6236    }
6237
6238    /// Build the Q4_0 split-plane decode mirror for a 2D Quant tensor (device-side permutation,
6239    /// q4_0_split_rp_build). Raw bytes stay resident (prefill/gemm/Stage-A); the m<=8 decode
6240    /// dispatch prefers the mirror (_rp twins). No-op unless (Q4_0, 2D, mirror absent).
6241    /// VRAM cost == the tensor's weight size. MEMRA_Q4RP=0 disables at the call sites.
6242    pub fn build_q4_rp4(
6243        &self,
6244        t: &mut crate::model::GpuTensor,
6245    ) -> Result<(), Box<dyn std::error::Error>> {
6246        use crate::model::GpuTensor;
6247        let GpuTensor::Quant {
6248            bytes,
6249            qtype,
6250            row_bytes,
6251            ne,
6252            rp4,
6253            ..
6254        } = t
6255        else {
6256            return Ok(());
6257        };
6258        if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
6259            return Ok(());
6260        }
6261        let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
6262        if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
6263            return Ok(());
6264        }
6265        let nblk = in_f / 32;
6266        let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
6267        let f = self.func("q4_0_split_rp_build");
6268        let n = (out_f * nblk) as i32;
6269        let cfg = LaunchConfig {
6270            grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
6271            block_dim: (256, 1, 1),
6272            shared_mem_bytes: 0,
6273        };
6274        let (of, nb) = (out_f as i32, nblk as i32);
6275        let _ = n;
6276        let __s_b = self.gpu.stream();
6277        let mut b = __s_b.launch_builder(&f);
6278        b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
6279        unsafe {
6280            b.launch(cfg)?;
6281        }
6282        *rp4 = Some(dst);
6283        Ok(())
6284    }
6285
6286    /// Q8_0 twin of `build_q4_rp4` (H100 coalescing fix, 2026-07-26 ncu: GGUF 34B-stride
6287    /// weight loads hold Max Bandwidth at 41-46%; the split mirror makes them aligned 16B
6288    /// ldcs). Raw bytes stay resident (prefill GEMM/MMQ/fused m=1 launches read GGUF layout);
6289    /// the mmvq/batched decode arms prefer the mirror via `rp4`. Bit-identical outputs.
6290    pub fn build_q8_rp4(
6291        &self,
6292        t: &mut crate::model::GpuTensor,
6293    ) -> Result<(), Box<dyn std::error::Error>> {
6294        use crate::model::GpuTensor;
6295        let GpuTensor::Quant {
6296            bytes,
6297            qtype,
6298            row_bytes,
6299            ne,
6300            rp4,
6301            ..
6302        } = t
6303        else {
6304            return Ok(());
6305        };
6306        if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
6307            return Ok(());
6308        }
6309        let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
6310        if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
6311            return Ok(());
6312        }
6313        *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
6314        Ok(())
6315    }
6316
6317    /// Raw rp-mirror build for gates/benches: split GGUF Q8_0 bytes into the qplane+dplane
6318    /// mirror without a GpuTensor (same kernel the loader path above uses).
6319    pub fn build_q8_rp4_raw(
6320        &self,
6321        bytes: &CudaSlice<u8>,
6322        in_f: usize,
6323        out_f: usize,
6324    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
6325        assert!(in_f % 32 == 0);
6326        let nblk = in_f / 32;
6327        let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
6328        let f = self.func("q8_0_split_rp_build");
6329        let cfg = LaunchConfig {
6330            grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
6331            block_dim: (256, 1, 1),
6332            shared_mem_bytes: 0,
6333        };
6334        let (of, nb) = (out_f as i32, nblk as i32);
6335        let __s_b = self.gpu.stream();
6336        let mut b = __s_b.launch_builder(&f);
6337        b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
6338        unsafe {
6339            b.launch(cfg)?;
6340        }
6341        Ok(dst)
6342    }
6343
6344    /// K-quant twins of `build_q8_rp4` (H100 K-quant coalescing fix, 2026-08-01 ncu on the
6345    /// q27 Q4_K_M decode: q4_K mmvq DRAM 41-54% with 65% excessive sectors, q6_K 40% with
6346    /// 78% — the 144B/210B superblock strides land every 4B weight load off-sector). The
6347    /// mirror re-packs each tensor into planes (q4_K: qs ++ 16B meta; q6_K: ql ++ qh ++
6348    /// scales ++ d — same total bytes) so every quant fetch is an aligned 16B ldcs. Raw
6349    /// bytes stay resident (prefill GEMM/dequant/Stage-A read GGUF layout); the mmvq/batched
6350    /// decode arms prefer the mirror via `rp4`. Bit-identical outputs.
6351    pub fn build_q4k_rp4(
6352        &self,
6353        t: &mut crate::model::GpuTensor,
6354    ) -> Result<(), Box<dyn std::error::Error>> {
6355        use crate::model::GpuTensor;
6356        let GpuTensor::Quant {
6357            bytes,
6358            qtype,
6359            row_bytes,
6360            ne,
6361            rp4,
6362            ..
6363        } = t
6364        else {
6365            return Ok(());
6366        };
6367        if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
6368            return Ok(());
6369        }
6370        let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
6371        if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
6372            return Ok(());
6373        }
6374        *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
6375        Ok(())
6376    }
6377
6378    pub fn build_q6k_rp4(
6379        &self,
6380        t: &mut crate::model::GpuTensor,
6381    ) -> Result<(), Box<dyn std::error::Error>> {
6382        use crate::model::GpuTensor;
6383        let GpuTensor::Quant {
6384            bytes,
6385            qtype,
6386            row_bytes,
6387            ne,
6388            rp4,
6389            ..
6390        } = t
6391        else {
6392            return Ok(());
6393        };
6394        if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
6395            return Ok(());
6396        }
6397        let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
6398        if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
6399            return Ok(());
6400        }
6401        *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
6402        Ok(())
6403    }
6404
6405    /// Raw K-quant rp-mirror build for gates/benches (same kernels the loader path uses).
6406    pub fn build_kq_rp4_raw(
6407        &self,
6408        bytes: &CudaSlice<u8>,
6409        in_f: usize,
6410        out_f: usize,
6411        qtype: i32,
6412    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
6413        assert!(in_f % 256 == 0);
6414        let nsbk = in_f / 256;
6415        let (sb_bytes, kname) = match qtype {
6416            QT_Q4_K => (144usize, "q4_K_split_rp_build"),
6417            QT_Q6_K => (210usize, "q6_K_split_rp_build"),
6418            _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
6419        };
6420        let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
6421        let f = self.func(kname);
6422        let cfg = LaunchConfig {
6423            grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
6424            block_dim: (256, 1, 1),
6425            shared_mem_bytes: 0,
6426        };
6427        let (of, nb) = (out_f as i32, nsbk as i32);
6428        let __s_b = self.gpu.stream();
6429        let mut b = __s_b.launch_builder(&f);
6430        b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
6431        unsafe {
6432            b.launch(cfg)?;
6433        }
6434        Ok(dst)
6435    }
6436
6437    /// MEMRA_KQRP seam: the K-quant (q4_K/q6_K) split-plane decode mirrors at model load.
6438    /// Default follows the Q8RP convention — ON on the Hopper lane (80GB pays the mirror
6439    /// VRAM), OFF elsewhere (a 24GB card cannot hold model + mirror + KV for the big trunks).
6440    pub fn kqrp_enabled() -> bool {
6441        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6442        *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
6443            Ok("0") => false,
6444            Ok(_) => true,
6445            Err(_) => cfg!(memra_hopper_mma),
6446        })
6447    }
6448
6449    /// IN-PLACE split-plane swap (the 31B dense arc): build the split layout and REPLACE the
6450    /// GGUF bytes (zero extra steady-state VRAM — the transient peak is one tensor's size).
6451    /// The tensor's `rp` flag then routes every consumer (mmvq/batched `_rp` twins, the
6452    /// `qmatvec_gemm_q4_0_rp` prefill kernel). Callers gate on the fast path being active —
6453    /// the Stage-A f32 oracle (`MEMRA_FAST=0`) reads GGUF layout and must never see a swap.
6454    pub fn build_q4_rp_swap(
6455        &self,
6456        t: &mut crate::model::GpuTensor,
6457    ) -> Result<bool, Box<dyn std::error::Error>> {
6458        use crate::model::GpuTensor;
6459        // QTYPE GUARD IN THE SWAP ITSELF (gemma4 NVFP4mix prefill-NaN, 2026-08-17):
6460        // `rp4` is a SHARED field — the Q8RP walk parks Q8_0 MIRRORS there, and this
6461        // fn used to `take()` whatever it found. On a Q8_0-carrying gemma4-dense trunk
6462        // the swap hijacked those mirrors: `bytes` became split-plane in place, the
6463        // m<=16 `_rp` dispatch read them correctly (masking the corruption from every
6464        // decode pin), and every GGUF-layout prefill consumer (MMQ + GEMM) read the
6465        // fp16 d-plane as weights -> layer-0 NaN, <pad>-spam serving. Only a tensor
6466        // this fn's OWN builder serves may ever be swapped; everything else refuses
6467        // here, regardless of walk ordering.
6468        if !matches!(t, GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0) {
6469            return Ok(false);
6470        }
6471        self.build_q4_rp4(t)?;
6472        self.gpu.stream().synchronize()?; // build kernel reads the GGUF bytes — drain BEFORE dropping them
6473        let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
6474            return Ok(false);
6475        };
6476        match rp4.take() {
6477            Some(split) => {
6478                *bytes = split; // the GGUF-layout buffer drops here
6479                *rp = true;
6480                Ok(true)
6481            }
6482            None => Ok(false),
6483        }
6484    }
6485
6486    /// MEMRA_Q4RP seam (default ON): the Q4_0 split-plane decode mirror at model load.
6487    pub fn q4rp_enabled() -> bool {
6488        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6489        *ON.get_or_init(|| {
6490            std::env::var("MEMRA_Q4RP")
6491                .map(|v| v != "0")
6492                .unwrap_or(true)
6493        })
6494    }
6495
6496    /// gemma4-E4B: dense [t][row_elems] gather of layer il's rows from the strided prologue
6497    /// buffer ([t][n_layer][n_epl]; off = il*n_epl, stride = n_layer*n_epl).
6498    pub fn copy_rows_strided(
6499        &self,
6500        src: &CudaSlice<f32>,
6501        dst: &mut CudaSlice<f32>,
6502        row_elems: usize,
6503        n_rows: usize,
6504        src_stride: usize,
6505        src_off: usize,
6506    ) -> Result<(), Box<dyn std::error::Error>> {
6507        let f = self.func("copy_rows_strided_f32");
6508        let cfg = LaunchConfig {
6509            grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
6510            block_dim: (256, 1, 1),
6511            shared_mem_bytes: 0,
6512        };
6513        let (re, nr) = (row_elems as i32, n_rows as i32);
6514        let (st, off) = (src_stride as i64, src_off as i64);
6515        let __s_b = self.gpu.stream();
6516        let mut b = __s_b.launch_builder(&f);
6517        b.arg(src)
6518            .arg(&mut *dst)
6519            .arg(&re)
6520            .arg(&nr)
6521            .arg(&st)
6522            .arg(&off);
6523        unsafe {
6524            b.launch(cfg)?;
6525        }
6526        Ok(())
6527    }
6528
6529    /// Place dense `[row][row_elems]` source rows into one column range of a strided destination.
6530    ///
6531    /// This is a byte-preserving layout operation. It exists so multi-GPU collectives can move
6532    /// one dense shard per rank and reconstruct the canonical token-major matrix without issuing
6533    /// one peer copy per token.
6534    pub fn place_rows_strided(
6535        &self,
6536        src: &CudaSlice<f32>,
6537        dst: &mut CudaSlice<f32>,
6538        row_elems: usize,
6539        n_rows: usize,
6540        dst_stride: usize,
6541        dst_off: usize,
6542    ) -> Result<(), Box<dyn std::error::Error>> {
6543        if row_elems == 0 || n_rows == 0 {
6544            return Err("strided row placement requires nonzero rows and row width".into());
6545        }
6546        let src_len = n_rows
6547            .checked_mul(row_elems)
6548            .ok_or("strided row placement source size overflow")?;
6549        let dst_len = n_rows
6550            .checked_sub(1)
6551            .and_then(|rows| rows.checked_mul(dst_stride))
6552            .and_then(|base| base.checked_add(dst_off))
6553            .and_then(|base| base.checked_add(row_elems))
6554            .ok_or("strided row placement destination size overflow")?;
6555        let row_end = dst_off
6556            .checked_add(row_elems)
6557            .ok_or("strided row placement row size overflow")?;
6558        if src.len() < src_len || dst.len() < dst_len || row_end > dst_stride {
6559            return Err(format!(
6560                "strided row placement geometry mismatch: src={} need_src={src_len} \
6561                 dst={} need_dst={dst_len} row_elems={row_elems} rows={n_rows} \
6562                 dst_stride={dst_stride} dst_off={dst_off}",
6563                src.len(),
6564                dst.len(),
6565            )
6566            .into());
6567        }
6568        if row_elems > i32::MAX as usize || n_rows > i32::MAX as usize {
6569            return Err("strided row placement exceeds CUDA kernel geometry".into());
6570        }
6571        let f = self.func("place_rows_strided_f32");
6572        let cfg = LaunchConfig {
6573            grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
6574            block_dim: (256, 1, 1),
6575            shared_mem_bytes: 0,
6576        };
6577        let (re, nr) = (row_elems as i32, n_rows as i32);
6578        let (st, off) = (dst_stride as i64, dst_off as i64);
6579        let __s_b = self.gpu.stream();
6580        let mut b = __s_b.launch_builder(&f);
6581        b.arg(src)
6582            .arg(&mut *dst)
6583            .arg(&re)
6584            .arg(&nr)
6585            .arg(&st)
6586            .arg(&off);
6587        unsafe {
6588            b.launch(cfg)?;
6589        }
6590        Ok(())
6591    }
6592
6593    /// Async device u32 store (value rides the kernel ARG — no host-memory transfer/sync).
6594    pub fn u32_set_k(
6595        &self,
6596        dst: &mut CudaSlice<u32>,
6597        v: u32,
6598        idx: usize,
6599    ) -> Result<(), Box<dyn std::error::Error>> {
6600        let f = self.func("u32_set_k");
6601        let cfg = LaunchConfig {
6602            grid_dim: (1, 1, 1),
6603            block_dim: (1, 1, 1),
6604            shared_mem_bytes: 0,
6605        };
6606        let ii = idx as i32;
6607        let __s_b = self.gpu.stream();
6608        let mut b = __s_b.launch_builder(&f);
6609        b.arg(dst).arg(&v).arg(&ii);
6610        unsafe {
6611            b.launch(cfg)?;
6612        }
6613        Ok(())
6614    }
6615
6616    /// counter += v (device-slot append advance; the +1 twin is `inc_seqlen`).
6617    pub fn i32_add_k(
6618        &self,
6619        d: &mut CudaSlice<i32>,
6620        v: i32,
6621    ) -> Result<(), Box<dyn std::error::Error>> {
6622        let f = self.func("i32_add_k");
6623        let cfg = LaunchConfig {
6624            grid_dim: (1, 1, 1),
6625            block_dim: (32, 1, 1),
6626            shared_mem_bytes: 0,
6627        };
6628        let __s_b = self.gpu.stream();
6629        let mut b = __s_b.launch_builder(&f);
6630        b.arg(d).arg(&v);
6631        unsafe {
6632            b.launch(cfg)?;
6633        }
6634        Ok(())
6635    }
6636
6637    /// pos rows from a device counter: dst[i] = ctr[0] + i (verify-stream rope positions).
6638    pub fn i32_iota_from(
6639        &self,
6640        ctr: &CudaSlice<i32>,
6641        dst: &mut CudaSlice<i32>,
6642        n: usize,
6643    ) -> Result<(), Box<dyn std::error::Error>> {
6644        let f = self.func("i32_iota_from");
6645        let cfg = LaunchConfig::for_num_elems(n as u32);
6646        let ni = n as i32;
6647        let __s_b = self.gpu.stream();
6648        let mut b = __s_b.launch_builder(&f);
6649        b.arg(ctr).arg(dst).arg(&ni);
6650        unsafe {
6651            b.launch(cfg)?;
6652        }
6653        Ok(())
6654    }
6655
6656    /// In-place trim-id translate: buf[idx] = map[buf[idx]] (FR-Spec d2t, async single-slot).
6657    pub fn u32_map_k(
6658        &self,
6659        buf: &mut CudaSlice<u32>,
6660        map: &CudaSlice<u32>,
6661        idx: usize,
6662    ) -> Result<(), Box<dyn std::error::Error>> {
6663        let f = self.func("u32_map_k");
6664        let cfg = LaunchConfig {
6665            grid_dim: (1, 1, 1),
6666            block_dim: (1, 1, 1),
6667            shared_mem_bytes: 0,
6668        };
6669        let ii = idx as i32;
6670        let __s_b = self.gpu.stream();
6671        let mut b = __s_b.launch_builder(&f);
6672        b.arg(buf).arg(map).arg(&ii);
6673        unsafe {
6674            b.launch(cfg)?;
6675        }
6676        Ok(())
6677    }
6678
6679    /// Pack a[off..off+n1] ++ b[0..n2] into one buffer (single dtoh follows).
6680    #[allow(clippy::too_many_arguments)]
6681    pub fn u32_pack2(
6682        &self,
6683        a: &CudaSlice<u32>,
6684        off_a: usize,
6685        n1: usize,
6686        b_in: &CudaSlice<u32>,
6687        n2: usize,
6688        out: &mut CudaSlice<u32>,
6689    ) -> Result<(), Box<dyn std::error::Error>> {
6690        let f = self.func("u32_pack2");
6691        let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
6692        let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
6693        let __s_b = self.gpu.stream();
6694        let mut b = __s_b.launch_builder(&f);
6695        b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
6696        unsafe {
6697            b.launch(cfg)?;
6698        }
6699        Ok(())
6700    }
6701
6702    /// gemma4 R3 device fold: w[i] *= s[sel[i]] over the router's [n] (sel, w) pair.
6703    pub fn moe_w_exscale(
6704        &self,
6705        w: &mut CudaSlice<f32>,
6706        sel: &CudaSlice<i32>,
6707        s: &CudaSlice<f32>,
6708        n: usize,
6709    ) -> Result<(), Box<dyn std::error::Error>> {
6710        let f = self.func("moe_w_exscale");
6711        let cfg = LaunchConfig::for_num_elems(n as u32);
6712        let ni = n as i32;
6713        let __s_b = self.gpu.stream();
6714        let mut b = __s_b.launch_builder(&f);
6715        b.arg(w).arg(sel).arg(s).arg(&ni);
6716        unsafe {
6717            b.launch(cfg)?;
6718        }
6719        Ok(())
6720    }
6721
6722    /// Down-projection macro fold: w[i] *= macros[2*n_expert + sel[i]] on the device router
6723    /// weights (one launch per MoE layer, only for macro-carrying artifacts — see MoeWeights).
6724    pub fn moe_w_scale_by_expert(
6725        &self,
6726        w: &mut CudaSlice<f32>,
6727        sel: &CudaSlice<i32>,
6728        macros: &CudaSlice<f32>,
6729        n_expert: usize,
6730        n: usize,
6731    ) -> Result<(), Box<dyn std::error::Error>> {
6732        let f = self.func("moe_w_scale_by_expert");
6733        let cfg = LaunchConfig {
6734            grid_dim: (n.div_ceil(64) as u32, 1, 1),
6735            block_dim: (64, 1, 1),
6736            shared_mem_bytes: 0,
6737        };
6738        let (ne, nn) = (n_expert as i32, n as i32);
6739        let __s_b = self.gpu.stream();
6740        let mut b = __s_b.launch_builder(&f);
6741        b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
6742        unsafe {
6743            b.launch(cfg)?;
6744        }
6745        Ok(())
6746    }
6747
6748    pub fn moe_gate_up_silu8_dev_q8(
6749        &self,
6750        table: &CudaSlice<u64>,
6751        sel: &cudarc::driver::CudaView<i32>,
6752        aq: &CudaSlice<i8>,
6753        ad: &CudaSlice<f32>,
6754        in_f: usize,
6755        n_ff: usize,
6756        n_used: usize,
6757        n_expert: usize,
6758        qt_g: i32,
6759        qt_u: i32,
6760        rb_g: usize,
6761        rb_u: usize,
6762        macros: &CudaSlice<f32>,
6763    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6764        static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
6765        let (mode, wpb) = GU.get_or_init(|| {
6766            let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
6767            let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
6768                .ok()
6769                .and_then(|v| v.parse().ok())
6770                .unwrap_or(4u32)
6771                .clamp(1, 16);
6772            (mode, wpb)
6773        });
6774        let (mode, wpb) = (mode.as_str(), *wpb);
6775        let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
6776        let (inf, nff, ne, rbg, rbu) = (
6777            in_f as i32,
6778            n_ff as i32,
6779            n_expert as i32,
6780            rb_g as i64,
6781            rb_u as i64,
6782        );
6783        let (f, cfg) = match mode {
6784            "1" | "2" | "4" => {
6785                let rpw: u32 = mode.parse().unwrap();
6786                let f = self.func(match rpw {
6787                    1 => "moe_gate_up_silu8_dev_q8_r1",
6788                    2 => "moe_gate_up_silu8_dev_q8_r2",
6789                    _ => "moe_gate_up_silu8_dev_q8_r4",
6790                });
6791                let rows_per_block = (rpw * wpb) as usize;
6792                let gx = n_ff.div_ceil(rows_per_block) as u32;
6793                (
6794                    f,
6795                    LaunchConfig {
6796                        grid_dim: (gx, n_used as u32, 1),
6797                        block_dim: (32, wpb, 1),
6798                        shared_mem_bytes: 0,
6799                    },
6800                )
6801            }
6802            "j8" if n_used <= 32 => (
6803                self.func("moe_gate_up_silu8_dev_q8_j8"),
6804                LaunchConfig {
6805                    grid_dim: (n_ff as u32, 1, 1),
6806                    block_dim: (32, n_used as u32, 1),
6807                    shared_mem_bytes: 0,
6808                },
6809            ),
6810            // SMEM-GRID twins (IQ3_S 2KB grid copied to shared, static smem — bit-identical dots)
6811            "vsm2" => {
6812                let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
6813                let sh = (rb_g + rb_u) as u32;
6814                use cudarc::driver::sys::CUfunction_attribute_enum as A;
6815                f.set_attribute(
6816                    A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
6817                    sh as i32,
6818                )?;
6819                (
6820                    f,
6821                    LaunchConfig {
6822                        grid_dim: (n_ff as u32, n_used as u32, 1),
6823                        block_dim: (32, 1, 1),
6824                        shared_mem_bytes: sh,
6825                    },
6826                )
6827            }
6828            "vsm" => {
6829                let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
6830                let sh = (rb_g + rb_u) as u32;
6831                use cudarc::driver::sys::CUfunction_attribute_enum as A;
6832                f.set_attribute(
6833                    A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
6834                    sh as i32,
6835                )?;
6836                (
6837                    f,
6838                    LaunchConfig {
6839                        grid_dim: (n_ff as u32, n_used as u32, 1),
6840                        block_dim: (32, 1, 1),
6841                        shared_mem_bytes: sh,
6842                    },
6843                )
6844            }
6845            "sg" => (
6846                self.func("moe_gate_up_silu8_dev_q8_sg"),
6847                LaunchConfig {
6848                    grid_dim: (n_ff as u32, n_used as u32, 1),
6849                    block_dim: (32, 1, 1),
6850                    shared_mem_bytes: 0,
6851                },
6852            ),
6853            "j8sg" if n_used <= 32 => (
6854                self.func("moe_gate_up_silu8_dev_q8_j8sg"),
6855                LaunchConfig {
6856                    grid_dim: (n_ff as u32, 1, 1),
6857                    block_dim: (32, n_used as u32, 1),
6858                    shared_mem_bytes: 0,
6859                },
6860            ),
6861            "u64" if in_f == 2048 => (
6862                self.func("moe_gate_up_silu8_dev_q8_u64"),
6863                LaunchConfig {
6864                    grid_dim: (n_ff as u32, n_used as u32, 1),
6865                    block_dim: (32, 1, 1),
6866                    shared_mem_bytes: 0,
6867                },
6868            ),
6869            "gs4" if in_f == 2048 => (
6870                self.func("moe_gate_up_silu8_dev_q8_gs4"),
6871                LaunchConfig {
6872                    grid_dim: (n_ff as u32, n_used as u32, 1),
6873                    block_dim: (32, 4, 1),
6874                    shared_mem_bytes: 0,
6875                },
6876            ),
6877            // _v twin (down8 lane 2026-07-08): wide-load IQ4_XS dot, base geometry, bit-identical.
6878            "v" | "" => (
6879                self.func("moe_gate_up_silu8_dev_q8_v"),
6880                LaunchConfig {
6881                    grid_dim: (n_ff as u32, n_used as u32, 1),
6882                    block_dim: (32, 1, 1),
6883                    shared_mem_bytes: 0,
6884                },
6885            ),
6886            "s2" => (
6887                self.func("moe_gate_up_silu8_dev_q8_s2"),
6888                LaunchConfig {
6889                    grid_dim: (n_ff as u32, n_used as u32, 1),
6890                    block_dim: (32, 2, 1),
6891                    shared_mem_bytes: 0,
6892                },
6893            ),
6894            "s2z" => {
6895                let rz = wpb.min(16); // s2z smem tile is [16][2]
6896                (
6897                    self.func("moe_gate_up_silu8_dev_q8_s2z"),
6898                    LaunchConfig {
6899                        grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
6900                        block_dim: (32, 2, rz),
6901                        shared_mem_bytes: 0,
6902                    },
6903                )
6904            }
6905            _ => (
6906                self.func("moe_gate_up_silu8_dev_q8"),
6907                LaunchConfig {
6908                    grid_dim: (n_ff as u32, n_used as u32, 1),
6909                    block_dim: (32, 1, 1),
6910                    shared_mem_bytes: 0,
6911                },
6912            ),
6913        };
6914        let __s_b = self.gpu.stream();
6915        let mut b = __s_b.launch_builder(&f);
6916        b.arg(table)
6917            .arg(sel)
6918            .arg(aq)
6919            .arg(ad)
6920            .arg(&mut act)
6921            .arg(&inf)
6922            .arg(&nff)
6923            .arg(&ne)
6924            .arg(&qt_g)
6925            .arg(&qt_u)
6926            .arg(&rbg)
6927            .arg(&rbu)
6928            .arg(macros);
6929        unsafe {
6930            b.launch(cfg)?;
6931        }
6932        Ok(act)
6933    }
6934
6935    #[allow(clippy::too_many_arguments)]
6936    pub fn moe_down8_fma_dev_q8(
6937        &self,
6938        table: &CudaSlice<u64>,
6939        sel: &cudarc::driver::CudaView<i32>,
6940        w: &cudarc::driver::CudaView<f32>,
6941        aq2: &CudaSlice<i8>,
6942        ad2: &CudaSlice<f32>,
6943        dst: &mut cudarc::driver::CudaViewMut<f32>,
6944        in_f: usize,
6945        out_f: usize,
6946        n_used: usize,
6947        n_expert: usize,
6948        qt: i32,
6949        rb: usize,
6950    ) -> Result<(), Box<dyn std::error::Error>> {
6951        static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
6952        let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
6953        let (inf, outf, nu, ne, rbi) = (
6954            in_f as i32,
6955            out_f as i32,
6956            n_used as i32,
6957            n_expert as i32,
6958            rb as i64,
6959        );
6960        // the w8 twins' smem tile is [RPW][8] — n_used must fit the 8-slot tile;
6961        // the h2 twins are nsb==16 (in_f==512) shape-gated.
6962        let (f, cfg) = match mode.as_str() {
6963            m @ ("1" | "2" | "4") if n_used <= 8 => {
6964                let rpw: usize = m.parse().unwrap();
6965                let f = self.func(match rpw {
6966                    1 => "moe_down8_fma_dev_q8_w8r1",
6967                    2 => "moe_down8_fma_dev_q8_w8r2",
6968                    _ => "moe_down8_fma_dev_q8_w8r4",
6969                });
6970                (
6971                    f,
6972                    LaunchConfig {
6973                        grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
6974                        block_dim: (32, n_used as u32, 1),
6975                        shared_mem_bytes: 0,
6976                    },
6977                )
6978            }
6979            "h2" if in_f == 512 => (
6980                self.func("moe_down8_fma_dev_q8_h2"),
6981                LaunchConfig {
6982                    grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6983                    block_dim: (32, 1, 1),
6984                    shared_mem_bytes: 0,
6985                },
6986            ),
6987            // "" = AUTO gemma shape (in_f==704): w8r2 measured +1 tok/s vs base (sweep
6988            // 1/2/4 -> 133.6/134.2/133.6, 2026-07-10); slot-ordered chain preserved.
6989            "" if in_f == 704 && n_used <= 8 => (
6990                self.func("moe_down8_fma_dev_q8_w8r2"),
6991                LaunchConfig {
6992                    grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6993                    block_dim: (32, n_used as u32, 1),
6994                    shared_mem_bytes: 0,
6995                },
6996            ),
6997            // "" = AUTO: the measured winner for the 35B expert shape (arc 2026-07-05, +3.8%);
6998            // any shape the h2 kernels can't take (nsb!=16 / n_used>8) falls to base via `_`.
6999            // _v twins (down8 lane 2026-07-08): wide-load IQ4_XS dot, bit-identical outputs.
7000            "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
7001                self.func("moe_down8_fma_dev_q8_w8h2v"),
7002                LaunchConfig {
7003                    grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
7004                    block_dim: (32, n_used as u32, 1),
7005                    shared_mem_bytes: 0,
7006                },
7007            ),
7008            "w8h2r2v" if in_f == 512 && n_used <= 8 => (
7009                self.func("moe_down8_fma_dev_q8_w8h2r2v"),
7010                LaunchConfig {
7011                    grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
7012                    block_dim: (32, n_used as u32, 1),
7013                    shared_mem_bytes: 0,
7014                },
7015            ),
7016            "w8h2r2" if in_f == 512 && n_used <= 8 => (
7017                self.func("moe_down8_fma_dev_q8_w8h2r2"),
7018                LaunchConfig {
7019                    grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
7020                    block_dim: (32, n_used as u32, 1),
7021                    shared_mem_bytes: 0,
7022                },
7023            ),
7024            "w8h2" if in_f == 512 && n_used <= 8 => (
7025                self.func("moe_down8_fma_dev_q8_w8h2"),
7026                LaunchConfig {
7027                    grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
7028                    block_dim: (32, n_used as u32, 1),
7029                    shared_mem_bytes: 0,
7030                },
7031            ),
7032            _ => (
7033                self.func("moe_down8_fma_dev_q8"),
7034                LaunchConfig {
7035                    grid_dim: (out_f as u32, 1, 1),
7036                    block_dim: (32, 1, 1),
7037                    shared_mem_bytes: 0,
7038                },
7039            ),
7040        };
7041        let __s_b = self.gpu.stream();
7042        let mut b = __s_b.launch_builder(&f);
7043        b.arg(table)
7044            .arg(sel)
7045            .arg(w)
7046            .arg(aq2)
7047            .arg(ad2)
7048            .arg(dst)
7049            .arg(&inf)
7050            .arg(&outf)
7051            .arg(&nu)
7052            .arg(&ne)
7053            .arg(&qt)
7054            .arg(&rbi);
7055        unsafe {
7056            b.launch(cfg)?;
7057        }
7058        Ok(())
7059    }
7060
7061    /// SMALL-M VERIFY rows twin (MEMRA_SPEC_M2, lane/spec-m2): ONE launch covers all `t` tokens
7062    /// of the spec verify's MoE dev gate/up (grid.z = token) — the _v geometry per token, with
7063    /// tok-offset sel/aq/ad/act pointers matching the serial loop's slices. BIT-IDENTICAL per
7064    /// token (see the kernel header). aq/ad are the BATCHED z-quantize ([t, in_f] rows —
7065    /// quantize_q8_1's per-32-block program is row-independent, so batched rows == the serial
7066    /// loop's per-token quantize_q8_1_view bytes). Returns act [t, n_used, n_ff].
7067    #[allow(clippy::too_many_arguments)]
7068    pub fn moe_gate_up_silu8_dev_q8_rows(
7069        &self,
7070        table: &CudaSlice<u64>,
7071        sel: &CudaSlice<i32>,
7072        aq: &CudaSlice<i8>,
7073        ad: &CudaSlice<f32>,
7074        t: usize,
7075        in_f: usize,
7076        n_ff: usize,
7077        n_used: usize,
7078        n_expert: usize,
7079        qt_g: i32,
7080        qt_u: i32,
7081        rb_g: usize,
7082        rb_u: usize,
7083        macros: &CudaSlice<f32>,
7084    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7085        let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
7086        let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
7087        let cfg = LaunchConfig {
7088            grid_dim: (n_ff as u32, n_used as u32, t as u32),
7089            block_dim: (32, 1, 1),
7090            shared_mem_bytes: 0,
7091        };
7092        let (inf, nff, ne, nu, rbg, rbu) = (
7093            in_f as i32,
7094            n_ff as i32,
7095            n_expert as i32,
7096            n_used as i32,
7097            rb_g as i64,
7098            rb_u as i64,
7099        );
7100        let __s_b = self.gpu.stream();
7101        let mut b = __s_b.launch_builder(&f);
7102        b.arg(table)
7103            .arg(sel)
7104            .arg(aq)
7105            .arg(ad)
7106            .arg(&mut act)
7107            .arg(&inf)
7108            .arg(&nff)
7109            .arg(&ne)
7110            .arg(&qt_g)
7111            .arg(&qt_u)
7112            .arg(&rbg)
7113            .arg(&rbu)
7114            .arg(&nu)
7115            .arg(macros);
7116        unsafe {
7117            b.launch(cfg)?;
7118        }
7119        Ok(act)
7120    }
7121
7122    /// SMALL-M VERIFY rows twin of the down proj: w8h2v geometry per token on a grid.z token
7123    /// axis. Caller gates the w8h2v shape contract (in_f == 512, n_used <= 8) — same gate as
7124    /// the AUTO dispatch in `moe_down8_fma_dev_q8`. aq2/ad2 = batched act quantize
7125    /// ([t*n_used, in_f] rows). dst rows are FULLY overwritten per token.
7126    #[allow(clippy::too_many_arguments)]
7127    pub fn moe_down8_fma_dev_q8_rows(
7128        &self,
7129        table: &CudaSlice<u64>,
7130        sel: &CudaSlice<i32>,
7131        w: &CudaSlice<f32>,
7132        aq2: &CudaSlice<i8>,
7133        ad2: &CudaSlice<f32>,
7134        dst: &mut CudaSlice<f32>,
7135        t: usize,
7136        in_f: usize,
7137        out_f: usize,
7138        n_used: usize,
7139        n_expert: usize,
7140        qt: i32,
7141        rb: usize,
7142    ) -> Result<(), Box<dyn std::error::Error>> {
7143        assert!(
7144            in_f == 512 && n_used <= 8,
7145            "down rows twin is w8h2v shape-gated"
7146        );
7147        let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
7148        let cfg = LaunchConfig {
7149            grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
7150            block_dim: (32, n_used as u32, 1),
7151            shared_mem_bytes: 0,
7152        };
7153        let (inf, outf, nu, ne, rbi) = (
7154            in_f as i32,
7155            out_f as i32,
7156            n_used as i32,
7157            n_expert as i32,
7158            rb as i64,
7159        );
7160        let __s_b = self.gpu.stream();
7161        let mut b = __s_b.launch_builder(&f);
7162        b.arg(table)
7163            .arg(sel)
7164            .arg(w)
7165            .arg(aq2)
7166            .arg(ad2)
7167            .arg(dst)
7168            .arg(&inf)
7169            .arg(&outf)
7170            .arg(&nu)
7171            .arg(&ne)
7172            .arg(&qt)
7173            .arg(&rbi);
7174        unsafe {
7175            b.launch(cfg)?;
7176        }
7177        Ok(())
7178    }
7179
7180    /// CSR gate/up v3 (owner-scan dedup, no build kernel): qtypes {IQ4_XS, IQ3_S} (caller
7181    /// gates), grid.y = pair index; the first pair of each expert serves all its pairs.
7182    /// Bit-identical to moe_gate_up_silu8_dev_q8_v_rows (explicit-intrinsic accumulate).
7183    #[allow(clippy::too_many_arguments)]
7184    pub fn moe_gate_up_silu8_dev_q8_csr(
7185        &self,
7186        table: &CudaSlice<u64>,
7187        sel: &CudaSlice<i32>,
7188        aq: &CudaSlice<i8>,
7189        ad: &CudaSlice<f32>,
7190        n_pairs: usize,
7191        in_f: usize,
7192        n_ff: usize,
7193        n_used: usize,
7194        n_expert: usize,
7195        qt_g: i32,
7196        qt_u: i32,
7197        rb_g: usize,
7198        rb_u: usize,
7199    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7200        // NVFP4 experts take the NVFP4-specialized owner-scan twin (lane/moebatch-q35moe);
7201        // host gate guarantees qt_g == qt_u within a supported class.
7202        let f = if qt_g == crate::QT_NVFP4 {
7203            self.func("moe_gate_up_silu8_dev_q8_csr_nvfp4")
7204        } else {
7205            self.func("moe_gate_up_silu8_dev_q8_csr_iq4")
7206        };
7207        let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
7208        let cfg = LaunchConfig {
7209            grid_dim: (n_ff as u32, n_pairs as u32, 1),
7210            block_dim: (32, 1, 1),
7211            shared_mem_bytes: 0,
7212        };
7213        let (inf, nff, ne, nu, npi, rbg, rbu) = (
7214            in_f as i32,
7215            n_ff as i32,
7216            n_expert as i32,
7217            n_used as i32,
7218            n_pairs as i32,
7219            rb_g as i64,
7220            rb_u as i64,
7221        );
7222        let __s_b = self.gpu.stream();
7223        let mut b = __s_b.launch_builder(&f);
7224        b.arg(table)
7225            .arg(sel)
7226            .arg(aq)
7227            .arg(ad)
7228            .arg(&mut act)
7229            .arg(&inf)
7230            .arg(&nff)
7231            .arg(&ne)
7232            .arg(&qt_g)
7233            .arg(&qt_u)
7234            .arg(&rbg)
7235            .arg(&rbu)
7236            .arg(&nu)
7237            .arg(&npi);
7238        unsafe {
7239            b.launch(cfg)?;
7240        }
7241        Ok(act)
7242    }
7243
7244    /// TEST SEAM (down8 lane 2026-07-08): launch a down dev_q8 variant BY NAME with its
7245    /// canonical geometry, bypassing the env-cached dispatch so moe-devq8-check can byte-
7246    /// compare variants in one process. Variants: "base", "w8h2", "w8h2r2", "w8h2v", "w8h2r2v".
7247    #[allow(clippy::too_many_arguments)]
7248    pub fn moe_down8_fma_dev_q8_variant(
7249        &self,
7250        variant: &str,
7251        table: &CudaSlice<u64>,
7252        sel: &cudarc::driver::CudaView<i32>,
7253        w: &cudarc::driver::CudaView<f32>,
7254        aq2: &CudaSlice<i8>,
7255        ad2: &CudaSlice<f32>,
7256        dst: &mut cudarc::driver::CudaViewMut<f32>,
7257        in_f: usize,
7258        out_f: usize,
7259        n_used: usize,
7260        n_expert: usize,
7261        qt: i32,
7262        rb: usize,
7263    ) -> Result<(), Box<dyn std::error::Error>> {
7264        let (inf, outf, nu, ne, rbi) = (
7265            in_f as i32,
7266            out_f as i32,
7267            n_used as i32,
7268            n_expert as i32,
7269            rb as i64,
7270        );
7271        let (f, cfg) = match variant {
7272            "w8h2" | "w8h2v" => (
7273                self.func(if variant == "w8h2" {
7274                    "moe_down8_fma_dev_q8_w8h2"
7275                } else {
7276                    "moe_down8_fma_dev_q8_w8h2v"
7277                }),
7278                LaunchConfig {
7279                    grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
7280                    block_dim: (32, n_used as u32, 1),
7281                    shared_mem_bytes: 0,
7282                },
7283            ),
7284            "w8h2r2" | "w8h2r2v" => (
7285                self.func(if variant == "w8h2r2" {
7286                    "moe_down8_fma_dev_q8_w8h2r2"
7287                } else {
7288                    "moe_down8_fma_dev_q8_w8h2r2v"
7289                }),
7290                LaunchConfig {
7291                    grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
7292                    block_dim: (32, n_used as u32, 1),
7293                    shared_mem_bytes: 0,
7294                },
7295            ),
7296            _ => (
7297                self.func("moe_down8_fma_dev_q8"),
7298                LaunchConfig {
7299                    grid_dim: (out_f as u32, 1, 1),
7300                    block_dim: (32, 1, 1),
7301                    shared_mem_bytes: 0,
7302                },
7303            ),
7304        };
7305        let __s_b = self.gpu.stream();
7306        let mut b = __s_b.launch_builder(&f);
7307        b.arg(table)
7308            .arg(sel)
7309            .arg(w)
7310            .arg(aq2)
7311            .arg(ad2)
7312            .arg(dst)
7313            .arg(&inf)
7314            .arg(&outf)
7315            .arg(&nu)
7316            .arg(&ne)
7317            .arg(&qt)
7318            .arg(&rbi);
7319        unsafe {
7320            b.launch(cfg)?;
7321        }
7322        Ok(())
7323    }
7324
7325    /// TEST SEAM (down8 lane): gate_up twin of the above. Variants: "base", "v".
7326    #[allow(clippy::too_many_arguments)]
7327    pub fn moe_gate_up_silu8_dev_q8_variant(
7328        &self,
7329        variant: &str,
7330        table: &CudaSlice<u64>,
7331        sel: &cudarc::driver::CudaView<i32>,
7332        aq: &CudaSlice<i8>,
7333        ad: &CudaSlice<f32>,
7334        in_f: usize,
7335        n_ff: usize,
7336        n_used: usize,
7337        n_expert: usize,
7338        qt_g: i32,
7339        qt_u: i32,
7340        rb_g: usize,
7341        rb_u: usize,
7342    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7343        let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
7344        let (inf, nff, ne, rbg, rbu) = (
7345            in_f as i32,
7346            n_ff as i32,
7347            n_expert as i32,
7348            rb_g as i64,
7349            rb_u as i64,
7350        );
7351        let f = self.func(if variant == "v" {
7352            "moe_gate_up_silu8_dev_q8_v"
7353        } else {
7354            "moe_gate_up_silu8_dev_q8"
7355        });
7356        let cfg = LaunchConfig {
7357            grid_dim: (n_ff as u32, n_used as u32, 1),
7358            block_dim: (32, 1, 1),
7359            shared_mem_bytes: 0,
7360        };
7361        let __s_b = self.gpu.stream();
7362        let mut b = __s_b.launch_builder(&f);
7363        b.arg(table)
7364            .arg(sel)
7365            .arg(aq)
7366            .arg(ad)
7367            .arg(&mut act)
7368            .arg(&inf)
7369            .arg(&nff)
7370            .arg(&ne)
7371            .arg(&qt_g)
7372            .arg(&qt_u)
7373            .arg(&rbg)
7374            .arg(&rbu);
7375        unsafe {
7376            b.launch(cfg)?;
7377        }
7378        Ok(act)
7379    }
7380
7381    pub fn moe_gate_up_silu8_dev(
7382        &self,
7383        table: &CudaSlice<u64>,
7384        sel: &cudarc::driver::CudaView<i32>,
7385        x: &cudarc::driver::CudaView<f32>,
7386        in_f: usize,
7387        n_ff: usize,
7388        n_used: usize,
7389        n_expert: usize,
7390        qt_g: i32,
7391        qt_u: i32,
7392        rb_g: usize,
7393        rb_u: usize,
7394        macros: &CudaSlice<f32>,
7395    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7396        let f = self.func("moe_gate_up_silu8_dev");
7397        let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; // fully overwritten
7398        let cfg = LaunchConfig {
7399            grid_dim: (n_ff as u32, n_used as u32, 1),
7400            block_dim: (256, 1, 1),
7401            shared_mem_bytes: 0,
7402        };
7403        let (inf, nff, ne, rbg, rbu) = (
7404            in_f as i32,
7405            n_ff as i32,
7406            n_expert as i32,
7407            rb_g as i64,
7408            rb_u as i64,
7409        );
7410        let __s_b = self.gpu.stream();
7411        let mut b = __s_b.launch_builder(&f);
7412        b.arg(table)
7413            .arg(sel)
7414            .arg(x)
7415            .arg(&mut act)
7416            .arg(&inf)
7417            .arg(&nff)
7418            .arg(&ne)
7419            .arg(&qt_g)
7420            .arg(&qt_u)
7421            .arg(&rbg)
7422            .arg(&rbu)
7423            .arg(macros);
7424        unsafe {
7425            b.launch(cfg)?;
7426        }
7427        Ok(act)
7428    }
7429
7430    /// LAUNCH-STRUCTURE STAGE 3: device-dispatch twin of `moe_down8_fma_into` — expert ids AND
7431    /// renormalized weights read from the router kernel's device output. BIT-IDENTICAL chain.
7432    #[allow(clippy::too_many_arguments)]
7433    pub fn moe_down8_fma_dev(
7434        &self,
7435        table: &CudaSlice<u64>,
7436        sel: &cudarc::driver::CudaView<i32>,
7437        w: &cudarc::driver::CudaView<f32>,
7438        act: &CudaSlice<f32>,
7439        dst: &mut cudarc::driver::CudaViewMut<f32>,
7440        in_f: usize,
7441        out_f: usize,
7442        n_used: usize,
7443        n_expert: usize,
7444        qt: i32,
7445        rb: usize,
7446    ) -> Result<(), Box<dyn std::error::Error>> {
7447        let f = self.func("moe_down8_fma_dev");
7448        let cfg = LaunchConfig {
7449            grid_dim: (out_f as u32, 1, 1),
7450            block_dim: (256, 1, 1),
7451            shared_mem_bytes: 0,
7452        };
7453        let (inf, outf, nu, ne, rbv) = (
7454            in_f as i32,
7455            out_f as i32,
7456            n_used as i32,
7457            n_expert as i32,
7458            rb as i64,
7459        );
7460        let __s_b = self.gpu.stream();
7461        let mut b = __s_b.launch_builder(&f);
7462        b.arg(table)
7463            .arg(sel)
7464            .arg(w)
7465            .arg(act)
7466            .arg(dst)
7467            .arg(&inf)
7468            .arg(&outf)
7469            .arg(&nu)
7470            .arg(&ne)
7471            .arg(&qt)
7472            .arg(&rbv);
7473        unsafe {
7474            b.launch(cfg)?;
7475        }
7476        Ok(())
7477    }
7478
7479    /// dst[i] += alpha * src[i], i in 0..n. dst is a CudaViewMut (a row of moe_out).
7480    pub fn axpy_into(
7481        &self,
7482        src: &CudaSlice<f32>,
7483        alpha: f32,
7484        dst: &mut cudarc::driver::CudaViewMut<f32>,
7485        n: usize,
7486    ) -> Result<(), Box<dyn std::error::Error>> {
7487        let f = self.func("axpy_f32");
7488        let cfg = LaunchConfig::for_num_elems(n as u32);
7489        let (a, ni) = (alpha, n as i32);
7490        let __s_b = self.gpu.stream();
7491        let mut b = __s_b.launch_builder(&f);
7492        b.arg(src).arg(dst).arg(&a).arg(&ni);
7493        unsafe {
7494            b.launch(cfg)?;
7495        }
7496        Ok(())
7497    }
7498
7499    /// Host-oracle twin of `axpy_into` with separate RN multiply and add.
7500    pub fn axpy_host_into(
7501        &self,
7502        src: &cudarc::driver::CudaView<'_, f32>,
7503        alpha: f32,
7504        dst: &mut cudarc::driver::CudaViewMut<f32>,
7505        n: usize,
7506    ) -> Result<(), Box<dyn std::error::Error>> {
7507        let f = self.func("axpy_host_f32");
7508        let cfg = LaunchConfig::for_num_elems(n as u32);
7509        let (a, ni) = (alpha, n as i32);
7510        let __s_b = self.gpu.stream();
7511        let mut b = __s_b.launch_builder(&f);
7512        b.arg(src).arg(dst).arg(&a).arg(&ni);
7513        unsafe {
7514            b.launch(cfg)?;
7515        }
7516        Ok(())
7517    }
7518
7519    /// dst[r*ncols + c] += src[r*ncols + c] * scale[r]. Per-row scalar accumulate (shared expert).
7520    pub fn add_scaled_rows(
7521        &self,
7522        src: &CudaSlice<f32>,
7523        scale: &CudaSlice<f32>,
7524        dst: &mut CudaSlice<f32>,
7525        ncols: usize,
7526        nrows: usize,
7527    ) -> Result<(), Box<dyn std::error::Error>> {
7528        let f = self.func("add_scaled_rows_f32");
7529        let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
7530        let (nc, nr) = (ncols as i32, nrows as i32);
7531        let __s_b = self.gpu.stream();
7532        let mut b = __s_b.launch_builder(&f);
7533        b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
7534        unsafe {
7535            b.launch(cfg)?;
7536        }
7537        Ok(())
7538    }
7539
7540    /// y[r, :] *= s[r] in place (per-CSR-row macro scale for the grouped prime's gate/up —
7541    /// silu is nonlinear, so per-expert NVFP4 macros must land before it).
7542    pub fn scale_rows(
7543        &self,
7544        y: &mut CudaSlice<f32>,
7545        s: &CudaSlice<f32>,
7546        ncols: usize,
7547        nrows: usize,
7548    ) -> Result<(), Box<dyn std::error::Error>> {
7549        let f = self.func("scale_rows_f32");
7550        let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
7551        let (nc, nr) = (ncols as i32, nrows as i32);
7552        let __s_b = self.gpu.stream();
7553        let mut b = __s_b.launch_builder(&f);
7554        b.arg(&mut *y).arg(s).arg(&nc).arg(&nr);
7555        unsafe {
7556            b.launch(cfg)?;
7557        }
7558        Ok(())
7559    }
7560
7561    /// Fused grouped-prime tail: join both rank partials (canonical shard order), permute
7562    /// CSR->pair via `inv`, weight, and scatter to tokens in one pass — replaces
7563    /// rows_permute + add + scatter and the three large temporaries they needed.
7564    #[allow(clippy::too_many_arguments)]
7565    pub fn moe_prime_join_scatter(
7566        &self,
7567        y0: &CudaSlice<f32>,
7568        y1: &CudaSlice<f32>,
7569        inv: &CudaSlice<i32>,
7570        w: &CudaSlice<f32>,
7571        out: &mut CudaSlice<f32>,
7572        ncols: usize,
7573        n_used: usize,
7574        t: usize,
7575    ) -> Result<(), Box<dyn std::error::Error>> {
7576        let f = self.func("moe_prime_join_scatter_f32");
7577        let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
7578        let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
7579        let __s_b = self.gpu.stream();
7580        let mut b = __s_b.launch_builder(&f);
7581        b.arg(y0)
7582            .arg(y1)
7583            .arg(inv)
7584            .arg(w)
7585            .arg(&mut *out)
7586            .arg(&nc)
7587            .arg(&nu)
7588            .arg(&ti);
7589        unsafe {
7590            b.launch(cfg)?;
7591        }
7592        Ok(())
7593    }
7594
7595    /// out[t, :] += sum_j w[t*n_used+j] * y[t*n_used+j, :], the j-sum sequential per thread —
7596    /// a pinned per-token reduction order, never atomics (the grouped prime's scatter).
7597    pub fn moe_pairs_weighted_scatter(
7598        &self,
7599        y: &CudaSlice<f32>,
7600        w: &CudaSlice<f32>,
7601        out: &mut CudaSlice<f32>,
7602        ncols: usize,
7603        n_used: usize,
7604        t: usize,
7605    ) -> Result<(), Box<dyn std::error::Error>> {
7606        let f = self.func("moe_pairs_weighted_scatter_f32");
7607        let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
7608        let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
7609        let __s_b = self.gpu.stream();
7610        let mut b = __s_b.launch_builder(&f);
7611        b.arg(y).arg(w).arg(&mut *out).arg(&nc).arg(&nu).arg(&ti);
7612        unsafe {
7613            b.launch(cfg)?;
7614        }
7615        Ok(())
7616    }
7617
7618    // ======== A2 GROUPED MoE PREFILL KERNELS ========
7619
7620    /// Gather m_e rows from src[T, ncols] into dst[m_e, ncols] using index array idx[m_e].
7621    pub fn gather_rows(
7622        &self,
7623        src: &CudaSlice<f32>,
7624        idx: &CudaSlice<i32>,
7625        dst: &mut CudaSlice<f32>,
7626        ncols: usize,
7627        m_e: usize,
7628    ) -> Result<(), Box<dyn std::error::Error>> {
7629        let f = self.func("gather_rows_f32");
7630        let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
7631        let (nc, me) = (ncols as i32, m_e as i32);
7632        let __s_b = self.gpu.stream();
7633        let mut b = __s_b.launch_builder(&f);
7634        b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
7635        unsafe {
7636            b.launch(cfg)?;
7637        }
7638        Ok(())
7639    }
7640
7641    /// Scatter expert outputs into per-token slots: dst[tok_idx[r], slot_idx[r], :] = src[r, :] * weight[r].
7642    /// dst is [T, n_used, ncols], zero-initialized. Each (expert, token) pair maps to a unique slot.
7643    /// Scatter expert outputs into per-token slots (raw copy, no weight multiply).
7644    /// Weight stored into wbuf[tok*n_used + slot] for FMA in reduce step.
7645    pub fn scatter_slot(
7646        &self,
7647        src: &CudaSlice<f32>,
7648        tok_idx: &CudaSlice<i32>,
7649        slot_idx: &CudaSlice<i32>,
7650        weight: &CudaSlice<f32>,
7651        dst: &mut CudaSlice<f32>,
7652        wbuf: &mut CudaSlice<f32>,
7653        ncols: usize,
7654        n_used: usize,
7655        m_e: usize,
7656    ) -> Result<(), Box<dyn std::error::Error>> {
7657        let f = self.func("scatter_add_slot_f32");
7658        let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
7659        let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
7660        let __s_b = self.gpu.stream();
7661        let mut b = __s_b.launch_builder(&f);
7662        b.arg(src)
7663            .arg(tok_idx)
7664            .arg(slot_idx)
7665            .arg(weight)
7666            .arg(dst)
7667            .arg(wbuf)
7668            .arg(&nc)
7669            .arg(&nu)
7670            .arg(&me);
7671        unsafe {
7672            b.launch(cfg)?;
7673        }
7674        Ok(())
7675    }
7676
7677    /// Reduce n_used slots per token: dst[t, col] = sum_s slots[t, s, col].
7678    /// Reduce n_used slots per token: dst[t, col] = sum_s FMA(wbuf[t,s], slots[t,s,col], acc).
7679    /// Uses FMA for bit-identity with the sequential axpy path.
7680    pub fn reduce_slots(
7681        &self,
7682        slots: &CudaSlice<f32>,
7683        wbuf: &CudaSlice<f32>,
7684        dst: &mut CudaSlice<f32>,
7685        ncols: usize,
7686        n_used: usize,
7687        t: usize,
7688    ) -> Result<(), Box<dyn std::error::Error>> {
7689        let f = self.func("reduce_slots_f32");
7690        let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
7691        let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
7692        let __s_b = self.gpu.stream();
7693        let mut b = __s_b.launch_builder(&f);
7694        b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
7695        unsafe {
7696            b.launch(cfg)?;
7697        }
7698        Ok(())
7699    }
7700
7701    /// Canonical slot-order reduction with separately rounded multiply and add.
7702    ///
7703    /// This is the one-launch twin of repeated `axpy_host_into` calls. It preserves the official
7704    /// Step host-oracle arithmetic while allowing owner outputs to remain device-resident.
7705    pub fn reduce_slots_host(
7706        &self,
7707        slots: &CudaSlice<f32>,
7708        wbuf: &CudaSlice<f32>,
7709        dst: &mut CudaSlice<f32>,
7710        ncols: usize,
7711        n_used: usize,
7712        t: usize,
7713    ) -> Result<(), Box<dyn std::error::Error>> {
7714        let f = self.func("reduce_slots_host_f32");
7715        let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
7716        let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
7717        let __s_b = self.gpu.stream();
7718        let mut b = __s_b.launch_builder(&f);
7719        b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
7720        unsafe {
7721            b.launch(cfg)?;
7722        }
7723        Ok(())
7724    }
7725
7726    /// Stage-B: quantize activation [m,in] f32 -> q8_1 (int8 qs + per-block f32 scale).
7727    /// Quantize an activation [m, in_f] to q8_1 (int8 qs + per-32 f32 scale). Public so the
7728    /// forward can quantize a SHARED activation ONCE and feed it to several matmuls (gate+up
7729    /// share `z`; q/k/v and wqkv/gate/beta/alpha share `h`) — quantize_q8_1 was 13.5% of decode
7730    /// GPU time, ~half of it redundant re-quantization of the same row.
7731    /// quantize_q8_1 over a CudaView (a sliced z-row) — same kernel, offset-honoring arg.
7732    pub fn quantize_q8_1_view(
7733        &self,
7734        x: &cudarc::driver::CudaView<f32>,
7735        m: usize,
7736        in_f: usize,
7737    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7738        let f = self.func("quantize_q8_1");
7739        let nblk = in_f / 32;
7740        let mut q = self.alloc_uninit::<i8>(m * in_f)?;
7741        let mut d = self.alloc_uninit::<f32>(m * nblk)?;
7742        let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
7743        let (inf, mi) = (in_f as i32, m as i32);
7744        let __s_b = self.gpu.stream();
7745        let mut b = __s_b.launch_builder(&f);
7746        b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
7747        unsafe {
7748            b.launch(cfg)?;
7749        }
7750        Ok((q, d))
7751    }
7752
7753    pub fn quantize_q8_1(
7754        &self,
7755        x: &CudaSlice<f32>,
7756        m: usize,
7757        in_f: usize,
7758    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7759        let nblk = in_f / 32;
7760        let mut q = self.alloc_uninit::<i8>(m * in_f)?; // full-overwrite output: skip memset
7761        let mut d = self.alloc_uninit::<f32>(m * nblk)?; // full-overwrite output: skip memset
7762        // WARP-PER-BLOCK kernel: one warp per 32-block -> m*in_f threads total.
7763        let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
7764        let (inf, mi) = (in_f as i32, m as i32);
7765        if Self::pdl_on() && Self::pdl_wb_on() {
7766            {
7767                use cudarc::driver::{DevicePtr, DevicePtrMut};
7768                let s = &self.gpu.stream();
7769                let (px, _g0) = x.device_ptr(s);
7770                let (pq, _g1) = q.device_ptr_mut(s);
7771                let (pd, _g2) = d.device_ptr_mut(s);
7772                let mut ps = [
7773                    &px as *const _ as *mut std::ffi::c_void,
7774                    &pq as *const _ as *mut _,
7775                    &pd as *const _ as *mut _,
7776                    &inf as *const _ as *mut _,
7777                    &mi as *const _ as *mut _,
7778                ];
7779                unsafe {
7780                    self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
7781                }
7782            }
7783            return Ok((q, d));
7784        }
7785        let f = self.func("quantize_q8_1");
7786        let __s_b = self.gpu.stream();
7787        let mut b = __s_b.launch_builder(&f);
7788        b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
7789        unsafe {
7790            b.launch(cfg)?;
7791        }
7792        Ok((q, d))
7793    }
7794
7795    /// Stage-C FP4: quantize activation [m,in] f32 -> e2m1 nibbles (aq4: u32 [m, in/8]) + per-16
7796    /// UE4M3 scale (ad4: u8 [m, in/16]), the layout the mxf4nvf4 block-scale GEMM B-operand wants.
7797    /// in_f must be a multiple of 64 (one NVFP4 K-block). One thread per (token, 16-block).
7798    pub fn quantize_fp4_act(
7799        &self,
7800        x: &CudaSlice<f32>,
7801        m: usize,
7802        in_f: usize,
7803    ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
7804        let f = self.func("quantize_fp4_act");
7805        let nb16 = in_f / 16;
7806        let mut aq4 = self.alloc_uninit::<u32>(m * (in_f / 8))?; // full-overwrite output: skip memset
7807        let mut ad4 = self.alloc_uninit::<u8>(m * nb16)?; // full-overwrite output: skip memset
7808        let cfg = LaunchConfig::for_num_elems((m * nb16) as u32);
7809        let (inf, mi) = (in_f as i32, m as i32);
7810        let __s_b = self.gpu.stream();
7811        let mut b = __s_b.launch_builder(&f);
7812        b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
7813        unsafe {
7814            b.launch(cfg)?;
7815        }
7816        Ok((aq4, ad4))
7817    }
7818
7819    /// Stage-C FP4 GEMM (NVFP4 weights): native mxf4nvf4 block-scale tensor-core matmul. Feeds raw
7820    /// e2m1 weight nibbles + raw UE4M3 micro-scales directly to mma.sync.m16n8k64 (762 TFLOP/s peak,
7821    /// 3.5x int8). Activation `x` is quantized to FP4 e2m1 here. NVFP4 per-tensor macro-scale applied
7822    /// post (scale==1.0 -> no-op). `bytes` = raw NVFP4 weight rows. Used by the MEMRA_FP4 prefill path.
7823    pub fn qmatvec_gemm_nvfp4_fp4(
7824        &self,
7825        bytes: &CudaSlice<u8>,
7826        x: &CudaSlice<f32>,
7827        m: usize,
7828        in_f: usize,
7829        out_f: usize,
7830        row_bytes: usize,
7831        scale: f32,
7832    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7833        assert!(
7834            in_f % 64 == 0,
7835            "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
7836        );
7837        let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
7838        let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
7839        if scale != 1.0 {
7840            self.scale_inplace(&mut y, scale, m * out_f)?;
7841        }
7842        Ok(y)
7843    }
7844
7845    /// Shared mxf4 GEMM launch (pre-quantized FP4 activation aq4/ad4). Same CTA tile as the int8 GEMM
7846    /// (BM=64 rows x BN=128 tokens, 4 warps). No macro-scale applied here.
7847    fn fp4_gemm_launch(
7848        &self,
7849        bytes: &CudaSlice<u8>,
7850        aq4: &CudaSlice<u32>,
7851        ad4: &CudaSlice<u8>,
7852        m: usize,
7853        in_f: usize,
7854        out_f: usize,
7855        row_bytes: usize,
7856    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7857        let f = self.func("qmatvec_gemm_nvfp4_fp4");
7858        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
7859        const BM: u32 = 64;
7860        const BN: u32 = 256;
7861        let cfg = LaunchConfig {
7862            grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
7863            block_dim: (32, 4, 1),
7864            shared_mem_bytes: 0,
7865        };
7866        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7867        let __s_b = self.gpu.stream();
7868        let mut b = __s_b.launch_builder(&f);
7869        b.arg(bytes)
7870            .arg(aq4)
7871            .arg(ad4)
7872            .arg(&mut y)
7873            .arg(&inf)
7874            .arg(&outf)
7875            .arg(&mi)
7876            .arg(&rb);
7877        unsafe {
7878            b.launch(cfg)?;
7879        }
7880        Ok(y)
7881    }
7882
7883    /// Test entry (kernel_check): run the FP4 GEMM from raw bytes; NO macro-scale (caller compares bare).
7884    pub fn qmatvec_gemm_nvfp4_fp4_raw(
7885        &self,
7886        bytes: &CudaSlice<u8>,
7887        x: &CudaSlice<f32>,
7888        m: usize,
7889        in_f: usize,
7890        out_f: usize,
7891        row_bytes: usize,
7892    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7893        assert!(
7894            in_f % 64 == 0,
7895            "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
7896        );
7897        let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
7898        self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
7899    }
7900
7901    /// Stage-B: Q8_0 weight x q8_1 activation int8 dp4a matmul. y[m,out]=x@W^T.
7902    pub fn qmatvec_q8_0_fast(
7903        &self,
7904        w: &CudaSlice<u8>,
7905        x: &CudaSlice<f32>,
7906        m: usize,
7907        in_f: usize,
7908        out_f: usize,
7909        row_bytes: usize,
7910    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7911        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7912        let f = self.func("qmatvec_q8_0_dp4a");
7913        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
7914        let cfg = LaunchConfig {
7915            grid_dim: (out_f as u32, m as u32, 1),
7916            block_dim: (128, 1, 1),
7917            shared_mem_bytes: 0,
7918        };
7919        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7920        let __s_b = self.gpu.stream();
7921        let mut b = __s_b.launch_builder(&f);
7922        b.arg(w)
7923            .arg(&aq)
7924            .arg(&ad)
7925            .arg(&mut y)
7926            .arg(&inf)
7927            .arg(&outf)
7928            .arg(&mi)
7929            .arg(&rb);
7930        unsafe {
7931            b.launch(cfg)?;
7932        }
7933        Ok(y)
7934    }
7935
7936    /// Stage-B: Q4_K weight x q8_1 activation int8 dp4a (decode). Min-offset via q8_1 sum term.
7937    #[allow(non_snake_case)] // Keep GGUF qtype spelling visible at the public kernel boundary.
7938    pub fn qmatvec_q4_K_fast(
7939        &self,
7940        w: &CudaSlice<u8>,
7941        x: &CudaSlice<f32>,
7942        m: usize,
7943        in_f: usize,
7944        out_f: usize,
7945        row_bytes: usize,
7946    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7947        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7948        let f = self.func("qmatvec_q4_K_dp4a");
7949        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
7950        let cfg = LaunchConfig {
7951            grid_dim: (out_f as u32, m as u32, 1),
7952            block_dim: (128, 1, 1),
7953            shared_mem_bytes: 0,
7954        };
7955        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7956        let __s_b = self.gpu.stream();
7957        let mut b = __s_b.launch_builder(&f);
7958        b.arg(w)
7959            .arg(&aq)
7960            .arg(&ad)
7961            .arg(&mut y)
7962            .arg(&inf)
7963            .arg(&outf)
7964            .arg(&mi)
7965            .arg(&rb);
7966        unsafe {
7967            b.launch(cfg)?;
7968        }
7969        Ok(y)
7970    }
7971
7972    /// Stage-B: Q6_K weight x q8_1 activation int8 dp4a (decode, symmetric).
7973    #[allow(non_snake_case)] // Keep GGUF qtype spelling visible at the public kernel boundary.
7974    pub fn qmatvec_q6_K_fast(
7975        &self,
7976        w: &CudaSlice<u8>,
7977        x: &CudaSlice<f32>,
7978        m: usize,
7979        in_f: usize,
7980        out_f: usize,
7981        row_bytes: usize,
7982    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7983        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7984        let f = self.func("qmatvec_q6_K_dp4a");
7985        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
7986        let cfg = LaunchConfig {
7987            grid_dim: (out_f as u32, m as u32, 1),
7988            block_dim: (128, 1, 1),
7989            shared_mem_bytes: 0,
7990        };
7991        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7992        let __s_b = self.gpu.stream();
7993        let mut b = __s_b.launch_builder(&f);
7994        b.arg(w)
7995            .arg(&aq)
7996            .arg(&ad)
7997            .arg(&mut y)
7998            .arg(&inf)
7999            .arg(&outf)
8000            .arg(&mi)
8001            .arg(&rb);
8002        unsafe {
8003            b.launch(cfg)?;
8004        }
8005        Ok(y)
8006    }
8007
8008    /// Stage-B: Q5_K weight x q8_1 activation int8 dp4a (decode). Min-offset via q8_1 sum term.
8009    #[allow(non_snake_case)] // Keep GGUF qtype spelling visible at the public kernel boundary.
8010    pub fn qmatvec_q5_K_fast(
8011        &self,
8012        w: &CudaSlice<u8>,
8013        x: &CudaSlice<f32>,
8014        m: usize,
8015        in_f: usize,
8016        out_f: usize,
8017        row_bytes: usize,
8018    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8019        self.qmatvec_dp4a_named(
8020            "qmatvec_q5_K_dp4a",
8021            &w.slice(0..w.len()),
8022            x,
8023            m,
8024            in_f,
8025            out_f,
8026            row_bytes,
8027        )
8028    }
8029    /// Stage-B: Q3_K weight x q8_1 activation int8 dp4a (decode, symmetric).
8030    #[allow(non_snake_case)] // Keep GGUF qtype spelling visible at the public kernel boundary.
8031    pub fn qmatvec_q3_K_fast(
8032        &self,
8033        w: &CudaSlice<u8>,
8034        x: &CudaSlice<f32>,
8035        m: usize,
8036        in_f: usize,
8037        out_f: usize,
8038        row_bytes: usize,
8039    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8040        self.qmatvec_dp4a_named(
8041            "qmatvec_q3_K_dp4a",
8042            &w.slice(0..w.len()),
8043            x,
8044            m,
8045            in_f,
8046            out_f,
8047            row_bytes,
8048        )
8049    }
8050    /// A6 split-plane twin of `qmatvec_nvfp4_fast` (weights repacked; used by the rp gates).
8051    pub fn qmatvec_nvfp4_fast_rp(
8052        &self,
8053        w: &CudaSlice<u8>,
8054        x: &CudaSlice<f32>,
8055        m: usize,
8056        in_f: usize,
8057        out_f: usize,
8058        row_bytes: usize,
8059    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8060        assert!(
8061            in_f % 64 == 0,
8062            "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
8063        );
8064        self.qmatvec_dp4a_named(
8065            "qmatvec_nvfp4_dp4a_rp",
8066            &w.slice(0..w.len()),
8067            x,
8068            m,
8069            in_f,
8070            out_f,
8071            row_bytes,
8072        )
8073    }
8074    /// Stage-B: NVFP4 weight x q8_1 activation int8 dp4a (decode, symmetric, codebook lookup).
8075    pub fn qmatvec_nvfp4_fast(
8076        &self,
8077        w: &cudarc::driver::CudaView<'_, u8>,
8078        x: &CudaSlice<f32>,
8079        m: usize,
8080        in_f: usize,
8081        out_f: usize,
8082        row_bytes: usize,
8083    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8084        // B1: the NVFP4 dp4a kernel maps two 32-elem q8_1 blocks onto one 64-elem block_nvfp4
8085        // (sblk = g >> 1). in_f must be a multiple of 64 or the last block reads a partial superblock.
8086        assert!(
8087            in_f % 64 == 0,
8088            "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
8089        );
8090        self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
8091    }
8092    /// Slot-major-layout twin of `qmatvec_nvfp4_fast`: bit-identical per row, coalesced
8093    /// reads. Since the 2026-08-29 `MEMRA_NVFP4_BANK_V2` door removal its only in-tree
8094    /// producer of slot-major banks is the EP2 whole-expert bank build; this is EP2's
8095    /// host-canonical oracle reader (plus offline harnesses like moe_tp2_repro).
8096    pub fn qmatvec_nvfp4_fast_v2(
8097        &self,
8098        w: &cudarc::driver::CudaView<'_, u8>,
8099        x: &CudaSlice<f32>,
8100        m: usize,
8101        in_f: usize,
8102        out_f: usize,
8103        row_bytes: usize,
8104    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8105        assert!(
8106            in_f % 64 == 0,
8107            "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
8108        );
8109        self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_v2", w, x, m, in_f, out_f, row_bytes)
8110    }
8111    /// Stage-B (optional perf): IQ4_XS codebook int8 dp4a.
8112    #[allow(non_snake_case)] // Keep GGUF qtype spelling visible at the public kernel boundary.
8113    pub fn qmatvec_iq4_XS_fast(
8114        &self,
8115        w: &CudaSlice<u8>,
8116        x: &CudaSlice<f32>,
8117        m: usize,
8118        in_f: usize,
8119        out_f: usize,
8120        row_bytes: usize,
8121    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8122        self.qmatvec_dp4a_named(
8123            "qmatvec_iq4_XS_dp4a",
8124            &w.slice(0..w.len()),
8125            x,
8126            m,
8127            in_f,
8128            out_f,
8129            row_bytes,
8130        )
8131    }
8132
8133    /// Shared dp4a launcher: quantize_q8_1 then call the named kernel (grid (out,m), block 64).
8134    fn qmatvec_dp4a_named(
8135        &self,
8136        name: &str,
8137        w: &cudarc::driver::CudaView<'_, u8>,
8138        x: &CudaSlice<f32>,
8139        m: usize,
8140        in_f: usize,
8141        out_f: usize,
8142        row_bytes: usize,
8143    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8144        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
8145        let f = self.func(name);
8146        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
8147        let cfg = LaunchConfig {
8148            grid_dim: (out_f as u32, m as u32, 1),
8149            block_dim: (128, 1, 1),
8150            shared_mem_bytes: 0,
8151        };
8152        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
8153        let __s_b = self.gpu.stream();
8154        let mut b = __s_b.launch_builder(&f);
8155        b.arg(w)
8156            .arg(&aq)
8157            .arg(&ad)
8158            .arg(&mut y)
8159            .arg(&inf)
8160            .arg(&outf)
8161            .arg(&mi)
8162            .arg(&rb);
8163        unsafe {
8164            b.launch(cfg)?;
8165        }
8166        Ok(y)
8167    }
8168
8169    /// NVFP4 dp4a matvec over PRE-QUANTIZED q8_1 activations, writing a caller-provided output.
8170    /// Same kernel and math as `qmatvec_nvfp4_fast` (which quantizes internally and allocates
8171    /// its output); this entry exists so a routed-expert program can quantize one activation
8172    /// ONCE and reuse it across every expert's gate/up, feed `silu_mul_scaled_q8_1`'s q8_1
8173    /// straight into down, and keep persistent output workspaces — zero per-expert allocations.
8174    #[allow(clippy::too_many_arguments)]
8175    pub fn qmatvec_nvfp4_fast_prequant_into(
8176        &self,
8177        w: &CudaSlice<u8>,
8178        aq: &CudaSlice<i8>,
8179        ad: &CudaSlice<f32>,
8180        y: &mut CudaSlice<f32>,
8181        m: usize,
8182        in_f: usize,
8183        out_f: usize,
8184        row_bytes: usize,
8185    ) -> Result<(), Box<dyn std::error::Error>> {
8186        assert!(
8187            in_f % 64 == 0,
8188            "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
8189        );
8190        if y.len() < m * out_f {
8191            return Err(format!(
8192                "NVFP4 prequant output {} is shorter than {m}x{out_f}",
8193                y.len()
8194            )
8195            .into());
8196        }
8197        let f = self.func("qmatvec_nvfp4_dp4a");
8198        let cfg = LaunchConfig {
8199            grid_dim: (out_f as u32, m as u32, 1),
8200            block_dim: (128, 1, 1),
8201            shared_mem_bytes: 0,
8202        };
8203        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
8204        let __s_b = self.gpu.stream();
8205        let mut b = __s_b.launch_builder(&f);
8206        b.arg(w)
8207            .arg(aq)
8208            .arg(ad)
8209            .arg(y)
8210            .arg(&inf)
8211            .arg(&outf)
8212            .arg(&mi)
8213            .arg(&rb);
8214        unsafe {
8215            b.launch(cfg)?;
8216        }
8217        Ok(())
8218    }
8219
8220    /// Fused QKV F32 matvec (one launch for all three rank-local projections; see the kernel
8221    /// doc for the numeric-class note). Requires `in_f % 4 == 0`.
8222    #[allow(clippy::too_many_arguments)]
8223    pub fn matvec_f32_qkv_into(
8224        &self,
8225        wq: &CudaSlice<f32>,
8226        wk: &CudaSlice<f32>,
8227        wv: &CudaSlice<f32>,
8228        wg: &CudaSlice<f32>,
8229        x: &CudaSlice<f32>,
8230        yq: &mut CudaSlice<f32>,
8231        yk: &mut CudaSlice<f32>,
8232        yv: &mut CudaSlice<f32>,
8233        yg: &mut CudaSlice<f32>,
8234        in_f: usize,
8235        out_q: usize,
8236        out_kv: usize,
8237        out_g: usize,
8238    ) -> Result<(), Box<dyn std::error::Error>> {
8239        if in_f % 4 != 0
8240            || wq.len() != out_q * in_f
8241            || wk.len() != out_kv * in_f
8242            || wv.len() != out_kv * in_f
8243            || wg.len() < out_g * in_f
8244            || x.len() < in_f
8245            || yq.len() < out_q
8246            || yk.len() < out_kv
8247            || yv.len() < out_kv
8248            || (out_g > 0 && yg.len() < out_g)
8249        {
8250            return Err(format!(
8251                "fused QKV geometry in={in_f} out_q={out_q} out_kv={out_kv} out_g={out_g} \
8252                 wq={} wk={} wv={} wg={}",
8253                wq.len(),
8254                wk.len(),
8255                wv.len(),
8256                wg.len()
8257            )
8258            .into());
8259        }
8260        let f = self.func("matvec_f32_qkv");
8261        let cfg = LaunchConfig {
8262            grid_dim: ((out_q + 2 * out_kv + out_g) as u32, 1, 1),
8263            block_dim: (128, 1, 1),
8264            shared_mem_bytes: 0,
8265        };
8266        let (inf, oq, okv, og) = (in_f as i32, out_q as i32, out_kv as i32, out_g as i32);
8267        let __s_b = self.gpu.stream();
8268        let mut b = __s_b.launch_builder(&f);
8269        b.arg(wq)
8270            .arg(wk)
8271            .arg(wv)
8272            .arg(wg)
8273            .arg(x)
8274            .arg(yq)
8275            .arg(yk)
8276            .arg(yv)
8277            .arg(yg)
8278            .arg(&inf)
8279            .arg(&oq)
8280            .arg(&okv)
8281            .arg(&og);
8282        unsafe {
8283            b.launch(cfg)?;
8284        }
8285        Ok(())
8286    }
8287
8288    /// EP2 owner-guarded gate+up sweep: full-width rows, pairs whose expert this rank
8289    /// does not own exit immediately. Per-pair dot == the _sel_v2 gu body.
8290    #[allow(clippy::too_many_arguments)]
8291    pub fn qmatvec_nvfp4_sel_gu_ep_into(
8292        &self,
8293        gate_bank: &CudaSlice<u8>,
8294        up_bank: &CudaSlice<u8>,
8295        sel: &CudaSlice<i32>,
8296        aq: &CudaSlice<i8>,
8297        ad: &CudaSlice<f32>,
8298        yg: &mut CudaSlice<f32>,
8299        yu: &mut CudaSlice<f32>,
8300        n_sel: usize,
8301        in_f: usize,
8302        out_f: usize,
8303        row_bytes: usize,
8304        expert_stride: usize,
8305        owner: usize,
8306    ) -> Result<(), Box<dyn std::error::Error>> {
8307        assert!(in_f % 64 == 0, "NVFP4 dp4a requires in_f % 64 == 0");
8308        if yg.len() < n_sel * out_f || yu.len() < n_sel * out_f || sel.len() < n_sel {
8309            return Err("NVFP4 gu ep geometry".into());
8310        }
8311        let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_gu_ep");
8312        let cfg = LaunchConfig {
8313            grid_dim: ((2 * out_f) as u32, n_sel as u32, 1),
8314            block_dim: (128, 1, 1),
8315            shared_mem_bytes: 0,
8316        };
8317        let (inf, outf, ns, own) = (in_f as i32, out_f as i32, n_sel as i32, owner as i32);
8318        let (rb, es) = (row_bytes as i64, expert_stride as i64);
8319        let (ars, adrs) = (0i64, 0i64);
8320        let __s_b = self.gpu.stream();
8321        let mut b = __s_b.launch_builder(&f);
8322        b.arg(gate_bank)
8323            .arg(up_bank)
8324            .arg(sel)
8325            .arg(aq)
8326            .arg(ad)
8327            .arg(yg)
8328            .arg(yu)
8329            .arg(&inf)
8330            .arg(&outf)
8331            .arg(&ns)
8332            .arg(&rb)
8333            .arg(&es)
8334            .arg(&ars)
8335            .arg(&adrs)
8336            .arg(&own);
8337        unsafe {
8338            b.launch(cfg)?;
8339        }
8340        Ok(())
8341    }
8342
8343    /// EP2 owner-guarded SwiGLU (q8_1 emission), clamped or plain by `limit`.
8344    #[allow(clippy::too_many_arguments)]
8345    pub fn silu_mul_scaled_q8_1_sel_ep_into(
8346        &self,
8347        gate: &CudaSlice<f32>,
8348        up: &CudaSlice<f32>,
8349        gmac: &CudaSlice<f32>,
8350        umac: &CudaSlice<f32>,
8351        sel: &CudaSlice<i32>,
8352        limit: Option<f32>,
8353        out_q: &mut CudaSlice<i8>,
8354        out_d: &mut CudaSlice<f32>,
8355        n_per: usize,
8356        n_sel: usize,
8357        owner: usize,
8358    ) -> Result<(), Box<dyn std::error::Error>> {
8359        if n_per % 32 != 0 || out_q.len() < n_sel * n_per || out_d.len() < n_sel * n_per / 32 {
8360            return Err("NVFP4 silu ep geometry".into());
8361        }
8362        let f = self.func("silu_mul_scaled_q8_1_sel_ep");
8363        let warps = n_sel * n_per / 32;
8364        let cfg = LaunchConfig {
8365            grid_dim: ((warps as u32).div_ceil(4), 1, 1),
8366            block_dim: (128, 1, 1),
8367            shared_mem_bytes: 0,
8368        };
8369        let (np, ns, own) = (n_per as i32, n_sel as i32, owner as i32);
8370        let (lim, has) = match limit {
8371            Some(l) => (l, 1i32),
8372            None => (0.0f32, 0i32),
8373        };
8374        let __s_b = self.gpu.stream();
8375        let mut b = __s_b.launch_builder(&f);
8376        b.arg(gate)
8377            .arg(up)
8378            .arg(gmac)
8379            .arg(umac)
8380            .arg(sel)
8381            .arg(&lim)
8382            .arg(&has)
8383            .arg(out_q)
8384            .arg(out_d)
8385            .arg(&np)
8386            .arg(&ns)
8387            .arg(&own);
8388        unsafe {
8389            b.launch(cfg)?;
8390        }
8391        Ok(())
8392    }
8393
8394    /// EP2 owner-guarded down + owned-slot combine in one launch (block `(32, n_sel)`).
8395    #[allow(clippy::too_many_arguments)]
8396    pub fn qmatvec_nvfp4_sel_down8_ep_into(
8397        &self,
8398        bank: &CudaSlice<u8>,
8399        sel: &CudaSlice<i32>,
8400        aq: &CudaSlice<i8>,
8401        ad: &CudaSlice<f32>,
8402        route_w: &CudaSlice<f32>,
8403        md: &CudaSlice<f32>,
8404        dst: &mut CudaSlice<f32>,
8405        n_sel: usize,
8406        in_f: usize,
8407        out_f: usize,
8408        row_bytes: usize,
8409        expert_stride: usize,
8410        act_row_stride: usize,
8411        ad_row_stride: usize,
8412        owner: usize,
8413    ) -> Result<(), Box<dyn std::error::Error>> {
8414        if in_f % 64 != 0 || n_sel == 0 || n_sel > 8 || (in_f >> 5) > 64 || dst.len() < out_f {
8415            return Err("NVFP4 down8 ep geometry".into());
8416        }
8417        let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_down8_ep");
8418        let cfg = LaunchConfig {
8419            grid_dim: (out_f as u32, 1, 1),
8420            block_dim: (32, n_sel as u32, 1),
8421            shared_mem_bytes: 0,
8422        };
8423        let (inf, outf, ns, own) = (in_f as i32, out_f as i32, n_sel as i32, owner as i32);
8424        let (rb, es) = (row_bytes as i64, expert_stride as i64);
8425        let (ars, adrs) = (act_row_stride as i64, ad_row_stride as i64);
8426        let __s_b = self.gpu.stream();
8427        let mut b = __s_b.launch_builder(&f);
8428        b.arg(bank)
8429            .arg(sel)
8430            .arg(aq)
8431            .arg(ad)
8432            .arg(route_w)
8433            .arg(md)
8434            .arg(dst)
8435            .arg(&inf)
8436            .arg(&outf)
8437            .arg(&ns)
8438            .arg(&rb)
8439            .arg(&es)
8440            .arg(&ars)
8441            .arg(&adrs)
8442            .arg(&own);
8443        unsafe {
8444            b.launch(cfg)?;
8445        }
8446        Ok(())
8447    }
8448
8449    /// Selected-experts batched twin of `qmatvec_nvfp4_fast_prequant_into`: one launch covers
8450    /// every selected expert, weights indexed `sel[t] * expert_stride` into a contiguous
8451    /// per-rank bank, activations advancing `act_row_stride`/`ad_row_stride` elements per
8452    /// selection (0 for a shared input). Per (expert, row) bit-identical to the per-expert
8453    /// kernel — the batching only removes host launch latency.
8454    #[allow(clippy::too_many_arguments)]
8455    pub fn qmatvec_nvfp4_sel_into(
8456        &self,
8457        bank: &CudaSlice<u8>,
8458        sel: &CudaSlice<i32>,
8459        aq: &CudaSlice<i8>,
8460        ad: &CudaSlice<f32>,
8461        y: &mut CudaSlice<f32>,
8462        n_sel: usize,
8463        in_f: usize,
8464        out_f: usize,
8465        row_bytes: usize,
8466        expert_stride: usize,
8467        act_row_stride: usize,
8468        ad_row_stride: usize,
8469    ) -> Result<(), Box<dyn std::error::Error>> {
8470        assert!(
8471            in_f % 64 == 0,
8472            "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
8473        );
8474        if y.len() < n_sel * out_f || sel.len() < n_sel {
8475            return Err(format!(
8476                "NVFP4 sel output {} / sel {} shorter than {n_sel}x{out_f}",
8477                y.len(),
8478                sel.len()
8479            )
8480            .into());
8481        }
8482        // MEMRA_SEL_MR=1: 4-concurrent-row-groups twin — per row bit-identical (same 128-thread
8483        // striding + reduction). MEASURED SLOWER on the 188-SM card (40.8 vs 42.9 tok/s e2e,
8484        // 2026-08-21: 512-thread blocks trade occupancy for launch-tail savings and lose; the
8485        // sequential-rows variant was flat). Default stays the single-row form.
8486        // MEMRA_SEL_STREAM=1: 16-rows-per-block streaming twin with next-row register
8487        // prefetch (bit-identical per row; one group per thread, so in_f <= 4096 only).
8488        static MR: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
8489        let mode = *MR.get_or_init(|| {
8490            if std::env::var("MEMRA_SEL_STREAM").as_deref() == Ok("1") {
8491                2
8492            } else if std::env::var("MEMRA_SEL_MR").as_deref() == Ok("1") {
8493                1
8494            } else {
8495                0
8496            }
8497        });
8498        let mode = if mode == 2 && in_f > 4096 { 0 } else { mode };
8499        let f = match mode {
8500            2 => self.func("qmatvec_nvfp4_dp4a_sel_stream"),
8501            1 => self.func("qmatvec_nvfp4_dp4a_sel_mr4"),
8502            _ => self.func("qmatvec_nvfp4_dp4a_sel"),
8503        };
8504        // Thread-fit block for narrow rows (the DOWN sweep: in_f=640 -> nsb=20 slots left
8505        // 108 of 128 threads idle AND thread-capped resident blocks). blockDim >= nsb keeps
8506        // thread g on slot g; the dropped threads contributed exact 0.0 partials to the
8507        // reduce, so the result bits are unchanged. Applies to the single-row forms only.
8508        let nsb = in_f >> 5;
8509        let fit_block: u32 = if mode == 0 && nsb <= 32 {
8510            32
8511        } else if mode == 1 {
8512            512
8513        } else {
8514            128
8515        };
8516        let cfg = LaunchConfig {
8517            grid_dim: (
8518                match mode {
8519                    2 => (out_f as u32).div_ceil(16),
8520                    1 => (out_f as u32).div_ceil(4),
8521                    _ => out_f as u32,
8522                },
8523                n_sel as u32,
8524                1,
8525            ),
8526            block_dim: (fit_block, 1, 1),
8527            shared_mem_bytes: 0,
8528        };
8529        let (inf, outf, ns) = (in_f as i32, out_f as i32, n_sel as i32);
8530        let (rb, es, ars, adrs) = (
8531            row_bytes as i64,
8532            expert_stride as i64,
8533            act_row_stride as i64,
8534            ad_row_stride as i64,
8535        );
8536        let __s_b = self.gpu.stream();
8537        let mut b = __s_b.launch_builder(&f);
8538        b.arg(bank)
8539            .arg(sel)
8540            .arg(aq)
8541            .arg(ad)
8542            .arg(y)
8543            .arg(&inf)
8544            .arg(&outf)
8545            .arg(&ns)
8546            .arg(&rb)
8547            .arg(&es)
8548            .arg(&ars)
8549            .arg(&adrs);
8550        unsafe {
8551            b.launch(cfg)?;
8552        }
8553        Ok(())
8554    }
8555
8556    /// Selected-experts batched twin of `silu_mul_scaled_q8_1`: [n_sel, n_per] rows, macros
8557    /// from device arrays indexed via sel. Per expert row bit-identical to the scalar kernel.
8558    /// `limit` = the step35 routed SwiGLU clamp (min(silu, limit) * clamp(up, +-limit)); None
8559    /// takes the plain SiLU kernel.
8560    #[allow(clippy::too_many_arguments)]
8561    pub fn silu_mul_scaled_q8_1_sel_into(
8562        &self,
8563        gate: &CudaSlice<f32>,
8564        up: &CudaSlice<f32>,
8565        gmac: &CudaSlice<f32>,
8566        umac: &CudaSlice<f32>,
8567        sel: &CudaSlice<i32>,
8568        limit: Option<f32>,
8569        out_q: &mut CudaSlice<i8>,
8570        out_d: &mut CudaSlice<f32>,
8571        n_per: usize,
8572        n_sel: usize,
8573    ) -> Result<(), Box<dyn std::error::Error>> {
8574        let n = n_per * n_sel;
8575        if n_per % 32 != 0 || out_q.len() < n || out_d.len() < n / 32 {
8576            return Err(format!(
8577                "silu sel geometry n_per={n_per} n_sel={n_sel} q={} d={}",
8578                out_q.len(),
8579                out_d.len()
8580            )
8581            .into());
8582        }
8583        if let Some(limit) = limit {
8584            if limit <= 1e-6 {
8585                return Err(format!(
8586                    "silu sel clamp limit {limit} is at or below the 1e-6 eps gate"
8587                )
8588                .into());
8589            }
8590            let f = self.func("silu_mul_scaled_q8_1_sel_clamp");
8591            let cfg = LaunchConfig::for_num_elems(n as u32);
8592            let (np, ns) = (n_per as i32, n_sel as i32);
8593            let __s_b = self.gpu.stream();
8594            let mut b = __s_b.launch_builder(&f);
8595            b.arg(gate)
8596                .arg(up)
8597                .arg(gmac)
8598                .arg(umac)
8599                .arg(sel)
8600                .arg(&limit)
8601                .arg(out_q)
8602                .arg(out_d)
8603                .arg(&np)
8604                .arg(&ns);
8605            unsafe {
8606                b.launch(cfg)?;
8607            }
8608            return Ok(());
8609        }
8610        let f = self.func("silu_mul_scaled_q8_1_sel");
8611        let cfg = LaunchConfig::for_num_elems(n as u32);
8612        let (np, ns) = (n_per as i32, n_sel as i32);
8613        let __s_b = self.gpu.stream();
8614        let mut b = __s_b.launch_builder(&f);
8615        b.arg(gate)
8616            .arg(up)
8617            .arg(gmac)
8618            .arg(umac)
8619            .arg(sel)
8620            .arg(out_q)
8621            .arg(out_d)
8622            .arg(&np)
8623            .arg(&ns);
8624        unsafe {
8625            b.launch(cfg)?;
8626        }
8627        Ok(())
8628    }
8629
8630    pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8631        Ok(self.gpu.stream().clone_htod(v)?)
8632    }
8633    pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
8634        Ok(self.gpu.stream().clone_htod(v)?)
8635    }
8636    /// i8 upload (moe-devq8-check: synthetic q8_1 activation bytes).
8637    pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
8638        Ok(self.gpu.stream().clone_htod(v)?)
8639    }
8640    pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
8641        Ok(self.gpu.stream().clone_htod(v)?)
8642    }
8643    /// View twin of `dtoh` (lean-logits component 3: D2H one row of a [B, n_vocab] stack).
8644    pub fn dtoh_view(
8645        &self,
8646        d: &cudarc::driver::CudaView<f32>,
8647    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8648        let v = self.gpu.stream().clone_dtoh(d)?;
8649        self.gpu.stream().synchronize()?;
8650        Ok(v)
8651    }
8652    pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8653        let v = self.gpu.stream().clone_dtoh(d)?;
8654        self.gpu.stream().synchronize()?;
8655        Ok(v)
8656    }
8657    /// Queue two f32 device-to-host copies on the compute stream, then establish one host
8658    /// boundary for both. Hy3's CPU/GPU expert split needs the router logits and the MoE input;
8659    /// issuing them together avoids a second stream synchronization in every trunk layer.
8660    pub fn dtoh_pair(
8661        &self,
8662        a: &CudaSlice<f32>,
8663        b: &CudaSlice<f32>,
8664    ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
8665        let av = self.gpu.stream().clone_dtoh(a)?;
8666        let bv = self.gpu.stream().clone_dtoh(b)?;
8667        self.gpu.stream().synchronize()?;
8668        Ok((av, bv))
8669    }
8670    /// View-scoped twin of `dtoh_pair` for reusable capacity buffers whose inactive tail must not
8671    /// cross a shape-sensitive host boundary.
8672    pub fn dtoh_pair_views(
8673        &self,
8674        a: &cudarc::driver::CudaView<f32>,
8675        b: &cudarc::driver::CudaView<f32>,
8676    ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
8677        let av = self.gpu.stream().clone_dtoh(a)?;
8678        let bv = self.gpu.stream().clone_dtoh(b)?;
8679        self.gpu.stream().synchronize()?;
8680        Ok((av, bv))
8681    }
8682    /// Device-to-host copy of an i32 buffer (fused-router sel_idx readback).
8683    pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
8684        let v = self.gpu.stream().clone_dtoh(d)?;
8685        self.gpu.stream().synchronize()?;
8686        Ok(v)
8687    }
8688    /// Device-to-host copy of a u8 buffer (used to read back the quantized KV cache for validation).
8689    pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
8690        let v = self.gpu.stream().clone_dtoh(d)?;
8691        self.gpu.stream().synchronize()?;
8692        Ok(v)
8693    }
8694    pub fn dtoh_u8_view(
8695        &self,
8696        d: &cudarc::driver::CudaView<u8>,
8697    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
8698        let v = self.gpu.stream().clone_dtoh(d)?;
8699        self.gpu.stream().synchronize()?;
8700        Ok(v)
8701    }
8702    pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8703        let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
8704        self.keep_if_capturing(&s);
8705        Ok(s)
8706    }
8707
8708    /// GPU-resident greedy argmax (CUDA-GRAPH-PLAN Phase 1): logits[n_vocab] -> token id in a
8709    /// resident device u32 [1]. PARALLEL 2-pass (RANK1 LEVER): the old single-CTA scan (one 256-thread
8710    /// block on one SM over 248K logits) was memory-starved at ~426us/token. Now pass 1 fans NB=256
8711    /// blocks across the SMs to saturate HBM, pass 2 reduces the NB partials. Bit-identical to host
8712    /// `argmax` (smallest index on tie). The whole point is NOT to dtoh logits — only a [1] u32 is read
8713    /// back (or kept resident for graph replay). Returns the device token buffer.
8714    /// Softmax probability of the (already-argmaxed) token `tok` under `logits` — the spec-decode
8715    /// p-min confidence signal. 2-pass like the parallel argmax; returns a device [1] f32.
8716    pub fn prob_of_token_device(
8717        &self,
8718        logits: &CudaSlice<f32>,
8719        tok: &CudaSlice<u32>,
8720        n_vocab: usize,
8721    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8722        let nb = ARGMAX_NB;
8723        let mut part = self.alloc_uninit::<f32>(nb)?;
8724        let mut p = self.alloc_uninit::<f32>(1)?;
8725        let f1 = self.func("prob_of_token_partial_f32");
8726        let cfg1 = LaunchConfig {
8727            grid_dim: (nb as u32, 1, 1),
8728            block_dim: (256, 1, 1),
8729            shared_mem_bytes: 0,
8730        };
8731        let nv = n_vocab as i32;
8732        let __s_b1 = self.gpu.stream();
8733        let mut b1 = __s_b1.launch_builder(&f1);
8734        b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
8735        unsafe {
8736            b1.launch(cfg1)?;
8737        }
8738        let f2 = self.func("prob_of_token_final_f32");
8739        let cfg2 = LaunchConfig {
8740            grid_dim: (1, 1, 1),
8741            block_dim: (256, 1, 1),
8742            shared_mem_bytes: 0,
8743        };
8744        let nbi = nb as i32;
8745        let __s_b2 = self.gpu.stream();
8746        let mut b2 = __s_b2.launch_builder(&f2);
8747        b2.arg(&part).arg(&mut p).arg(&nbi);
8748        unsafe {
8749            b2.launch(cfg2)?;
8750        }
8751        Ok(p)
8752    }
8753
8754    /// Like `prob_of_token_device` but writes into a PERSISTENT `p_out` buffer (stable pointer).
8755    /// Required for CUDA-graph capture of the draft chain: the captured prob kernels must write
8756    /// where the host reads the p-min confidence between replays. Same kernels, same math.
8757    /// Slot-addressed twin of `prob_of_token_device_into`: token read from `tok_all[tok_idx]`
8758    /// (a view at the slot), probability written to `p_out[p_idx]` — same two kernels, the
8759    /// pointers just land mid-buffer. Zero-sync (gemma confidence-adaptive draft depth).
8760    pub fn prob_of_token_device_col(
8761        &self,
8762        logits: &CudaSlice<f32>,
8763        tok_all: &CudaSlice<u32>,
8764        tok_idx: usize,
8765        p_out: &mut CudaSlice<f32>,
8766        p_idx: usize,
8767        n_vocab: usize,
8768    ) -> Result<(), Box<dyn std::error::Error>> {
8769        let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
8770        let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
8771        let nb = ARGMAX_NB;
8772        let mut part = self.alloc_uninit::<f32>(nb)?;
8773        let f1 = self.func("prob_of_token_partial_f32");
8774        let cfg1 = LaunchConfig {
8775            grid_dim: (nb as u32, 1, 1),
8776            block_dim: (256, 1, 1),
8777            shared_mem_bytes: 0,
8778        };
8779        let nv = n_vocab as i32;
8780        let __s_b1 = self.gpu.stream();
8781        let mut b1 = __s_b1.launch_builder(&f1);
8782        b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
8783        unsafe {
8784            b1.launch(cfg1)?;
8785        }
8786        let f2 = self.func("prob_of_token_final_f32");
8787        let cfg2 = LaunchConfig {
8788            grid_dim: (1, 1, 1),
8789            block_dim: (256, 1, 1),
8790            shared_mem_bytes: 0,
8791        };
8792        let nbi = nb as i32;
8793        let __s_b2 = self.gpu.stream();
8794        let mut b2 = __s_b2.launch_builder(&f2);
8795        b2.arg(&part).arg(&mut p_v).arg(&nbi);
8796        unsafe {
8797            b2.launch(cfg2)?;
8798        }
8799        Ok(())
8800    }
8801
8802    pub fn prob_of_token_device_into(
8803        &self,
8804        logits: &CudaSlice<f32>,
8805        tok: &CudaSlice<u32>,
8806        p_out: &mut CudaSlice<f32>,
8807        n_vocab: usize,
8808    ) -> Result<(), Box<dyn std::error::Error>> {
8809        let nb = ARGMAX_NB;
8810        let mut part = self.alloc_uninit::<f32>(nb)?;
8811        let f1 = self.func("prob_of_token_partial_f32");
8812        let cfg1 = LaunchConfig {
8813            grid_dim: (nb as u32, 1, 1),
8814            block_dim: (256, 1, 1),
8815            shared_mem_bytes: 0,
8816        };
8817        let nv = n_vocab as i32;
8818        let __s_b1 = self.gpu.stream();
8819        let mut b1 = __s_b1.launch_builder(&f1);
8820        b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
8821        unsafe {
8822            b1.launch(cfg1)?;
8823        }
8824        let f2 = self.func("prob_of_token_final_f32");
8825        let cfg2 = LaunchConfig {
8826            grid_dim: (1, 1, 1),
8827            block_dim: (256, 1, 1),
8828            shared_mem_bytes: 0,
8829        };
8830        let nbi = nb as i32;
8831        let __s_b2 = self.gpu.stream();
8832        let mut b2 = __s_b2.launch_builder(&f2);
8833        b2.arg(&part).arg(p_out).arg(&nbi);
8834        unsafe {
8835            b2.launch(cfg2)?;
8836        }
8837        Ok(())
8838    }
8839
8840    /// Token-graph chunk loop: hist[idx] = *tok; idx += 1 — device-indexed history append
8841    /// (graph-constant params, device-varying index). Capture-safe.
8842    pub fn u32_hist_append(
8843        &self,
8844        tok: &CudaSlice<u32>,
8845        hist: &mut CudaSlice<u32>,
8846        idx: &mut CudaSlice<i32>,
8847    ) -> Result<(), Box<dyn std::error::Error>> {
8848        let f = self.func("u32_hist_append");
8849        let cfg = LaunchConfig {
8850            grid_dim: (1, 1, 1),
8851            block_dim: (32, 1, 1),
8852            shared_mem_bytes: 0,
8853        };
8854        let __s_b = self.gpu.stream();
8855        let mut b = __s_b.launch_builder(&f);
8856        b.arg(tok).arg(&mut *hist).arg(&mut *idx);
8857        unsafe {
8858            b.launch(cfg)?;
8859        }
8860        Ok(())
8861    }
8862
8863    pub fn argmax_token_device(
8864        &self,
8865        logits: &CudaSlice<f32>,
8866        n_vocab: usize,
8867    ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
8868        let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
8869        self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
8870        Ok(tok)
8871    }
8872    /// Like `argmax_token_device` but writes into a PERSISTENT `tok` buffer (stable pointer) instead
8873    /// of allocating a fresh one. Required for CUDA-graph capture: the captured argmax must write the
8874    /// next token into the SAME device buffer the next replay's embed_gather reads, so the buffer
8875    /// pointer is baked once and the token id never round-trips to host inside steady state. The
8876    /// pass-1 partials scratch (`argmax_partials`) is also a resident stable-pointer buffer so both
8877    /// captured passes bake fixed addresses.
8878    pub fn argmax_token_device_into(
8879        &self,
8880        logits: &CudaSlice<f32>,
8881        tok: &mut CudaSlice<u32>,
8882        n_vocab: usize,
8883    ) -> Result<(), Box<dyn std::error::Error>> {
8884        let nb = ARGMAX_NB;
8885        let f1 = self.func("argmax_partial_f32");
8886        let f2 = self.func("argmax_final_f32");
8887        let mut guard = self.argmax_partials.lock().unwrap();
8888        if guard.is_none() {
8889            // allocate ONCE; under generate_graph this runs in the tracking-off prime window so the
8890            // buffers carry no cudarc events (illegal inside capture).
8891            let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
8892            let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
8893            *guard = Some((pv, pi));
8894        }
8895        let (part_v, part_i) = guard.as_mut().unwrap();
8896        let nv = n_vocab as i32;
8897        let nbi = nb as i32;
8898        // pass 1: NB blocks x 256 threads grid-stride scan -> per-block (val, idx) partials.
8899        let cfg1 = LaunchConfig {
8900            grid_dim: (nb as u32, 1, 1),
8901            block_dim: (256, 1, 1),
8902            shared_mem_bytes: 0,
8903        };
8904        let __s_b1 = self.gpu.stream();
8905        let mut b1 = __s_b1.launch_builder(&f1);
8906        b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
8907        unsafe {
8908            b1.launch(cfg1)?;
8909        }
8910        // pass 2: one block reduces NB partials -> token_out[0].
8911        let cfg2 = LaunchConfig {
8912            grid_dim: (1, 1, 1),
8913            block_dim: (256, 1, 1),
8914            shared_mem_bytes: 0,
8915        };
8916        let __s_b2 = self.gpu.stream();
8917        let mut b2 = __s_b2.launch_builder(&f2);
8918        b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
8919        unsafe {
8920            b2.launch(cfg2)?;
8921        }
8922        Ok(())
8923    }
8924    /// Column-`col` device argmax over a stacked verify-logits buffer [t, n_vocab] (spec accept
8925    /// walk): toks[out_idx] = argmax(logits[col*n_vocab .. (col+1)*n_vocab]). SAME 2-pass kernels
8926    /// and tie-break contract as `argmax_token_device_into` (bit-identical to host argmax,
8927    /// argmax_gate-validated) — only the input pointer (a column view) and the output slot differ.
8928    /// Lets the accept walk read ONE [t] u32 instead of dtoh'ing the full [t, n_vocab] logits.
8929    pub fn argmax_token_device_col(
8930        &self,
8931        logits: &CudaSlice<f32>,
8932        col: usize,
8933        n_vocab: usize,
8934        toks: &mut CudaSlice<u32>,
8935        out_idx: usize,
8936    ) -> Result<(), Box<dyn std::error::Error>> {
8937        let nb = ARGMAX_NB;
8938        let f1 = self.func("argmax_partial_f32");
8939        let f2 = self.func("argmax_final_f32");
8940        let mut guard = self.argmax_partials.lock().unwrap();
8941        if guard.is_none() {
8942            let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
8943            let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
8944            *guard = Some((pv, pi));
8945        }
8946        let (part_v, part_i) = guard.as_mut().unwrap();
8947        let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
8948        let nv = n_vocab as i32;
8949        let nbi = nb as i32;
8950        let cfg1 = LaunchConfig {
8951            grid_dim: (nb as u32, 1, 1),
8952            block_dim: (256, 1, 1),
8953            shared_mem_bytes: 0,
8954        };
8955        let __s_b1 = self.gpu.stream();
8956        let mut b1 = __s_b1.launch_builder(&f1);
8957        b1.arg(&col_view)
8958            .arg(&mut *part_v)
8959            .arg(&mut *part_i)
8960            .arg(&nv);
8961        unsafe {
8962            b1.launch(cfg1)?;
8963        }
8964        let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
8965        let cfg2 = LaunchConfig {
8966            grid_dim: (1, 1, 1),
8967            block_dim: (256, 1, 1),
8968            shared_mem_bytes: 0,
8969        };
8970        let __s_b2 = self.gpu.stream();
8971        let mut b2 = __s_b2.launch_builder(&f2);
8972        b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
8973        unsafe {
8974            b2.launch(cfg2)?;
8975        }
8976        Ok(())
8977    }
8978    /// Read back a device u32 buffer (the spec accept walk's [t] per-column argmax tokens).
8979    pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
8980        Ok(self.gpu.stream().clone_htod(v)?)
8981    }
8982    pub fn dtoh_u64(&self, d: &CudaSlice<u64>) -> Result<Vec<u64>, Box<dyn std::error::Error>> {
8983        let v = self.gpu.stream().clone_dtoh(d)?;
8984        self.gpu.stream().synchronize()?;
8985        Ok(v)
8986    }
8987
8988    pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
8989        let v = self.gpu.stream().clone_dtoh(d)?;
8990        self.gpu.stream().synchronize()?;
8991        Ok(v)
8992    }
8993    /// Allocate a zeroed device u32 buffer (persistent spec-loop prediction slots).
8994    /// H2D into an EXISTING u32 buffer (stable pointer — the per-step grammar-mask upload:
8995    /// contents change every step, the address must not, so a captured graph can read it).
8996    pub fn htod_u32_into(
8997        &self,
8998        dst: &mut CudaSlice<u32>,
8999        src: &[u32],
9000    ) -> Result<(), Box<dyn std::error::Error>> {
9001        let mut view = dst.slice_mut(0..src.len());
9002        self.gpu.stream().memcpy_htod(src, &mut view)?;
9003        Ok(())
9004    }
9005
9006    /// H2D into an existing i32 buffer. OPTIPIPE uses this to refresh a stage-local saved-len
9007    /// table without changing the device address its reconcile kernel consumes.
9008    pub fn htod_i32_into(
9009        &self,
9010        dst: &mut CudaSlice<i32>,
9011        src: &[i32],
9012    ) -> Result<(), Box<dyn std::error::Error>> {
9013        let mut view = dst.slice_mut(0..src.len());
9014        self.gpu.stream().memcpy_htod(src, &mut view)?;
9015        Ok(())
9016    }
9017
9018    pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
9019        let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
9020        self.keep_if_capturing(&s);
9021        Ok(s)
9022    }
9023    /// embed_gather into a PERSISTENT `x_out` buffer (stable pointer) for CUDA-graph capture (the
9024    /// embed output starts the per-step kernel chain and must be at a fixed address across replays).
9025    pub fn embed_gather_device_into(
9026        &self,
9027        embd: &CudaSlice<u8>,
9028        token_d: &CudaSlice<u32>,
9029        x_out: &mut CudaSlice<f32>,
9030        n_embd: usize,
9031        qtype: i32,
9032        row_bytes: usize,
9033    ) -> Result<(), Box<dyn std::error::Error>> {
9034        let f = self.func("embed_gather_u32");
9035        let cfg = LaunchConfig {
9036            grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
9037            block_dim: (256, 1, 1),
9038            shared_mem_bytes: 0,
9039        };
9040        let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
9041        let __s_b = self.gpu.stream();
9042        let mut b = __s_b.launch_builder(&f);
9043        b.arg(embd)
9044            .arg(token_d)
9045            .arg(x_out)
9046            .arg(&ne)
9047            .arg(&qt)
9048            .arg(&rb);
9049        unsafe {
9050            b.launch(cfg)?;
9051        }
9052        Ok(())
9053    }
9054    /// Read a [1] i32 device counter (pos / seqlen) back to host. Tiny D2H + sync.
9055    pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
9056        let v = self.gpu.stream().clone_dtoh(d)?;
9057        self.gpu.stream().synchronize()?;
9058        Ok(v[0])
9059    }
9060    /// Set a [1] i32 device counter IN PLACE (keeps the buffer pointer stable — required for the
9061    /// graph-resident pos/seqlen counters whose addresses are baked into captured graphs). Restores
9062    /// the counter value after the throwaway capture warmups corrupt it.
9063    /// ASYNC i32 single-slot store (value rides the kernel arg — no host-memory transfer/sync).
9064    /// The graph-arc device-len counters use this; set_i32_one below is the SYNCING pageable
9065    /// copy (fine at stream-idle boundaries, poison mid-round).
9066    pub fn i32_set_k(
9067        &self,
9068        dst: &mut CudaSlice<i32>,
9069        v: i32,
9070    ) -> Result<(), Box<dyn std::error::Error>> {
9071        let f = self.func("i32_set_k");
9072        let cfg = LaunchConfig {
9073            grid_dim: (1, 1, 1),
9074            block_dim: (1, 1, 1),
9075            shared_mem_bytes: 0,
9076        };
9077        let idx = 0i32;
9078        let __s_b = self.gpu.stream();
9079        let mut b = __s_b.launch_builder(&f);
9080        b.arg(dst).arg(&v).arg(&idx);
9081        unsafe {
9082            b.launch(cfg)?;
9083        }
9084        Ok(())
9085    }
9086
9087    pub fn set_i32_one(
9088        &self,
9089        d: &mut CudaSlice<i32>,
9090        v: i32,
9091    ) -> Result<(), Box<dyn std::error::Error>> {
9092        self.gpu.stream().memcpy_htod(&[v], d)?;
9093        Ok(())
9094    }
9095    /// Set a [1] u32 device buffer IN PLACE (stable pointer) — for the resident `token_d` counter
9096    /// during priming / capture-state restore.
9097    pub fn set_u32_one(
9098        &self,
9099        d: &mut CudaSlice<u32>,
9100        v: u32,
9101    ) -> Result<(), Box<dyn std::error::Error>> {
9102        self.gpu.stream().memcpy_htod(&[v], d)?;
9103        Ok(())
9104    }
9105    /// Read back a [1] u32 device buffer (the argmax token). One tiny D2H + sync.
9106    pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
9107        let v = self.gpu.stream().clone_dtoh(d)?;
9108        self.gpu.stream().synchronize()?;
9109        Ok(v[0])
9110    }
9111    /// Upload raw bytes to a resident device u8 buffer (e.g. the embed table for device gather).
9112    pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
9113        Ok(self.gpu.stream().clone_htod(bytes)?)
9114    }
9115    /// Embed-from-device (CUDA-GRAPH-PLAN Phase 1): gather+dequant the row for the token id in
9116    /// `token_d[0]` from the resident embed table `embd` -> x_out[n_embd]. Bit-identical to host
9117    /// EmbedHost::gather (same per-dtype `deq`). No host round-trip of the token id.
9118    pub fn embed_gather_device(
9119        &self,
9120        embd: &CudaSlice<u8>,
9121        token_d: &CudaSlice<u32>,
9122        n_embd: usize,
9123        qtype: i32,
9124        row_bytes: usize,
9125    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9126        let f = self.func("embed_gather_u32");
9127        let mut x = self.alloc_uninit::<f32>(n_embd)?;
9128        let cfg = LaunchConfig {
9129            grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
9130            block_dim: (256, 1, 1),
9131            shared_mem_bytes: 0,
9132        };
9133        let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
9134        let __s_b = self.gpu.stream();
9135        let mut b = __s_b.launch_builder(&f);
9136        b.arg(embd)
9137            .arg(token_d)
9138            .arg(&mut x)
9139            .arg(&ne)
9140            .arg(&qt)
9141            .arg(&rb);
9142        unsafe {
9143            b.launch(cfg)?;
9144        }
9145        Ok(x)
9146    }
9147
9148    /// T-token device embed gather (spec verify/replay): tokens uploaded as a tiny [T] u32 htod,
9149    /// rows dequanted on-device -> x[T, n_embd]. Replaces host per-row dequant + T*n_embd*4B htod
9150    /// (nsys: 84% of spec API time was HtoD). Bit-identical rows (same per-dtype deq).
9151    pub fn embed_gather_device_t(
9152        &self,
9153        embd: &CudaSlice<u8>,
9154        tokens: &[u32],
9155        n_embd: usize,
9156        qtype: i32,
9157        row_bytes: usize,
9158    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9159        let t = tokens.len();
9160        let tok_d = self.gpu.stream().clone_htod(tokens)?;
9161        let f = self.func("embed_gather_u32_t");
9162        let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
9163        let cfg = LaunchConfig {
9164            grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
9165            block_dim: (256, 1, 1),
9166            shared_mem_bytes: 0,
9167        };
9168        let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
9169        let __s_b = self.gpu.stream();
9170        let mut b = __s_b.launch_builder(&f);
9171        b.arg(embd)
9172            .arg(&tok_d)
9173            .arg(&mut x)
9174            .arg(&ne)
9175            .arg(&qt)
9176            .arg(&rb)
9177            .arg(&ti);
9178        unsafe {
9179            b.launch(cfg)?;
9180        }
9181        Ok(x)
9182    }
9183
9184    /// T-token embed gather from a DEVICE token buffer (round-stream stage c: the verify tokens
9185    /// are assembled on-device from the draft-chain pack slots; no host round trip). Same kernel
9186    /// as embed_gather_device_t — bit-identical rows.
9187    /// embed_gather over a token VIEW (spec round: tokens live in the round's batch buffer).
9188    pub fn embed_gather_device_tv(
9189        &self,
9190        embd: &CudaSlice<u8>,
9191        tok_v: &cudarc::driver::CudaView<u32>,
9192        t: usize,
9193        n_embd: usize,
9194        qtype: i32,
9195        row_bytes: usize,
9196    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9197        let f = self.func("embed_gather_u32_t");
9198        let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
9199        let cfg = LaunchConfig {
9200            grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
9201            block_dim: (256, 1, 1),
9202            shared_mem_bytes: 0,
9203        };
9204        let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
9205        let __s_b = self.gpu.stream();
9206        let mut b = __s_b.launch_builder(&f);
9207        b.arg(embd)
9208            .arg(tok_v)
9209            .arg(&mut x)
9210            .arg(&ne)
9211            .arg(&qt)
9212            .arg(&rb)
9213            .arg(&ti);
9214        unsafe {
9215            b.launch(cfg)?;
9216        }
9217        Ok(x)
9218    }
9219
9220    pub fn embed_gather_device_td(
9221        &self,
9222        embd: &CudaSlice<u8>,
9223        tok_d: &CudaSlice<u32>,
9224        t: usize,
9225        n_embd: usize,
9226        qtype: i32,
9227        row_bytes: usize,
9228    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9229        let f = self.func("embed_gather_u32_t");
9230        let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
9231        let cfg = LaunchConfig {
9232            grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
9233            block_dim: (256, 1, 1),
9234            shared_mem_bytes: 0,
9235        };
9236        let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
9237        let __s_b = self.gpu.stream();
9238        let mut b = __s_b.launch_builder(&f);
9239        b.arg(embd)
9240            .arg(tok_d)
9241            .arg(&mut x)
9242            .arg(&ne)
9243            .arg(&qt)
9244            .arg(&rb)
9245            .arg(&ti);
9246        unsafe {
9247            b.launch(cfg)?;
9248        }
9249        Ok(x)
9250    }
9251
9252    /// Uninitialized device buffer — SKIPS the memset that `alloc_zeros` always issues. Decode
9253    /// profile (nsys): ~1050 memsets/token = 6.5% of decode GPU time + ~half the launch count, the
9254    /// dominant contributor to the 19% inter-kernel idle gap and a blocker for clean CUDA-graph
9255    /// capture. Use ONLY for buffers a kernel FULLY overwrites (every element written, no `+=`).
9256    /// SAFETY: caller guarantees the producing kernel writes every element before any read.
9257    #[inline]
9258    /// Keep an allocation alive for the current capture (no-op when retain mode is off).
9259    fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
9260        if self
9261            .capture_keep_on
9262            .load(std::sync::atomic::Ordering::Relaxed)
9263        {
9264            self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
9265        }
9266    }
9267
9268    fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
9269        &self,
9270        n: usize,
9271    ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
9272        let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
9273        // MEMRA_DEBUG_ZERO_ALLOCS=1 (task #14 defect hunt): memset EVERY engine allocation —
9274        // the global uninit-read discriminator (the prime-fn-scoped zeroing experiment could
9275        // not cover engine-internal buffers). Debug-only: massive launch overhead.
9276        {
9277            static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9278            if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
9279                // raw D8 memset (T lacks ValidAsZeroBits in the generic bound)
9280                use cudarc::driver::DevicePtrMut;
9281                let n_bytes = s.len() * std::mem::size_of::<T>();
9282                let stream = self.gpu.stream();
9283                let (p_, _g) = s.device_ptr_mut(&stream);
9284                unsafe {
9285                    cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
9286                        .result()?;
9287                }
9288            }
9289        }
9290        self.keep_if_capturing(&s);
9291        Ok(s)
9292    }
9293
9294    /// Public f32 uninitialized scratch (see `alloc_uninit`). For decode/forward scratch a kernel
9295    /// fully overwrites. SAFETY: producing kernel must write every element before any read.
9296    /// Uninitialized q8_1 activation pair (int8 + per-32 scales) — the fa combine q8-emit
9297    /// consumers alloc through this (m=1 decode arms).
9298    pub fn uninit_q8_pair(
9299        &self,
9300        n: usize,
9301    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9302        Ok((
9303            self.alloc_uninit::<i8>(n)?,
9304            self.alloc_uninit::<f32>(n / 32)?,
9305        ))
9306    }
9307
9308    pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9309        self.alloc_uninit::<f32>(n)
9310    }
9311
9312    /// i8 uninitialized scratch (same contract as `uninit`).
9313    pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
9314        self.alloc_uninit::<i8>(n)
9315    }
9316
9317    /// RMSNorm: x[ncols,nrows] row-major, weight[ncols] -> dst. One block/row, 256 threads.
9318    /// gemma4: 3 rms_norms of the SAME input in one launch (one reduction, three weights).
9319    /// Per-output bit-identical to three rms_norm calls (verbatim reduction/scale chain).
9320    #[allow(clippy::too_many_arguments)]
9321    pub fn rms_norm3(
9322        &self,
9323        x: &CudaSlice<f32>,
9324        w0: &CudaSlice<f32>,
9325        w1: &CudaSlice<f32>,
9326        w2: &CudaSlice<f32>,
9327        d0: &mut CudaSlice<f32>,
9328        d1: &mut CudaSlice<f32>,
9329        d2: &mut CudaSlice<f32>,
9330        ncols: usize,
9331        nrows: usize,
9332        eps: f32,
9333    ) -> Result<(), Box<dyn std::error::Error>> {
9334        let f = self.func("rms_norm3_f32");
9335        let cfg = LaunchConfig {
9336            grid_dim: (nrows as u32, 1, 1),
9337            block_dim: (rms_block(), 1, 1),
9338            shared_mem_bytes: 0,
9339        };
9340        let (nc, e) = (ncols as i32, eps);
9341        let __s_b = self.gpu.stream();
9342        let mut b = __s_b.launch_builder(&f);
9343        b.arg(x)
9344            .arg(w0)
9345            .arg(w1)
9346            .arg(w2)
9347            .arg(d0)
9348            .arg(d1)
9349            .arg(d2)
9350            .arg(&nc)
9351            .arg(&e);
9352        unsafe {
9353            b.launch(cfg)?;
9354        }
9355        Ok(())
9356    }
9357
9358    /// gemma4 fused q/k/v head norms (one launch, per-row rms_norm_f32-verbatim).
9359    #[allow(clippy::too_many_arguments)]
9360    /// True when the warp-per-row qkv norm would engage for (rows, ncols) — the emit lane
9361    /// piggybacks on the same conditions.
9362    pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
9363        static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9364        *WARP_ON.get_or_init(|| {
9365            std::env::var("MEMRA_QKVNORM_W")
9366                .map(|v| v != "0")
9367                .unwrap_or(true)
9368        }) && ncols % 4 == 0
9369            && rows >= 64
9370    }
9371
9372    /// w4 norm with bf16 V EMIT (31B glue lane): the v segment also writes its normed rows as
9373    /// bf16 (the FA V operand — bit-identical to a post-hoc f32_to_bf16). Prefill-depth only.
9374    #[allow(clippy::too_many_arguments)]
9375    pub fn rms_norm_qkv_w4b(
9376        &self,
9377        q: &CudaSlice<f32>,
9378        k: &CudaSlice<f32>,
9379        v: &CudaSlice<f32>,
9380        wq: &CudaSlice<f32>,
9381        wk: &CudaSlice<f32>,
9382        wv: &CudaSlice<f32>,
9383        dq: &mut CudaSlice<f32>,
9384        dk: &mut CudaSlice<f32>,
9385        dv: &mut CudaSlice<f32>,
9386        dvb: &mut CudaSlice<u8>,
9387        ncols: usize,
9388        rq: usize,
9389        rk: usize,
9390        eps: f32,
9391        vf16: bool,
9392    ) -> Result<(), Box<dyn std::error::Error>> {
9393        assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
9394        let f = self.func("rms_norm_qkv_w4b_f32");
9395        let rows = (rq + 2 * rk) as u32;
9396        let cfg = LaunchConfig {
9397            grid_dim: (rows.div_ceil(8), 1, 1),
9398            block_dim: (256, 1, 1),
9399            shared_mem_bytes: 0,
9400        };
9401        let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
9402        let vf = vf16 as i32;
9403        let __s_b = self.gpu.stream();
9404        let mut b = __s_b.launch_builder(&f);
9405        b.arg(q)
9406            .arg(k)
9407            .arg(v)
9408            .arg(wq)
9409            .arg(wk)
9410            .arg(wv)
9411            .arg(dq)
9412            .arg(dk)
9413            .arg(dv)
9414            .arg(&mut *dvb)
9415            .arg(&nc)
9416            .arg(&rqi)
9417            .arg(&rki)
9418            .arg(&rvi)
9419            .arg(&e)
9420            .arg(&vf);
9421        unsafe {
9422            b.launch(cfg)?;
9423        }
9424        Ok(())
9425    }
9426
9427    pub fn rms_norm_qkv(
9428        &self,
9429        q: &CudaSlice<f32>,
9430        k: &CudaSlice<f32>,
9431        v: &CudaSlice<f32>,
9432        wq: &CudaSlice<f32>,
9433        wk: &CudaSlice<f32>,
9434        wv: &CudaSlice<f32>,
9435        dq: &mut CudaSlice<f32>,
9436        dk: &mut CudaSlice<f32>,
9437        dv: &mut CudaSlice<f32>,
9438        ncols: usize,
9439        rq: usize,
9440        rk: usize,
9441        eps: f32,
9442    ) -> Result<(), Box<dyn std::error::Error>> {
9443        // Warp-per-row float4 twin (default; MEMRA_QKVNORM_W=0 reverts): the block-per-row form
9444        // spends 767us/launch on 17k+ 2KB rows at prefill depth (launch/reduce latency-bound,
9445        // ~92GB/s). Own numeric config (reduce order differs) — battery-gated.
9446        static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9447        let warp_on = *WARP_ON.get_or_init(|| {
9448            std::env::var("MEMRA_QKVNORM_W")
9449                .map(|v| v != "0")
9450                .unwrap_or(true)
9451        });
9452        // rows >= 64 keeps decode (nh + 2*nkv rows) on the block-tree kernel — decode/verify/
9453        // replay numerics are untouched on every model; only prefill depth takes the new config.
9454        if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
9455            let f = self.func("rms_norm_qkv_w4_f32");
9456            let rows = (rq + 2 * rk) as u32;
9457            let cfg = LaunchConfig {
9458                grid_dim: (rows.div_ceil(8), 1, 1),
9459                block_dim: (256, 1, 1),
9460                shared_mem_bytes: 0,
9461            };
9462            let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
9463            let __s_b = self.gpu.stream();
9464            let mut b = __s_b.launch_builder(&f);
9465            b.arg(q)
9466                .arg(k)
9467                .arg(v)
9468                .arg(wq)
9469                .arg(wk)
9470                .arg(wv)
9471                .arg(dq)
9472                .arg(dk)
9473                .arg(dv)
9474                .arg(&nc)
9475                .arg(&rqi)
9476                .arg(&rki)
9477                .arg(&rvi)
9478                .arg(&e);
9479            unsafe {
9480                b.launch(cfg)?;
9481            }
9482            return Ok(());
9483        }
9484        let f = self.func("rms_norm_qkv_f32");
9485        let grid = (rq + 2 * rk) as u32;
9486        let cfg = LaunchConfig {
9487            grid_dim: (grid, 1, 1),
9488            block_dim: (rms_block(), 1, 1),
9489            shared_mem_bytes: 0,
9490        };
9491        let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
9492        let __s_b = self.gpu.stream();
9493        let mut b = __s_b.launch_builder(&f);
9494        b.arg(q)
9495            .arg(k)
9496            .arg(v)
9497            .arg(wq)
9498            .arg(wk)
9499            .arg(wv)
9500            .arg(dq)
9501            .arg(dk)
9502            .arg(dv)
9503            .arg(&nc)
9504            .arg(&rqi)
9505            .arg(&rki)
9506            .arg(&e);
9507        unsafe {
9508            b.launch(cfg)?;
9509        }
9510        Ok(())
9511    }
9512
9513    /// gemma4 fused pair of rms_norms over two different inputs (same width).
9514    #[allow(clippy::too_many_arguments)]
9515    pub fn rms_norm2x(
9516        &self,
9517        a: &CudaSlice<f32>,
9518        bb: &CudaSlice<f32>,
9519        wa: &CudaSlice<f32>,
9520        wb: &CudaSlice<f32>,
9521        da: &mut CudaSlice<f32>,
9522        db: &mut CudaSlice<f32>,
9523        ncols: usize,
9524        nrows: usize,
9525        eps: f32,
9526    ) -> Result<(), Box<dyn std::error::Error>> {
9527        let f = self.func("rms_norm2x_f32");
9528        let cfg = LaunchConfig {
9529            grid_dim: (2 * nrows as u32, 1, 1),
9530            block_dim: (rms_block(), 1, 1),
9531            shared_mem_bytes: 0,
9532        };
9533        let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9534        let __s_b = self.gpu.stream();
9535        let mut b = __s_b.launch_builder(&f);
9536        b.arg(a)
9537            .arg(bb)
9538            .arg(wa)
9539            .arg(wb)
9540            .arg(da)
9541            .arg(db)
9542            .arg(&nc)
9543            .arg(&nr)
9544            .arg(&e);
9545        unsafe {
9546            b.launch(cfg)?;
9547        }
9548        Ok(())
9549    }
9550
9551    /// gemma4 R4: in-place final-logit softcap y = cap*tanh(y/cap).
9552    pub fn softcap(
9553        &self,
9554        y: &mut CudaSlice<f32>,
9555        cap: f32,
9556        n: usize,
9557    ) -> Result<(), Box<dyn std::error::Error>> {
9558        let f = self.func("softcap_f32");
9559        let cfg = LaunchConfig::for_num_elems(n as u32);
9560        let ni = n as i32;
9561        let __s_b = self.gpu.stream();
9562        let mut b = __s_b.launch_builder(&f);
9563        b.arg(y).arg(&cap).arg(&ni);
9564        unsafe {
9565            b.launch(cfg)?;
9566        }
9567        Ok(())
9568    }
9569
9570    /// gemma4 suppress-token mask: y[row][ids[j]] = -inf over t logits rows (fixed-arg launch —
9571    /// graph-capture safe; NOT monotonic like softcap, so it must run before any argmax).
9572    pub fn mask_ids_rows(
9573        &self,
9574        y: &mut CudaSlice<f32>,
9575        ids: &CudaSlice<i32>,
9576        n_ids: usize,
9577        n_vocab: usize,
9578        t: usize,
9579    ) -> Result<(), Box<dyn std::error::Error>> {
9580        let f = self.func("mask_ids_rows_f32");
9581        let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
9582        let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
9583        let __s_b = self.gpu.stream();
9584        let mut b = __s_b.launch_builder(&f);
9585        b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
9586        unsafe {
9587            b.launch(cfg)?;
9588        }
9589        Ok(())
9590    }
9591
9592    /// gemma4: res = (a+b)*c AND dst = rms_norm(res, w) in one launch.
9593    #[allow(clippy::too_many_arguments)]
9594    pub fn add_scale_rms_norm(
9595        &self,
9596        a: &CudaSlice<f32>,
9597        b_in: &CudaSlice<f32>,
9598        c: f32,
9599        w: &CudaSlice<f32>,
9600        res: &mut CudaSlice<f32>,
9601        dst: &mut CudaSlice<f32>,
9602        ncols: usize,
9603        nrows: usize,
9604        eps: f32,
9605    ) -> Result<(), Box<dyn std::error::Error>> {
9606        let f = self.func("add_scale_rms_norm_f32");
9607        let cfg = LaunchConfig {
9608            grid_dim: (nrows as u32, 1, 1),
9609            block_dim: (rms_block(), 1, 1),
9610            shared_mem_bytes: 0,
9611        };
9612        let (nc, e2) = (ncols as i32, eps);
9613        let __s_b = self.gpu.stream();
9614        let mut b = __s_b.launch_builder(&f);
9615        b.arg(a)
9616            .arg(b_in)
9617            .arg(&c)
9618            .arg(w)
9619            .arg(res)
9620            .arg(dst)
9621            .arg(&nc)
9622            .arg(&e2);
9623        unsafe {
9624            b.launch(cfg)?;
9625        }
9626        Ok(())
9627    }
9628
9629    /// gemma4: res = (a+b)*c AND the next layer's attn_norm EMITTED q8_1 in one launch.
9630    /// Quantize epilogue bit-identical to quantize_q8_1 (the rms_norm_q8_1 form).
9631    #[allow(clippy::too_many_arguments)]
9632    pub fn add_scale_rms_norm_q8_1(
9633        &self,
9634        a: &CudaSlice<f32>,
9635        b_in: &CudaSlice<f32>,
9636        c: f32,
9637        w: &CudaSlice<f32>,
9638        res: &mut CudaSlice<f32>,
9639        ncols: usize,
9640        nrows: usize,
9641        eps: f32,
9642    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9643        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9644        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9645        let (nc, e2) = (ncols as i32, eps);
9646        if Self::pdl_on() && Self::pdl_wb_on() {
9647            {
9648                use cudarc::driver::{DevicePtr, DevicePtrMut};
9649                let s = &self.gpu.stream();
9650                let (pa, _g0) = a.device_ptr(s);
9651                let (pb, _g1) = b_in.device_ptr(s);
9652                let (pw, _g2) = w.device_ptr(s);
9653                let (pr, _g3) = res.device_ptr_mut(s);
9654                let (pq, _g4) = out_q.device_ptr_mut(s);
9655                let (pd, _g5) = out_d.device_ptr_mut(s);
9656                let mut ps = [
9657                    &pa as *const _ as *mut std::ffi::c_void,
9658                    &pb as *const _ as *mut _,
9659                    &c as *const _ as *mut _,
9660                    &pw as *const _ as *mut _,
9661                    &pr as *const _ as *mut _,
9662                    &pq as *const _ as *mut _,
9663                    &pd as *const _ as *mut _,
9664                    &nc as *const _ as *mut _,
9665                    &e2 as *const _ as *mut _,
9666                ];
9667                unsafe {
9668                    self.launch_pdl(
9669                        "add_scale_rms_norm_q8_1",
9670                        (nrows as u32, 1, 1),
9671                        (rms_block(), 1, 1),
9672                        &mut ps,
9673                    )?;
9674                }
9675            }
9676            return Ok((out_q, out_d));
9677        }
9678        let f = self.func("add_scale_rms_norm_q8_1");
9679        let cfg = LaunchConfig {
9680            grid_dim: (nrows as u32, 1, 1),
9681            block_dim: (rms_block(), 1, 1),
9682            shared_mem_bytes: 0,
9683        };
9684        let __s_b = self.gpu.stream();
9685        let mut b = __s_b.launch_builder(&f);
9686        b.arg(a)
9687            .arg(b_in)
9688            .arg(&c)
9689            .arg(w)
9690            .arg(res)
9691            .arg(&mut out_q)
9692            .arg(&mut out_d)
9693            .arg(&nc)
9694            .arg(&e2);
9695        unsafe {
9696            b.launch(cfg)?;
9697        }
9698        Ok((out_q, out_d))
9699    }
9700
9701    /// Slot-fed add_scale_rms_norm_q8_1 twin (alloc-free capture lane).
9702    #[allow(clippy::too_many_arguments)]
9703    pub fn add_scale_rms_norm_q8_1_into(
9704        &self,
9705        a: &CudaSlice<f32>,
9706        b_in: &CudaSlice<f32>,
9707        c: f32,
9708        w: &CudaSlice<f32>,
9709        res: &mut CudaSlice<f32>,
9710        ncols: usize,
9711        nrows: usize,
9712        eps: f32,
9713        out_q: &mut CudaSlice<i8>,
9714        out_d: &mut CudaSlice<f32>,
9715    ) -> Result<(), Box<dyn std::error::Error>> {
9716        debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
9717        let (nc, e2) = (ncols as i32, eps);
9718        if Self::pdl_on() && Self::pdl_wb_on() {
9719            use cudarc::driver::{DevicePtr, DevicePtrMut};
9720            let s = &self.gpu.stream();
9721            let (pa, _g0) = a.device_ptr(s);
9722            let (pb, _g1) = b_in.device_ptr(s);
9723            let (pw, _g2) = w.device_ptr(s);
9724            let (pr, _g3) = res.device_ptr_mut(s);
9725            let (pq, _g4) = out_q.device_ptr_mut(s);
9726            let (pd, _g5) = out_d.device_ptr_mut(s);
9727            let mut ps = [
9728                &pa as *const _ as *mut std::ffi::c_void,
9729                &pb as *const _ as *mut _,
9730                &c as *const _ as *mut _,
9731                &pw as *const _ as *mut _,
9732                &pr as *const _ as *mut _,
9733                &pq as *const _ as *mut _,
9734                &pd as *const _ as *mut _,
9735                &nc as *const _ as *mut _,
9736                &e2 as *const _ as *mut _,
9737            ];
9738            unsafe {
9739                self.launch_pdl(
9740                    "add_scale_rms_norm_q8_1",
9741                    (nrows as u32, 1, 1),
9742                    (rms_block(), 1, 1),
9743                    &mut ps,
9744                )?;
9745            }
9746            return Ok(());
9747        }
9748        let f = self.func("add_scale_rms_norm_q8_1");
9749        let cfg = LaunchConfig {
9750            grid_dim: (nrows as u32, 1, 1),
9751            block_dim: (rms_block(), 1, 1),
9752            shared_mem_bytes: 0,
9753        };
9754        let __s_b = self.gpu.stream();
9755        let mut b = __s_b.launch_builder(&f);
9756        b.arg(a)
9757            .arg(b_in)
9758            .arg(&c)
9759            .arg(w)
9760            .arg(res)
9761            .arg(&mut *out_q)
9762            .arg(&mut *out_d)
9763            .arg(&nc)
9764            .arg(&e2);
9765        unsafe {
9766            b.launch(cfg)?;
9767        }
9768        Ok(())
9769    }
9770
9771    /// E4B glue fusion: rms(a, wa) prologue + the add_scale_rms_norm_q8_1 program — one launch
9772    /// replaces the per-layer rms_norm_f32(y) + emit pair in the PLE tail.
9773    #[allow(clippy::too_many_arguments)]
9774    pub fn rms_pre_add_scale_rms_norm_q8_1(
9775        &self,
9776        a: &CudaSlice<f32>,
9777        wa: &CudaSlice<f32>,
9778        b_in: &CudaSlice<f32>,
9779        c: f32,
9780        w: &CudaSlice<f32>,
9781        res: &mut CudaSlice<f32>,
9782        ncols: usize,
9783        nrows: usize,
9784        eps: f32,
9785    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9786        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9787        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9788        let (nc, e2) = (ncols as i32, eps);
9789        if Self::pdl_on() {
9790            {
9791                use cudarc::driver::{DevicePtr, DevicePtrMut};
9792                let s = &self.gpu.stream();
9793                let (pa, _g0) = a.device_ptr(s);
9794                let (pwa, _g1) = wa.device_ptr(s);
9795                let (pb, _g2) = b_in.device_ptr(s);
9796                let (pw, _g3) = w.device_ptr(s);
9797                let (pr, _g4) = res.device_ptr_mut(s);
9798                let (pq, _g5) = out_q.device_ptr_mut(s);
9799                let (pd, _g6) = out_d.device_ptr_mut(s);
9800                let mut ps = [
9801                    &pa as *const _ as *mut std::ffi::c_void,
9802                    &pwa as *const _ as *mut _,
9803                    &pb as *const _ as *mut _,
9804                    &c as *const _ as *mut _,
9805                    &pw as *const _ as *mut _,
9806                    &pr as *const _ as *mut _,
9807                    &pq as *const _ as *mut _,
9808                    &pd as *const _ as *mut _,
9809                    &nc as *const _ as *mut _,
9810                    &e2 as *const _ as *mut _,
9811                ];
9812                unsafe {
9813                    self.launch_pdl(
9814                        "rms_pre_add_scale_rms_norm_q8_1",
9815                        (nrows as u32, 1, 1),
9816                        (rms_block(), 1, 1),
9817                        &mut ps,
9818                    )?;
9819                }
9820            }
9821            return Ok((out_q, out_d));
9822        }
9823        let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
9824        let cfg = LaunchConfig {
9825            grid_dim: (nrows as u32, 1, 1),
9826            block_dim: (rms_block(), 1, 1),
9827            shared_mem_bytes: 0,
9828        };
9829        let __s_b = self.gpu.stream();
9830        let mut b = __s_b.launch_builder(&f);
9831        b.arg(a)
9832            .arg(wa)
9833            .arg(b_in)
9834            .arg(&c)
9835            .arg(w)
9836            .arg(res)
9837            .arg(&mut out_q)
9838            .arg(&mut out_d)
9839            .arg(&nc)
9840            .arg(&e2);
9841        unsafe {
9842            b.launch(cfg)?;
9843        }
9844        Ok((out_q, out_d))
9845    }
9846
9847    /// GELU(tanh)*up with the activation emitted q8_1 alongside f32 (glue-fusion lane): the
9848    /// consumer matmul rides matmul_pre, killing its standalone quantize_q8_1 launch.
9849    pub fn gelu_tanh_mul_q8_1(
9850        &self,
9851        gate: &CudaSlice<f32>,
9852        up: &cudarc::driver::CudaView<f32>,
9853        act: &mut CudaSlice<f32>,
9854        ncols: usize,
9855        nrows: usize,
9856    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9857        debug_assert!(ncols % 128 == 0);
9858        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9859        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9860        let nc = ncols as i32;
9861        if Self::pdl_on() {
9862            {
9863                use cudarc::driver::{DevicePtr, DevicePtrMut};
9864                let s = &self.gpu.stream();
9865                let (pg, _g0) = gate.device_ptr(s);
9866                let (pu, _g1) = up.device_ptr(s);
9867                let (pact, _g2) = act.device_ptr_mut(s);
9868                let (pq, _g3) = out_q.device_ptr_mut(s);
9869                let (pd, _g4) = out_d.device_ptr_mut(s);
9870                let mut ps = [
9871                    &pg as *const _ as *mut std::ffi::c_void,
9872                    &pu as *const _ as *mut _,
9873                    &pact as *const _ as *mut _,
9874                    &pq as *const _ as *mut _,
9875                    &pd as *const _ as *mut _,
9876                    &nc as *const _ as *mut _,
9877                ];
9878                unsafe {
9879                    self.launch_pdl(
9880                        "gelu_tanh_mul_q8_1",
9881                        (nrows as u32, 1, 1),
9882                        (rms_block(), 1, 1),
9883                        &mut ps,
9884                    )?;
9885                }
9886            }
9887            return Ok((out_q, out_d));
9888        }
9889        let f = self.func("gelu_tanh_mul_q8_1");
9890        let cfg = LaunchConfig {
9891            grid_dim: (nrows as u32, 1, 1),
9892            block_dim: (rms_block(), 1, 1),
9893            shared_mem_bytes: 0,
9894        };
9895        let __s_b = self.gpu.stream();
9896        let mut b = __s_b.launch_builder(&f);
9897        b.arg(gate)
9898            .arg(up)
9899            .arg(act)
9900            .arg(&mut out_q)
9901            .arg(&mut out_d)
9902            .arg(&nc);
9903        unsafe {
9904            b.launch(cfg)?;
9905        }
9906        Ok((out_q, out_d))
9907    }
9908
9909    /// Slot-fed gelu_tanh_mul_q8_1 twin (alloc-free capture lane; incl. the PDL arm).
9910    #[allow(clippy::too_many_arguments)]
9911    pub fn gelu_tanh_mul_q8_1_into(
9912        &self,
9913        gate: &CudaSlice<f32>,
9914        up: &cudarc::driver::CudaView<f32>,
9915        act: &mut CudaSlice<f32>,
9916        ncols: usize,
9917        nrows: usize,
9918        out_q: &mut CudaSlice<i8>,
9919        out_d: &mut CudaSlice<f32>,
9920    ) -> Result<(), Box<dyn std::error::Error>> {
9921        debug_assert!(ncols % 128 == 0);
9922        debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
9923        let nc = ncols as i32;
9924        if Self::pdl_on() {
9925            use cudarc::driver::{DevicePtr, DevicePtrMut};
9926            let s = &self.gpu.stream();
9927            let (pg, _g0) = gate.device_ptr(s);
9928            let (pu, _g1) = up.device_ptr(s);
9929            let (pact, _g2) = act.device_ptr_mut(s);
9930            let (pq, _g3) = out_q.device_ptr_mut(s);
9931            let (pd, _g4) = out_d.device_ptr_mut(s);
9932            let mut ps = [
9933                &pg as *const _ as *mut std::ffi::c_void,
9934                &pu as *const _ as *mut _,
9935                &pact as *const _ as *mut _,
9936                &pq as *const _ as *mut _,
9937                &pd as *const _ as *mut _,
9938                &nc as *const _ as *mut _,
9939            ];
9940            unsafe {
9941                self.launch_pdl(
9942                    "gelu_tanh_mul_q8_1",
9943                    (nrows as u32, 1, 1),
9944                    (rms_block(), 1, 1),
9945                    &mut ps,
9946                )?;
9947            }
9948            return Ok(());
9949        }
9950        let f = self.func("gelu_tanh_mul_q8_1");
9951        let cfg = LaunchConfig {
9952            grid_dim: (nrows as u32, 1, 1),
9953            block_dim: (rms_block(), 1, 1),
9954            shared_mem_bytes: 0,
9955        };
9956        let __s_b = self.gpu.stream();
9957        let mut b = __s_b.launch_builder(&f);
9958        b.arg(gate)
9959            .arg(up)
9960            .arg(&mut *act)
9961            .arg(&mut *out_q)
9962            .arg(&mut *out_d)
9963            .arg(&nc);
9964        unsafe {
9965            b.launch(cfg)?;
9966        }
9967        Ok(())
9968    }
9969
9970    /// gemma4: add + rms_norm3 with outputs 0/2 emitted q8_1 (zsh + moe_in) and 1 f32 (router).
9971    #[allow(clippy::too_many_arguments)]
9972    pub fn add_rms_norm3_q8z(
9973        &self,
9974        a: &CudaSlice<f32>,
9975        b_in: &CudaSlice<f32>,
9976        w0: &CudaSlice<f32>,
9977        w1: &CudaSlice<f32>,
9978        w2: &CudaSlice<f32>,
9979        res: &mut CudaSlice<f32>,
9980        out1: &mut CudaSlice<f32>,
9981        ncols: usize,
9982        nrows: usize,
9983        eps: f32,
9984    ) -> Result<
9985        (
9986            (CudaSlice<i8>, CudaSlice<f32>),
9987            (CudaSlice<i8>, CudaSlice<f32>),
9988        ),
9989        Box<dyn std::error::Error>,
9990    > {
9991        let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
9992        let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9993        let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
9994        let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9995        let f = self.func("add_rms_norm3_q8z_f32");
9996        let cfg = LaunchConfig {
9997            grid_dim: (nrows as u32, 1, 1),
9998            block_dim: (rms_block(), 1, 1),
9999            shared_mem_bytes: 0,
10000        };
10001        let (nc, e2) = (ncols as i32, eps);
10002        let __s_b = self.gpu.stream();
10003        let mut b = __s_b.launch_builder(&f);
10004        b.arg(a)
10005            .arg(b_in)
10006            .arg(w0)
10007            .arg(w1)
10008            .arg(w2)
10009            .arg(res)
10010            .arg(&mut q0)
10011            .arg(&mut d0)
10012            .arg(out1)
10013            .arg(&mut q2)
10014            .arg(&mut d2)
10015            .arg(&nc)
10016            .arg(&e2);
10017        unsafe {
10018            b.launch(cfg)?;
10019        }
10020        Ok(((q0, d0), (q2, d2)))
10021    }
10022
10023    /// gemma4: res = a+b AND the three rms_norms of res in one launch.
10024    #[allow(clippy::too_many_arguments)]
10025    pub fn add_rms_norm3(
10026        &self,
10027        a: &CudaSlice<f32>,
10028        b_in: &CudaSlice<f32>,
10029        w0: &CudaSlice<f32>,
10030        w1: &CudaSlice<f32>,
10031        w2: &CudaSlice<f32>,
10032        res: &mut CudaSlice<f32>,
10033        d0: &mut CudaSlice<f32>,
10034        d1: &mut CudaSlice<f32>,
10035        d2: &mut CudaSlice<f32>,
10036        ncols: usize,
10037        nrows: usize,
10038        eps: f32,
10039    ) -> Result<(), Box<dyn std::error::Error>> {
10040        let f = self.func("add_rms_norm3_f32");
10041        let cfg = LaunchConfig {
10042            grid_dim: (nrows as u32, 1, 1),
10043            block_dim: (rms_block(), 1, 1),
10044            shared_mem_bytes: 0,
10045        };
10046        let (nc, e2) = (ncols as i32, eps);
10047        let __s_b = self.gpu.stream();
10048        let mut b = __s_b.launch_builder(&f);
10049        b.arg(a)
10050            .arg(b_in)
10051            .arg(w0)
10052            .arg(w1)
10053            .arg(w2)
10054            .arg(res)
10055            .arg(d0)
10056            .arg(d1)
10057            .arg(d2)
10058            .arg(&nc)
10059            .arg(&e2);
10060        unsafe {
10061            b.launch(cfg)?;
10062        }
10063        Ok(())
10064    }
10065
10066    /// dst = (a + b) * c (residual add + layer scale, one launch).
10067    pub fn add_scale(
10068        &self,
10069        a: &CudaSlice<f32>,
10070        b_in: &CudaSlice<f32>,
10071        c: f32,
10072        dst: &mut CudaSlice<f32>,
10073        n: usize,
10074    ) -> Result<(), Box<dyn std::error::Error>> {
10075        let f = self.func("add_scale_f32");
10076        let cfg = LaunchConfig::for_num_elems(n as u32);
10077        let ni = n as i32;
10078        let __s_b = self.gpu.stream();
10079        let mut b = __s_b.launch_builder(&f);
10080        b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
10081        unsafe {
10082            b.launch(cfg)?;
10083        }
10084        Ok(())
10085    }
10086
10087    /// Vision-tower LayerNorm (with bias) over [nrows, ncols] — lane/vision.
10088    pub fn layer_norm_bias(
10089        &self,
10090        x: &CudaSlice<f32>,
10091        w: &CudaSlice<f32>,
10092        b: &CudaSlice<f32>,
10093        dst: &mut CudaSlice<f32>,
10094        ncols: usize,
10095        nrows: usize,
10096        eps: f32,
10097    ) -> Result<(), Box<dyn std::error::Error>> {
10098        let f = self.func("layer_norm_bias_f32");
10099        let (nc, e) = (ncols as i32, eps);
10100        let cfg = LaunchConfig {
10101            grid_dim: (nrows as u32, 1, 1),
10102            block_dim: (256, 1, 1),
10103            shared_mem_bytes: 0,
10104        };
10105        let __s_b = self.gpu.stream();
10106        let mut lb = __s_b.launch_builder(&f);
10107        lb.arg(x).arg(w).arg(b).arg(&mut *dst).arg(&nc).arg(&e);
10108        unsafe {
10109            lb.launch(cfg)?;
10110        }
10111        Ok(())
10112    }
10113
10114    /// gelu_pytorch_tanh elementwise (vision tower MLP activation).
10115    pub fn gelu_tanh(
10116        &self,
10117        x: &CudaSlice<f32>,
10118        dst: &mut CudaSlice<f32>,
10119        n: usize,
10120    ) -> Result<(), Box<dyn std::error::Error>> {
10121        let f = self.func("gelu_tanh_f32");
10122        let ni = n as i64;
10123        let cfg = LaunchConfig {
10124            grid_dim: (n.div_ceil(256) as u32, 1, 1),
10125            block_dim: (256, 1, 1),
10126            shared_mem_bytes: 0,
10127        };
10128        let __s_b = self.gpu.stream();
10129        let mut lb = __s_b.launch_builder(&f);
10130        lb.arg(x).arg(&mut *dst).arg(&ni);
10131        unsafe {
10132            lb.launch(cfg)?;
10133        }
10134        Ok(())
10135    }
10136
10137    /// In-place row softmax over [nrows, ncols] (bidirectional vision attention).
10138    pub fn row_softmax(
10139        &self,
10140        x: &mut CudaSlice<f32>,
10141        ncols: usize,
10142        nrows: usize,
10143    ) -> Result<(), Box<dyn std::error::Error>> {
10144        let f = self.func("row_softmax_f32");
10145        let nc = ncols as i32;
10146        let cfg = LaunchConfig {
10147            grid_dim: (nrows as u32, 1, 1),
10148            block_dim: (256, 1, 1),
10149            shared_mem_bytes: 0,
10150        };
10151        let __s_b = self.gpu.stream();
10152        let mut lb = __s_b.launch_builder(&f);
10153        lb.arg(&mut *x).arg(&nc);
10154        unsafe {
10155            lb.launch(cfg)?;
10156        }
10157        Ok(())
10158    }
10159
10160    pub fn rms_norm(
10161        &self,
10162        x: &CudaSlice<f32>,
10163        w: &CudaSlice<f32>,
10164        dst: &mut CudaSlice<f32>,
10165        ncols: usize,
10166        nrows: usize,
10167        eps: f32,
10168    ) -> Result<(), Box<dyn std::error::Error>> {
10169        let (nc, e) = (ncols as i32, eps);
10170        let kname = if Self::norm_ilp_on() {
10171            "rms_norm_f32_v2"
10172        } else {
10173            "rms_norm_f32"
10174        };
10175        if Self::pdl_on() && Self::pdl_wb_on() {
10176            use cudarc::driver::{DevicePtr, DevicePtrMut};
10177            let s = &self.gpu.stream();
10178            let (px, _g0) = x.device_ptr(s);
10179            let (pw, _g1) = w.device_ptr(s);
10180            let (pd, _g2) = dst.device_ptr_mut(s);
10181            let mut ps = [
10182                &px as *const _ as *mut std::ffi::c_void,
10183                &pw as *const _ as *mut _,
10184                &pd as *const _ as *mut _,
10185                &nc as *const _ as *mut _,
10186                &e as *const _ as *mut _,
10187            ];
10188            unsafe {
10189                self.launch_pdl(kname, (nrows as u32, 1, 1), (rms_block(), 1, 1), &mut ps)?;
10190            }
10191            return Ok(());
10192        }
10193        let f = self.func(kname);
10194        let cfg = LaunchConfig {
10195            grid_dim: (nrows as u32, 1, 1),
10196            block_dim: (rms_block(), 1, 1),
10197            shared_mem_bytes: 0,
10198        };
10199        let __s_b = self.gpu.stream();
10200        let mut b = __s_b.launch_builder(&f);
10201        b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
10202        unsafe {
10203            b.launch(cfg)?;
10204        }
10205        Ok(())
10206    }
10207
10208    /// RMS-norm with blockDim=1024 — BIT-IDENTICAL to the fused `rms_norm_q8_1` and
10209    /// `add_rms_norm_q8_1` kernels' sum-of-squares reduction. The spec verify path MUST use this
10210    /// to match decode's FP accumulation order: the standard `rms_norm` at blockDim=256 has a
10211    /// different per-thread stride (ncols/256 partials vs ncols/1024 partials) and therefore a
10212    /// different shfl-tree reduction that can shift `scale = rsqrt(sum/n + eps)` by ULPs, causing
10213    /// divergence through the GDN scan and argmax flips on the 9B text prompt. The underlying
10214    /// `rms_norm_f32` kernel supports any blockDim (generic reduce with shared[32]).
10215    pub fn rms_norm_decode(
10216        &self,
10217        x: &CudaSlice<f32>,
10218        w: &CudaSlice<f32>,
10219        dst: &mut CudaSlice<f32>,
10220        ncols: usize,
10221        nrows: usize,
10222        eps: f32,
10223    ) -> Result<(), Box<dyn std::error::Error>> {
10224        let f = self.func(if Self::norm_ilp_on() {
10225            "rms_norm_f32_v2"
10226        } else {
10227            "rms_norm_f32"
10228        });
10229        let cfg = LaunchConfig {
10230            grid_dim: (nrows as u32, 1, 1),
10231            block_dim: (1024, 1, 1),
10232            shared_mem_bytes: 0,
10233        };
10234        let (nc, e) = (ncols as i32, eps);
10235        let __s_b = self.gpu.stream();
10236        let mut b = __s_b.launch_builder(&f);
10237        b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
10238        unsafe {
10239            b.launch(cfg)?;
10240        }
10241        Ok(())
10242    }
10243
10244    /// DECODE GLUE-FUSION LEVER: `z = rms_norm(x)*w` emitted DIRECTLY as q8_1 (no f32 `z` materialized,
10245    /// no standalone quantize_q8_1 launch). Returns (out_q [nrows*ncols i8], out_d [nrows*nblk f32])
10246    /// ready to feed matmul_pre. BIT-IDENTICAL to rms_norm + quantize_q8_1. ncols % 32 == 0.
10247    pub fn rms_norm_q8_1(
10248        &self,
10249        x: &CudaSlice<f32>,
10250        w: &CudaSlice<f32>,
10251        ncols: usize,
10252        nrows: usize,
10253        eps: f32,
10254    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10255        let nblk = ncols / 32;
10256        let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
10257        let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
10258        let (nc, e) = (ncols as i32, eps);
10259        if Self::pdl_on() {
10260            {
10261                use cudarc::driver::{DevicePtr, DevicePtrMut};
10262                let s = &self.gpu.stream();
10263                let (px, _g0) = x.device_ptr(s);
10264                let (pw, _g1) = w.device_ptr(s);
10265                let (pq, _g2) = q.device_ptr_mut(s);
10266                let (pd, _g3) = d.device_ptr_mut(s);
10267                let mut ps = [
10268                    &px as *const _ as *mut std::ffi::c_void,
10269                    &pw as *const _ as *mut _,
10270                    &pq as *const _ as *mut _,
10271                    &pd as *const _ as *mut _,
10272                    &nc as *const _ as *mut _,
10273                    &e as *const _ as *mut _,
10274                ];
10275                unsafe {
10276                    self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
10277                }
10278            }
10279            return Ok((q, d));
10280        }
10281        let f = self.func("rms_norm_q8_1");
10282        // 1024 threads: decode is nrows=1 -> ONE CTA; 32 warps hide the pass1->pass2 latency
10283        // (s[32] reduce already sized for 32 warps). Same shape math at any blockDim.
10284        let cfg = LaunchConfig {
10285            grid_dim: (nrows as u32, 1, 1),
10286            block_dim: (1024, 1, 1),
10287            shared_mem_bytes: 0,
10288        };
10289        let __s_b = self.gpu.stream();
10290        let mut b = __s_b.launch_builder(&f);
10291        b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
10292        unsafe {
10293            b.launch(cfg)?;
10294        }
10295        Ok((q, d))
10296    }
10297
10298    /// Slot-fed rms_norm_q8_1 twin (alloc-free capture lane): identical launch (incl. the
10299    /// PDL arm), caller-owned outputs.
10300    pub fn rms_norm_q8_1_into(
10301        &self,
10302        x: &CudaSlice<f32>,
10303        w: &CudaSlice<f32>,
10304        ncols: usize,
10305        nrows: usize,
10306        eps: f32,
10307        q: &mut CudaSlice<i8>,
10308        d: &mut CudaSlice<f32>,
10309    ) -> Result<(), Box<dyn std::error::Error>> {
10310        let nblk = ncols / 32;
10311        debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
10312        let (nc, e) = (ncols as i32, eps);
10313        if Self::pdl_on() {
10314            use cudarc::driver::{DevicePtr, DevicePtrMut};
10315            let s = &self.gpu.stream();
10316            let (px, _g0) = x.device_ptr(s);
10317            let (pw, _g1) = w.device_ptr(s);
10318            let (pq, _g2) = q.device_ptr_mut(s);
10319            let (pd, _g3) = d.device_ptr_mut(s);
10320            let mut ps = [
10321                &px as *const _ as *mut std::ffi::c_void,
10322                &pw as *const _ as *mut _,
10323                &pq as *const _ as *mut _,
10324                &pd as *const _ as *mut _,
10325                &nc as *const _ as *mut _,
10326                &e as *const _ as *mut _,
10327            ];
10328            unsafe {
10329                self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
10330            }
10331            return Ok(());
10332        }
10333        let f = self.func("rms_norm_q8_1");
10334        let cfg = LaunchConfig {
10335            grid_dim: (nrows as u32, 1, 1),
10336            block_dim: (1024, 1, 1),
10337            shared_mem_bytes: 0,
10338        };
10339        let __s_b = self.gpu.stream();
10340        let mut b = __s_b.launch_builder(&f);
10341        b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
10342        unsafe {
10343            b.launch(cfg)?;
10344        }
10345        Ok(())
10346    }
10347
10348    /// Slot-fed quantize_q8_1 twin (alloc-free capture lane).
10349    pub fn quantize_q8_1_into(
10350        &self,
10351        x: &CudaSlice<f32>,
10352        m: usize,
10353        in_f: usize,
10354        q: &mut CudaSlice<i8>,
10355        d: &mut CudaSlice<f32>,
10356    ) -> Result<(), Box<dyn std::error::Error>> {
10357        let nblk = in_f / 32;
10358        debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
10359        let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
10360        let (inf, mi) = (in_f as i32, m as i32);
10361        if Self::pdl_on() && Self::pdl_wb_on() {
10362            use cudarc::driver::{DevicePtr, DevicePtrMut};
10363            let s = &self.gpu.stream();
10364            let (px, _g0) = x.device_ptr(s);
10365            let (pq, _g1) = q.device_ptr_mut(s);
10366            let (pd, _g2) = d.device_ptr_mut(s);
10367            let mut ps = [
10368                &px as *const _ as *mut std::ffi::c_void,
10369                &pq as *const _ as *mut _,
10370                &pd as *const _ as *mut _,
10371                &inf as *const _ as *mut _,
10372                &mi as *const _ as *mut _,
10373            ];
10374            unsafe {
10375                self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
10376            }
10377            return Ok(());
10378        }
10379        let f = self.func("quantize_q8_1");
10380        let __s_b = self.gpu.stream();
10381        let mut b = __s_b.launch_builder(&f);
10382        b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
10383        unsafe {
10384            b.launch(cfg)?;
10385        }
10386        Ok(())
10387    }
10388
10389    /// DECODE GLUE-FUSION LEVER: `res = a+b; z = rms_norm(res)*w` with z emitted as q8_1. `res` is
10390    /// still written (the post-ffn residual add reads it). Fuses add_rms_norm + quantize_q8_1.
10391    /// Returns (out_q, out_d) for matmul_pre. BIT-IDENTICAL. ncols % 32 == 0.
10392    pub fn add_rms_norm_q8_1(
10393        &self,
10394        a: &CudaSlice<f32>,
10395        b_in: &CudaSlice<f32>,
10396        w: &CudaSlice<f32>,
10397        res: &mut CudaSlice<f32>,
10398        ncols: usize,
10399        nrows: usize,
10400        eps: f32,
10401    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10402        let nblk = ncols / 32;
10403        let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
10404        let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
10405        let f = self.func("add_rms_norm_q8_1");
10406        // 1024 threads: same single-CTA-at-decode reasoning as rms_norm_q8_1.
10407        let cfg = LaunchConfig {
10408            grid_dim: (nrows as u32, 1, 1),
10409            block_dim: (1024, 1, 1),
10410            shared_mem_bytes: 0,
10411        };
10412        let (nc, e) = (ncols as i32, eps);
10413        let __s_bld = self.gpu.stream();
10414        let mut bld = __s_bld.launch_builder(&f);
10415        bld.arg(a)
10416            .arg(b_in)
10417            .arg(w)
10418            .arg(res)
10419            .arg(&mut q)
10420            .arg(&mut d)
10421            .arg(&nc)
10422            .arg(&e);
10423        unsafe {
10424            bld.launch(cfg)?;
10425        }
10426        Ok((q, d))
10427    }
10428
10429    /// RANK3 LEVER (add+rmsnorm fuse): `res = a + b; dst = rms_norm(res) * w` in ONE launch. Fuses
10430    /// e.add(a,b,res) + e.rms_norm(res,w,dst), removing one launch + one HBM read of the residual per
10431    /// residual+norm pair. BIT-IDENTICAL to the two-kernel sequence (same IEEE add, same reduction).
10432    /// O-PROJ TAIL FUSION M2: mixed = a0+a1 composed in-register, then the VERBATIM
10433    /// add_rms_norm program. Raw UVA pointers for the join partials (persistent ws rows).
10434    #[allow(clippy::too_many_arguments)]
10435    pub fn join_add_rms_norm_raw(
10436        &self,
10437        a0_raw: u64,
10438        a1_raw: u64,
10439        x: &CudaSlice<f32>,
10440        w: &CudaSlice<f32>,
10441        res: &mut CudaSlice<f32>,
10442        dst: &mut CudaSlice<f32>,
10443        ncols: usize,
10444        eps: f32,
10445    ) -> Result<(), Box<dyn std::error::Error>> {
10446        if a0_raw == 0 || a1_raw == 0 || x.len() < ncols || res.len() < ncols || dst.len() < ncols {
10447            return Err("join_add_rms_norm geometry".into());
10448        }
10449        let f = self.func("join_add_rms_norm_f32");
10450        let cfg = LaunchConfig {
10451            grid_dim: (1, 1, 1),
10452            block_dim: (rms_block(), 1, 1),
10453            shared_mem_bytes: 0,
10454        };
10455        let (nc, e) = (ncols as i32, eps);
10456        let __s_b = self.gpu.stream();
10457        let mut b = __s_b.launch_builder(&f);
10458        b.arg(&a0_raw)
10459            .arg(&a1_raw)
10460            .arg(x)
10461            .arg(w)
10462            .arg(&mut *res)
10463            .arg(&mut *dst)
10464            .arg(&nc)
10465            .arg(&e);
10466        unsafe {
10467            b.launch(cfg)?;
10468        }
10469        Ok(())
10470    }
10471
10472    pub fn add_rms_norm(
10473        &self,
10474        a: &CudaSlice<f32>,
10475        b: &CudaSlice<f32>,
10476        w: &CudaSlice<f32>,
10477        res: &mut CudaSlice<f32>,
10478        dst: &mut CudaSlice<f32>,
10479        ncols: usize,
10480        nrows: usize,
10481        eps: f32,
10482    ) -> Result<(), Box<dyn std::error::Error>> {
10483        let (nc, e) = (ncols as i32, eps);
10484        let kname = if Self::norm_ilp_on() {
10485            "add_rms_norm_f32_v2"
10486        } else {
10487            "add_rms_norm_f32"
10488        };
10489        if Self::pdl_on() && Self::pdl_wb_on() {
10490            use cudarc::driver::{DevicePtr, DevicePtrMut};
10491            let s = &self.gpu.stream();
10492            let (pa, _g0) = a.device_ptr(s);
10493            let (pb, _g1) = b.device_ptr(s);
10494            let (pw, _g2) = w.device_ptr(s);
10495            let (pr, _g3) = res.device_ptr_mut(s);
10496            let (pd, _g4) = dst.device_ptr_mut(s);
10497            let mut ps = [
10498                &pa as *const _ as *mut std::ffi::c_void,
10499                &pb as *const _ as *mut _,
10500                &pw as *const _ as *mut _,
10501                &pr as *const _ as *mut _,
10502                &pd as *const _ as *mut _,
10503                &nc as *const _ as *mut _,
10504                &e as *const _ as *mut _,
10505            ];
10506            unsafe {
10507                self.launch_pdl(kname, (nrows as u32, 1, 1), (rms_block(), 1, 1), &mut ps)?;
10508            }
10509            return Ok(());
10510        }
10511        let f = self.func(kname);
10512        let cfg = LaunchConfig {
10513            grid_dim: (nrows as u32, 1, 1),
10514            block_dim: (rms_block(), 1, 1),
10515            shared_mem_bytes: 0,
10516        };
10517        let __s_b2 = self.gpu.stream();
10518        let mut b2 = __s_b2.launch_builder(&f);
10519        b2.arg(a)
10520            .arg(b)
10521            .arg(w)
10522            .arg(&mut *res)
10523            .arg(&mut *dst)
10524            .arg(&nc)
10525            .arg(&e);
10526        unsafe {
10527            b2.launch(cfg)?;
10528        }
10529        Ok(())
10530    }
10531
10532    /// E4B glue fusion: rms(a, wa) prologue + add_rms_norm — folds the post-attn norm into
10533    /// the tail entry (res = rms(a)*wa + b; dst = rms(res)*w).
10534    #[allow(clippy::too_many_arguments)]
10535    pub fn rms_pre_add_rms_norm(
10536        &self,
10537        a: &CudaSlice<f32>,
10538        wa: &CudaSlice<f32>,
10539        b: &CudaSlice<f32>,
10540        w: &CudaSlice<f32>,
10541        res: &mut CudaSlice<f32>,
10542        dst: &mut CudaSlice<f32>,
10543        ncols: usize,
10544        nrows: usize,
10545        eps: f32,
10546    ) -> Result<(), Box<dyn std::error::Error>> {
10547        let f = self.func("rms_pre_add_rms_norm_f32");
10548        let cfg = LaunchConfig {
10549            grid_dim: (nrows as u32, 1, 1),
10550            block_dim: (rms_block(), 1, 1),
10551            shared_mem_bytes: 0,
10552        };
10553        let (nc, e) = (ncols as i32, eps);
10554        let __s_b2 = self.gpu.stream();
10555        let mut b2 = __s_b2.launch_builder(&f);
10556        b2.arg(a)
10557            .arg(wa)
10558            .arg(b)
10559            .arg(w)
10560            .arg(&mut *res)
10561            .arg(&mut *dst)
10562            .arg(&nc)
10563            .arg(&e);
10564        unsafe {
10565            b2.launch(cfg)?;
10566        }
10567        Ok(())
10568    }
10569
10570    /// wave-2 fold: rms(a,wa) + add + ffn-norm with zsh EMITTED q8_1 (fused2 consumes it).
10571    #[allow(clippy::too_many_arguments)]
10572    pub fn rms_pre_add_rms_norm_q8z(
10573        &self,
10574        a: &CudaSlice<f32>,
10575        wa: &CudaSlice<f32>,
10576        b: &CudaSlice<f32>,
10577        w: &CudaSlice<f32>,
10578        res: &mut CudaSlice<f32>,
10579        dst: &mut CudaSlice<f32>,
10580        ncols: usize,
10581        nrows: usize,
10582        eps: f32,
10583    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10584        debug_assert!(ncols % 128 == 0);
10585        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
10586        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
10587        let (nc, e) = (ncols as i32, eps);
10588        if Self::pdl_on() {
10589            {
10590                use cudarc::driver::{DevicePtr, DevicePtrMut};
10591                let s = &self.gpu.stream();
10592                let (pa, _g0) = a.device_ptr(s);
10593                let (pwa, _g1) = wa.device_ptr(s);
10594                let (pb, _g2) = b.device_ptr(s);
10595                let (pw, _g3) = w.device_ptr(s);
10596                let (pr, _g4) = res.device_ptr_mut(s);
10597                let (pdst, _g5) = dst.device_ptr_mut(s);
10598                let (pq, _g6) = out_q.device_ptr_mut(s);
10599                let (pd, _g7) = out_d.device_ptr_mut(s);
10600                let mut ps = [
10601                    &pa as *const _ as *mut std::ffi::c_void,
10602                    &pwa as *const _ as *mut _,
10603                    &pb as *const _ as *mut _,
10604                    &pw as *const _ as *mut _,
10605                    &pr as *const _ as *mut _,
10606                    &pdst as *const _ as *mut _,
10607                    &pq as *const _ as *mut _,
10608                    &pd as *const _ as *mut _,
10609                    &nc as *const _ as *mut _,
10610                    &e as *const _ as *mut _,
10611                ];
10612                unsafe {
10613                    self.launch_pdl(
10614                        "rms_pre_add_rms_norm_q8z_f32",
10615                        (nrows as u32, 1, 1),
10616                        (rms_block(), 1, 1),
10617                        &mut ps,
10618                    )?;
10619                }
10620            }
10621            return Ok((out_q, out_d));
10622        }
10623        let f = self.func("rms_pre_add_rms_norm_q8z_f32");
10624        let cfg = LaunchConfig {
10625            grid_dim: (nrows as u32, 1, 1),
10626            block_dim: (rms_block(), 1, 1),
10627            shared_mem_bytes: 0,
10628        };
10629        let __s_b2 = self.gpu.stream();
10630        let mut b2 = __s_b2.launch_builder(&f);
10631        b2.arg(a)
10632            .arg(wa)
10633            .arg(b)
10634            .arg(w)
10635            .arg(&mut *res)
10636            .arg(&mut *dst)
10637            .arg(&mut out_q)
10638            .arg(&mut out_d)
10639            .arg(&nc)
10640            .arg(&e);
10641        unsafe {
10642            b2.launch(cfg)?;
10643        }
10644        Ok((out_q, out_d))
10645    }
10646
10647    /// Slot-fed twin of `rms_pre_add_rms_norm_q8z` (gemma4 pn-fold, slotted/graph arm):
10648    /// identical kernel, caller-owned outputs, PLAIN launch only — the dc_slotted capture
10649    /// body must stay attribute-free (the fused2_into precedent).
10650    #[allow(clippy::too_many_arguments)]
10651    pub fn rms_pre_add_rms_norm_q8z_into(
10652        &self,
10653        a: &CudaSlice<f32>,
10654        wa: &CudaSlice<f32>,
10655        b: &CudaSlice<f32>,
10656        w: &CudaSlice<f32>,
10657        res: &mut CudaSlice<f32>,
10658        dst: &mut CudaSlice<f32>,
10659        ncols: usize,
10660        nrows: usize,
10661        eps: f32,
10662        out_q: &mut CudaSlice<i8>,
10663        out_d: &mut CudaSlice<f32>,
10664    ) -> Result<(), Box<dyn std::error::Error>> {
10665        debug_assert!(ncols % 128 == 0);
10666        let (nc, e) = (ncols as i32, eps);
10667        let f = self.func("rms_pre_add_rms_norm_q8z_f32");
10668        let cfg = LaunchConfig {
10669            grid_dim: (nrows as u32, 1, 1),
10670            block_dim: (rms_block(), 1, 1),
10671            shared_mem_bytes: 0,
10672        };
10673        let __s_b = self.gpu.stream();
10674        let mut b2 = __s_b.launch_builder(&f);
10675        b2.arg(a)
10676            .arg(wa)
10677            .arg(b)
10678            .arg(w)
10679            .arg(&mut *res)
10680            .arg(&mut *dst)
10681            .arg(&mut *out_q)
10682            .arg(&mut *out_d)
10683            .arg(&nc)
10684            .arg(&e);
10685        unsafe {
10686            b2.launch(cfg)?;
10687        }
10688        Ok(())
10689    }
10690
10691    /// Slot-fed twin of `rms_pre_add_scale_rms_norm_q8_1` (gemma4 pn-fold exit, slotted
10692    /// arm): identical kernel, caller-owned outputs, PLAIN launch (capture-safe).
10693    #[allow(clippy::too_many_arguments)]
10694    pub fn rms_pre_add_scale_rms_norm_q8_1_into(
10695        &self,
10696        a: &CudaSlice<f32>,
10697        wa: &CudaSlice<f32>,
10698        b_in: &CudaSlice<f32>,
10699        c: f32,
10700        w: &CudaSlice<f32>,
10701        res: &mut CudaSlice<f32>,
10702        ncols: usize,
10703        nrows: usize,
10704        eps: f32,
10705        out_q: &mut CudaSlice<i8>,
10706        out_d: &mut CudaSlice<f32>,
10707    ) -> Result<(), Box<dyn std::error::Error>> {
10708        debug_assert!(ncols % 128 == 0);
10709        let (nc, e2) = (ncols as i32, eps);
10710        let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
10711        let cfg = LaunchConfig {
10712            grid_dim: (nrows as u32, 1, 1),
10713            block_dim: (rms_block(), 1, 1),
10714            shared_mem_bytes: 0,
10715        };
10716        let __s_b = self.gpu.stream();
10717        let mut b2 = __s_b.launch_builder(&f);
10718        b2.arg(a)
10719            .arg(wa)
10720            .arg(b_in)
10721            .arg(&c)
10722            .arg(w)
10723            .arg(&mut *res)
10724            .arg(&mut *out_q)
10725            .arg(&mut *out_d)
10726            .arg(&nc)
10727            .arg(&e2);
10728        unsafe {
10729            b2.launch(cfg)?;
10730        }
10731        Ok(())
10732    }
10733
10734    /// gemma4 pn-fold seam (GAP-DIAGNOSIS verdict 7, the E4B glue backport): the dense
10735    /// decode/verify/slotted trio folds post_attn_norm into the tail entry
10736    /// (rms_pre_add_rms_norm[_q8z]) and post_ffw_norm into the residual exit
10737    /// (rms_pre_add_scale_rms_norm_q8_1). BITS-CHANGING vs the two-launch chain (the
10738    /// single-phase reduction's expansion rounding — E4B receipts); every arm moves
10739    /// together so decode == verify == graph parity holds BY CONSTRUCTION within either
10740    /// seam value. MEMRA_G4_PNFOLD=0 restores the unfused chain everywhere.
10741    pub fn g4_pnfold_on() -> bool {
10742        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10743        *ON.get_or_init(|| {
10744            std::env::var("MEMRA_G4_PNFOLD")
10745                .map(|v| v != "0")
10746                .unwrap_or(true)
10747        })
10748    }
10749
10750    /// wave-4b: OUT-dim concat of three Q4_0 tensors (same in_features; rows are independent
10751    /// blocks, so the concat is a D2D byte concat of the GGUF-layout planes). Returns None
10752    /// off-class (non-Q4_0, mismatched widths, or any tensor already rp-swapped in place).
10753    pub fn build_q4_out_concat3(
10754        &self,
10755        w0: &crate::model::GpuTensor,
10756        w1: &crate::model::GpuTensor,
10757        w2: &crate::model::GpuTensor,
10758    ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
10759        use crate::model::GpuTensor;
10760        let part = |w: &GpuTensor| -> Option<(usize, usize)> {
10761            match w {
10762                GpuTensor::Quant {
10763                    qtype,
10764                    row_bytes,
10765                    rp,
10766                    ..
10767                } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
10768                _ => None,
10769            }
10770        };
10771        let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
10772        else {
10773            return Ok(None);
10774        };
10775        if rb0 != rb1
10776            || rb0 != rb2
10777            || w0.in_features() != w1.in_features()
10778            || w0.in_features() != w2.in_features()
10779        {
10780            return Ok(None);
10781        }
10782        fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
10783            match w {
10784                crate::model::GpuTensor::Quant { bytes, .. } => bytes,
10785                _ => unreachable!(),
10786            }
10787        }
10788        let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
10789        let total = rb0 * (o0 + o1 + o2);
10790        let mut cat = self.alloc_u8(total)?;
10791        self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
10792        self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
10793        self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
10794        Ok(Some(GpuTensor::Quant {
10795            bytes: cat,
10796            qtype: QT_Q4_0,
10797            row_bytes: rb0,
10798            ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
10799            scale: 1.0,
10800            rp: false,
10801            #[cfg(memra_cutlass)]
10802            cutlass: None,
10803            fp8: None,
10804            blk: None,
10805            rp4: None,
10806            f16: None,
10807        }))
10808    }
10809
10810    /// FULL-WIDTH-ROPE CONTRACT for the fused rms_norm+qkv+rope kernels
10811    /// (lane/graph-s-key-exactness-20260819, probe O-10 follow-up).
10812    ///
10813    /// `rms_norm_qkv_rope_f32` / `_cat_f32` (`cu/kernels.cu`) and
10814    /// `rms_norm_qkv_rope_append_body` (`cu/flash_attn.cu`) take NO `n_dims`/`n_rot` argument.
10815    /// They compute `int half = ncols / 2` and rotate the FULL head width by construction — the
10816    /// standalone `rope_neox*` kernels take `n_dims` and early-return above it, these do not.
10817    ///
10818    /// Every call site today is a gemma-4 arm, where `n_rot == head_dim`, so nothing is wrong
10819    /// now. But this is the n_rot bug ONE FUSION away: fuse a partial-rotary arch onto these
10820    /// kernels (qwen3.5 = 64 rotary dims of a 256-wide head; step35 full-attn = 64 of 128) and
10821    /// 192 dims that must pass through unrotated get rotated silently — no error, no NaN, just a
10822    /// wrong model. The n_rot lane already paid for that class once, in the config derivation.
10823    ///
10824    /// So the fusions now take the layer's DERIVED rope width and refuse anything but full
10825    /// width. A future partial-rotary caller fails at its first launch with the geometry named
10826    /// instead of serving quietly wrong logits.
10827    fn full_width_rope_only(
10828        kernel: &str,
10829        n_rot: usize,
10830        head_dim: usize,
10831    ) -> Result<(), Box<dyn std::error::Error>> {
10832        if n_rot == head_dim {
10833            return Ok(());
10834        }
10835        Err(format!(
10836            "{kernel}: PARTIAL ROTARY REFUSED — n_rot {n_rot} != head_dim {head_dim}. This fused \
10837             rms_norm+qkv+rope kernel carries no n_dims parameter and rotates the full head \
10838             width (half = ncols/2), so it would rotate dims {n_rot}..{head_dim} that must pass \
10839             through unrotated. Use the split path (rms_norm_qkv + rope_neox/rope_neox2 with \
10840             n_dims={n_rot}), or add an n_dims early-return to the kernel and widen this guard."
10841        )
10842        .into())
10843    }
10844
10845    /// wave-4b: the qkv-cat twin — one contiguous [rq+2*rk, hd] input from the concat matvec.
10846    /// `n_rot` is the layer's derived rotary width and MUST equal `head_dim`
10847    /// ([`Engine::full_width_rope_only`]).
10848    #[allow(clippy::too_many_arguments)]
10849    pub fn rms_norm_qkv_rope_cat(
10850        &self,
10851        qkv: &CudaSlice<f32>,
10852        wq: &CudaSlice<f32>,
10853        wk: &CudaSlice<f32>,
10854        wv: &CudaSlice<f32>,
10855        q: &mut CudaSlice<f32>,
10856        k: &mut CudaSlice<f32>,
10857        v: &mut CudaSlice<f32>,
10858        head_dim: usize,
10859        n_rot: usize,
10860        rq: usize,
10861        rk: usize,
10862        pos: &CudaSlice<i32>,
10863        nh_q: usize,
10864        nh_k: usize,
10865        base: f32,
10866        freq_scale: f32,
10867        ff: Option<&CudaSlice<f32>>,
10868        eps: f32,
10869    ) -> Result<(), Box<dyn std::error::Error>> {
10870        Self::full_width_rope_only("rms_norm_qkv_rope_cat", n_rot, head_dim)?;
10871        let rows = rq + rk + rk;
10872        let theta_scale = base.powf(-2.0 / head_dim as f32);
10873        let (nc, rqi, rki, nhq, nhk) = (
10874            head_dim as i32,
10875            rq as i32,
10876            rk as i32,
10877            nh_q as i32,
10878            nh_k as i32,
10879        );
10880        if Self::pdl_on() {
10881            use cudarc::driver::{DevicePtr, DevicePtrMut};
10882            let s = &self.gpu.stream();
10883            let (pqkv, _g0) = qkv.device_ptr(s);
10884            let (pwq, _g1) = wq.device_ptr(s);
10885            let (pwk, _g2) = wk.device_ptr(s);
10886            let (pwv, _g3) = wv.device_ptr(s);
10887            let (pq, _g4) = q.device_ptr_mut(s);
10888            let (pk, _g5) = k.device_ptr_mut(s);
10889            let (pv, _g6) = v.device_ptr_mut(s);
10890            let (ppos, _g7) = pos.device_ptr(s);
10891            let (pff, _g8) = match ff {
10892                Some(t) => {
10893                    let (p, g) = t.device_ptr(s);
10894                    (p, Some(g))
10895                }
10896                None => (0, None),
10897            };
10898            let mut ps = [
10899                &pqkv as *const _ as *mut std::ffi::c_void,
10900                &pwq as *const _ as *mut _,
10901                &pwk as *const _ as *mut _,
10902                &pwv as *const _ as *mut _,
10903                &pq as *const _ as *mut _,
10904                &pk as *const _ as *mut _,
10905                &pv as *const _ as *mut _,
10906                &nc as *const _ as *mut _,
10907                &rqi as *const _ as *mut _,
10908                &rki as *const _ as *mut _,
10909                &ppos as *const _ as *mut _,
10910                &nhq as *const _ as *mut _,
10911                &nhk as *const _ as *mut _,
10912                &theta_scale as *const _ as *mut _,
10913                &freq_scale as *const _ as *mut _,
10914                &pff as *const _ as *mut _,
10915                &eps as *const _ as *mut _,
10916            ];
10917            unsafe {
10918                self.launch_pdl(
10919                    "rms_norm_qkv_rope_cat_f32",
10920                    (rows as u32, 1, 1),
10921                    (rms_block(), 1, 1),
10922                    &mut ps,
10923                )?;
10924            }
10925            return Ok(());
10926        }
10927        let f = self.func("rms_norm_qkv_rope_cat_f32");
10928        let cfg = LaunchConfig {
10929            grid_dim: (rows as u32, 1, 1),
10930            block_dim: (rms_block(), 1, 1),
10931            shared_mem_bytes: 0,
10932        };
10933        let __s_b = self.gpu.stream();
10934        let mut b = __s_b.launch_builder(&f);
10935        match ff {
10936            Some(t) => {
10937                b.arg(qkv)
10938                    .arg(wq)
10939                    .arg(wk)
10940                    .arg(wv)
10941                    .arg(&mut *q)
10942                    .arg(&mut *k)
10943                    .arg(&mut *v)
10944                    .arg(&nc)
10945                    .arg(&rqi)
10946                    .arg(&rki)
10947                    .arg(pos)
10948                    .arg(&nhq)
10949                    .arg(&nhk)
10950                    .arg(&theta_scale)
10951                    .arg(&freq_scale)
10952                    .arg(t)
10953                    .arg(&eps);
10954                unsafe {
10955                    b.launch(cfg)?;
10956                }
10957            }
10958            None => {
10959                let null: u64 = 0;
10960                b.arg(qkv)
10961                    .arg(wq)
10962                    .arg(wk)
10963                    .arg(wv)
10964                    .arg(&mut *q)
10965                    .arg(&mut *k)
10966                    .arg(&mut *v)
10967                    .arg(&nc)
10968                    .arg(&rqi)
10969                    .arg(&rki)
10970                    .arg(pos)
10971                    .arg(&nhq)
10972                    .arg(&nhk)
10973                    .arg(&theta_scale)
10974                    .arg(&freq_scale)
10975                    .arg(&null)
10976                    .arg(&eps);
10977                unsafe {
10978                    b.launch(cfg)?;
10979                }
10980            }
10981        }
10982        Ok(())
10983    }
10984
10985    /// wave-3 fold: rms_norm_qkv + rope_neox2 in ONE launch (n_dims == head_dim; ff nullable).
10986    /// `n_rot` is the layer's derived rotary width and MUST equal `head_dim`
10987    /// ([`Engine::full_width_rope_only`]).
10988    #[allow(clippy::too_many_arguments)]
10989    pub fn rms_norm_qkv_rope(
10990        &self,
10991        q0: &CudaSlice<f32>,
10992        k0: &CudaSlice<f32>,
10993        v0: &CudaSlice<f32>,
10994        wq: &CudaSlice<f32>,
10995        wk: &CudaSlice<f32>,
10996        wv: &CudaSlice<f32>,
10997        q: &mut CudaSlice<f32>,
10998        k: &mut CudaSlice<f32>,
10999        v: &mut CudaSlice<f32>,
11000        head_dim: usize,
11001        n_rot: usize,
11002        rq: usize,
11003        rk: usize,
11004        pos: &CudaSlice<i32>,
11005        nh_q: usize,
11006        nh_k: usize,
11007        base: f32,
11008        freq_scale: f32,
11009        ff: Option<&CudaSlice<f32>>,
11010        eps: f32,
11011    ) -> Result<(), Box<dyn std::error::Error>> {
11012        Self::full_width_rope_only("rms_norm_qkv_rope", n_rot, head_dim)?;
11013        let f = self.func("rms_norm_qkv_rope_f32");
11014        let rows = rq + rk + rk; // q rows + k rows + v rows (rk == rv)
11015        let cfg = LaunchConfig {
11016            grid_dim: (rows as u32, 1, 1),
11017            block_dim: (rms_block(), 1, 1),
11018            shared_mem_bytes: 0,
11019        };
11020        let theta_scale = base.powf(-2.0 / head_dim as f32);
11021        let (nc, rqi, rki, nhq, nhk) = (
11022            head_dim as i32,
11023            rq as i32,
11024            rk as i32,
11025            nh_q as i32,
11026            nh_k as i32,
11027        );
11028        let __s_b = self.gpu.stream();
11029        let mut b = __s_b.launch_builder(&f);
11030        match ff {
11031            Some(t) => {
11032                b.arg(q0)
11033                    .arg(k0)
11034                    .arg(v0)
11035                    .arg(wq)
11036                    .arg(wk)
11037                    .arg(wv)
11038                    .arg(&mut *q)
11039                    .arg(&mut *k)
11040                    .arg(&mut *v)
11041                    .arg(&nc)
11042                    .arg(&rqi)
11043                    .arg(&rki)
11044                    .arg(pos)
11045                    .arg(&nhq)
11046                    .arg(&nhk)
11047                    .arg(&theta_scale)
11048                    .arg(&freq_scale)
11049                    .arg(t)
11050                    .arg(&eps);
11051                unsafe {
11052                    b.launch(cfg)?;
11053                }
11054            }
11055            None => {
11056                let null: u64 = 0;
11057                b.arg(q0)
11058                    .arg(k0)
11059                    .arg(v0)
11060                    .arg(wq)
11061                    .arg(wk)
11062                    .arg(wv)
11063                    .arg(&mut *q)
11064                    .arg(&mut *k)
11065                    .arg(&mut *v)
11066                    .arg(&nc)
11067                    .arg(&rqi)
11068                    .arg(&rki)
11069                    .arg(pos)
11070                    .arg(&nhq)
11071                    .arg(&nhk)
11072                    .arg(&theta_scale)
11073                    .arg(&freq_scale)
11074                    .arg(&null)
11075                    .arg(&eps);
11076                unsafe {
11077                    b.launch(cfg)?;
11078                }
11079            }
11080        }
11081        Ok(())
11082    }
11083
11084    /// FUSED norm+rope+APPEND (m=1 decode, 2026-07-23): one launch replaces the
11085    /// rms_norm_qkv_rope + append_kv_quantized_dc pair. Kernel lives in the flash fatbins
11086    /// (format-flavored quant tail) — `g` must mirror the append path's flavor exactly.
11087    /// `n_rot` is the layer's derived rotary width and MUST equal `head_dim`
11088    /// ([`Engine::full_width_rope_only`]).
11089    #[allow(clippy::too_many_arguments)]
11090    pub fn rms_norm_qkv_rope_append_dc(
11091        &self,
11092        q0: &CudaSlice<f32>,
11093        k0: &CudaSlice<f32>,
11094        v0: &CudaSlice<f32>,
11095        wq: &CudaSlice<f32>,
11096        wk: &CudaSlice<f32>,
11097        wv: &CudaSlice<f32>,
11098        q: &mut CudaSlice<f32>,
11099        k: &mut CudaSlice<f32>,
11100        v: &mut CudaSlice<f32>,
11101        head_dim: usize,
11102        n_rot: usize,
11103        rq: usize,
11104        rk: usize,
11105        pos: &CudaSlice<i32>,
11106        nh_q: usize,
11107        nh_k: usize,
11108        base: f32,
11109        freq_scale: f32,
11110        ff: Option<&CudaSlice<f32>>,
11111        eps: f32,
11112        kc: &mut CudaSlice<u8>,
11113        vc: &mut CudaSlice<u8>,
11114        t_dev: &CudaSlice<i32>,
11115        k_tok_bytes: usize,
11116        v_tok_bytes: usize,
11117        g: bool,
11118    ) -> Result<(), Box<dyn std::error::Error>> {
11119        Self::full_width_rope_only("rms_norm_qkv_rope_append_dc", n_rot, head_dim)?;
11120        let rows = rq + rk + rk;
11121        let theta_scale = base.powf(-2.0 / head_dim as f32);
11122        let (nc, rqi, rki, nhq, nhk) = (
11123            head_dim as i32,
11124            rq as i32,
11125            rk as i32,
11126            nh_q as i32,
11127            nh_k as i32,
11128        );
11129        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
11130        if Self::pdl_on() && Self::pdl_wb_on() {
11131            use cudarc::driver::{DevicePtr, DevicePtrMut};
11132            let s = &self.gpu.stream();
11133            let (p0, _a0) = q0.device_ptr(s);
11134            let (p1, _a1) = k0.device_ptr(s);
11135            let (p2, _a2) = v0.device_ptr(s);
11136            let (pwq, _a3) = wq.device_ptr(s);
11137            let (pwk, _a4) = wk.device_ptr(s);
11138            let (pwv, _a5) = wv.device_ptr(s);
11139            let (pq, _a6) = q.device_ptr_mut(s);
11140            let (pk, _a7) = k.device_ptr_mut(s);
11141            let (pv, _a8) = v.device_ptr_mut(s);
11142            let (pp, _a9) = pos.device_ptr(s);
11143            let pff: u64 = match ff {
11144                Some(t) => {
11145                    let (p, _gg) = t.device_ptr(s);
11146                    p as u64
11147                }
11148                None => 0,
11149            };
11150            let (pkc, _a10) = kc.device_ptr_mut(s);
11151            let (pvc, _a11) = vc.device_ptr_mut(s);
11152            let (pt, _a12) = t_dev.device_ptr(s);
11153            let mut ps = [
11154                &p0 as *const _ as *mut std::ffi::c_void,
11155                &p1 as *const _ as *mut _,
11156                &p2 as *const _ as *mut _,
11157                &pwq as *const _ as *mut _,
11158                &pwk as *const _ as *mut _,
11159                &pwv as *const _ as *mut _,
11160                &pq as *const _ as *mut _,
11161                &pk as *const _ as *mut _,
11162                &pv as *const _ as *mut _,
11163                &nc as *const _ as *mut _,
11164                &rqi as *const _ as *mut _,
11165                &rki as *const _ as *mut _,
11166                &pp as *const _ as *mut _,
11167                &nhq as *const _ as *mut _,
11168                &nhk as *const _ as *mut _,
11169                &theta_scale as *const _ as *mut _,
11170                &freq_scale as *const _ as *mut _,
11171                &pff as *const _ as *mut _,
11172                &eps as *const _ as *mut _,
11173                &pkc as *const _ as *mut _,
11174                &pvc as *const _ as *mut _,
11175                &pt as *const _ as *mut _,
11176                &ktb as *const _ as *mut _,
11177                &vtb as *const _ as *mut _,
11178            ];
11179            unsafe {
11180                self.launch_pdl_flash(
11181                    g,
11182                    "rms_norm_qkv_rope_append_dc_f32",
11183                    (rows as u32, 1, 1),
11184                    (rms_block(), 1, 1),
11185                    0,
11186                    &mut ps,
11187                )?;
11188            }
11189            return Ok(());
11190        }
11191        let f = if g {
11192            self.func_g("rms_norm_qkv_rope_append_dc_f32")
11193        } else {
11194            self.func("rms_norm_qkv_rope_append_dc_f32")
11195        };
11196        let cfg = LaunchConfig {
11197            grid_dim: (rows as u32, 1, 1),
11198            block_dim: (rms_block(), 1, 1),
11199            shared_mem_bytes: 0,
11200        };
11201        let __s_b = self.gpu.stream();
11202        let mut b = __s_b.launch_builder(&f);
11203        match ff {
11204            Some(t) => {
11205                b.arg(q0)
11206                    .arg(k0)
11207                    .arg(v0)
11208                    .arg(wq)
11209                    .arg(wk)
11210                    .arg(wv)
11211                    .arg(&mut *q)
11212                    .arg(&mut *k)
11213                    .arg(&mut *v)
11214                    .arg(&nc)
11215                    .arg(&rqi)
11216                    .arg(&rki)
11217                    .arg(pos)
11218                    .arg(&nhq)
11219                    .arg(&nhk)
11220                    .arg(&theta_scale)
11221                    .arg(&freq_scale)
11222                    .arg(t)
11223                    .arg(&eps)
11224                    .arg(&mut *kc)
11225                    .arg(&mut *vc)
11226                    .arg(t_dev)
11227                    .arg(&ktb)
11228                    .arg(&vtb);
11229                unsafe {
11230                    b.launch(cfg)?;
11231                }
11232            }
11233            None => {
11234                let null: u64 = 0;
11235                b.arg(q0)
11236                    .arg(k0)
11237                    .arg(v0)
11238                    .arg(wq)
11239                    .arg(wk)
11240                    .arg(wv)
11241                    .arg(&mut *q)
11242                    .arg(&mut *k)
11243                    .arg(&mut *v)
11244                    .arg(&nc)
11245                    .arg(&rqi)
11246                    .arg(&rki)
11247                    .arg(pos)
11248                    .arg(&nhq)
11249                    .arg(&nhk)
11250                    .arg(&theta_scale)
11251                    .arg(&freq_scale)
11252                    .arg(&null)
11253                    .arg(&eps)
11254                    .arg(&mut *kc)
11255                    .arg(&mut *vc)
11256                    .arg(t_dev)
11257                    .arg(&ktb)
11258                    .arg(&vtb);
11259                unsafe {
11260                    b.launch(cfg)?;
11261                }
11262            }
11263        }
11264        Ok(())
11265    }
11266
11267    /// wave-2 fold: a + b with the sum emitted q8_1 alongside f32.
11268    /// Host-len twin of `rms_norm_qkv_rope_append_dc` (zoo-fusion arc): the eager decode
11269    /// arm tracks kv length on host (`kvl.len`), so the append slot rides the launch arg
11270    /// instead of the device counter. Kernel body is SHARED with the _dc entry (one
11271    /// inlined body — bit-identical to the rms_norm_qkv_rope + append pair it replaces,
11272    /// same law as the dc fold). `n_rot` is the layer's derived rotary width and MUST equal
11273    /// `head_dim` ([`Engine::full_width_rope_only`]).
11274    #[allow(clippy::too_many_arguments)]
11275    pub fn rms_norm_qkv_rope_append(
11276        &self,
11277        q0: &CudaSlice<f32>,
11278        k0: &CudaSlice<f32>,
11279        v0: &CudaSlice<f32>,
11280        wq: &CudaSlice<f32>,
11281        wk: &CudaSlice<f32>,
11282        wv: &CudaSlice<f32>,
11283        q: &mut CudaSlice<f32>,
11284        k: &mut CudaSlice<f32>,
11285        v: &mut CudaSlice<f32>,
11286        head_dim: usize,
11287        n_rot: usize,
11288        rq: usize,
11289        rk: usize,
11290        pos: &CudaSlice<i32>,
11291        nh_q: usize,
11292        nh_k: usize,
11293        base: f32,
11294        freq_scale: f32,
11295        ff: Option<&CudaSlice<f32>>,
11296        eps: f32,
11297        kc: &mut CudaSlice<u8>,
11298        vc: &mut CudaSlice<u8>,
11299        t: usize,
11300        k_tok_bytes: usize,
11301        v_tok_bytes: usize,
11302        g: bool,
11303    ) -> Result<(), Box<dyn std::error::Error>> {
11304        Self::full_width_rope_only("rms_norm_qkv_rope_append", n_rot, head_dim)?;
11305        let rows = rq + rk + rk;
11306        let theta_scale = base.powf(-2.0 / head_dim as f32);
11307        let (nc, rqi, rki, nhq, nhk) = (
11308            head_dim as i32,
11309            rq as i32,
11310            rk as i32,
11311            nh_q as i32,
11312            nh_k as i32,
11313        );
11314        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
11315        let ti = t as i32;
11316        if Self::pdl_on() && Self::pdl_wb_on() {
11317            use cudarc::driver::{DevicePtr, DevicePtrMut};
11318            let s = &self.gpu.stream();
11319            let (p0, _a0) = q0.device_ptr(s);
11320            let (p1, _a1) = k0.device_ptr(s);
11321            let (p2, _a2) = v0.device_ptr(s);
11322            let (pwq, _a3) = wq.device_ptr(s);
11323            let (pwk, _a4) = wk.device_ptr(s);
11324            let (pwv, _a5) = wv.device_ptr(s);
11325            let (pq, _a6) = q.device_ptr_mut(s);
11326            let (pk, _a7) = k.device_ptr_mut(s);
11327            let (pv, _a8) = v.device_ptr_mut(s);
11328            let (pp, _a9) = pos.device_ptr(s);
11329            let pff: u64 = match ff {
11330                Some(t) => {
11331                    let (p, _gg) = t.device_ptr(s);
11332                    p as u64
11333                }
11334                None => 0,
11335            };
11336            let (pkc, _a10) = kc.device_ptr_mut(s);
11337            let (pvc, _a11) = vc.device_ptr_mut(s);
11338            let mut ps = [
11339                &p0 as *const _ as *mut std::ffi::c_void,
11340                &p1 as *const _ as *mut _,
11341                &p2 as *const _ as *mut _,
11342                &pwq as *const _ as *mut _,
11343                &pwk as *const _ as *mut _,
11344                &pwv as *const _ as *mut _,
11345                &pq as *const _ as *mut _,
11346                &pk as *const _ as *mut _,
11347                &pv as *const _ as *mut _,
11348                &nc as *const _ as *mut _,
11349                &rqi as *const _ as *mut _,
11350                &rki as *const _ as *mut _,
11351                &pp as *const _ as *mut _,
11352                &nhq as *const _ as *mut _,
11353                &nhk as *const _ as *mut _,
11354                &theta_scale as *const _ as *mut _,
11355                &freq_scale as *const _ as *mut _,
11356                &pff as *const _ as *mut _,
11357                &eps as *const _ as *mut _,
11358                &pkc as *const _ as *mut _,
11359                &pvc as *const _ as *mut _,
11360                &ti as *const _ as *mut _,
11361                &ktb as *const _ as *mut _,
11362                &vtb as *const _ as *mut _,
11363            ];
11364            unsafe {
11365                self.launch_pdl_flash(
11366                    g,
11367                    "rms_norm_qkv_rope_append_f32",
11368                    (rows as u32, 1, 1),
11369                    (rms_block(), 1, 1),
11370                    0,
11371                    &mut ps,
11372                )?;
11373            }
11374            return Ok(());
11375        }
11376        let f = if g {
11377            self.func_g("rms_norm_qkv_rope_append_f32")
11378        } else {
11379            self.func("rms_norm_qkv_rope_append_f32")
11380        };
11381        let cfg = LaunchConfig {
11382            grid_dim: (rows as u32, 1, 1),
11383            block_dim: (rms_block(), 1, 1),
11384            shared_mem_bytes: 0,
11385        };
11386        let __s_b = self.gpu.stream();
11387        let mut b = __s_b.launch_builder(&f);
11388        let null: u64 = 0;
11389        b.arg(q0)
11390            .arg(k0)
11391            .arg(v0)
11392            .arg(wq)
11393            .arg(wk)
11394            .arg(wv)
11395            .arg(&mut *q)
11396            .arg(&mut *k)
11397            .arg(&mut *v)
11398            .arg(&nc)
11399            .arg(&rqi)
11400            .arg(&rki)
11401            .arg(pos)
11402            .arg(&nhq)
11403            .arg(&nhk)
11404            .arg(&theta_scale)
11405            .arg(&freq_scale);
11406        match ff {
11407            Some(t) => {
11408                b.arg(t);
11409            }
11410            None => {
11411                b.arg(&null);
11412            }
11413        }
11414        b.arg(&eps)
11415            .arg(&mut *kc)
11416            .arg(&mut *vc)
11417            .arg(&ti)
11418            .arg(&ktb)
11419            .arg(&vtb);
11420        unsafe {
11421            b.launch(cfg)?;
11422        }
11423        Ok(())
11424    }
11425
11426    pub fn add_q8_1(
11427        &self,
11428        a: &CudaSlice<f32>,
11429        b: &CudaSlice<f32>,
11430        res: &mut CudaSlice<f32>,
11431        ncols: usize,
11432        nrows: usize,
11433    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11434        debug_assert!(ncols % 128 == 0);
11435        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
11436        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
11437        let f = self.func("add_q8_1_f32");
11438        let cfg = LaunchConfig {
11439            grid_dim: (nrows as u32, 1, 1),
11440            block_dim: (rms_block(), 1, 1),
11441            shared_mem_bytes: 0,
11442        };
11443        let nc = ncols as i32;
11444        let __s_b2 = self.gpu.stream();
11445        let mut b2 = __s_b2.launch_builder(&f);
11446        b2.arg(a)
11447            .arg(b)
11448            .arg(&mut *res)
11449            .arg(&mut out_q)
11450            .arg(&mut out_d)
11451            .arg(&nc);
11452        unsafe {
11453            b2.launch(cfg)?;
11454        }
11455        Ok((out_q, out_d))
11456    }
11457
11458    /// E4B FFN-tail exit fusion (glue wave 5): resid = b + rms(a, wa) emitted f32 + q8_1 pair
11459    /// in ONE launch — replaces rms_norm(a,wa->sn) + add_q8_1(sn,b). Same rms_block() config
11460    /// as both parents (bit-identity: identical reduction + quad-walk quantize).
11461    pub fn rms_pre_add_q8_1(
11462        &self,
11463        a: &CudaSlice<f32>,
11464        wa: &CudaSlice<f32>,
11465        b: &CudaSlice<f32>,
11466        res: &mut CudaSlice<f32>,
11467        ncols: usize,
11468        nrows: usize,
11469        eps: f32,
11470    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11471        debug_assert!(ncols % 128 == 0);
11472        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
11473        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
11474        let f = self.func("rms_pre_add_q8_1_f32");
11475        let cfg = LaunchConfig {
11476            grid_dim: (nrows as u32, 1, 1),
11477            block_dim: (rms_block(), 1, 1),
11478            shared_mem_bytes: 0,
11479        };
11480        let (nc, ep) = (ncols as i32, eps);
11481        let __s_b2 = self.gpu.stream();
11482        let mut b2 = __s_b2.launch_builder(&f);
11483        b2.arg(a)
11484            .arg(wa)
11485            .arg(b)
11486            .arg(&mut *res)
11487            .arg(&mut out_q)
11488            .arg(&mut out_d)
11489            .arg(&nc)
11490            .arg(&ep);
11491        unsafe {
11492            b2.launch(cfg)?;
11493        }
11494        Ok((out_q, out_d))
11495    }
11496
11497    /// L2 norm per row (head_dim), no weight.
11498    /// PREFILL l2 dispatch (round 27): the warp-per-row float4 v2 when the numeric-config
11499    /// seam allows (MEMRA_L2_V2, default ON, d_state==128 only); else the strided kernel.
11500    pub fn l2_v2_on(ncols: usize) -> bool {
11501        ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
11502    }
11503
11504    pub fn l2_norm_pp(
11505        &self,
11506        x: &CudaSlice<f32>,
11507        dst: &mut CudaSlice<f32>,
11508        dst16: Option<&mut CudaSlice<u8>>,
11509        ncols: usize,
11510        nrows: usize,
11511        eps: f32,
11512    ) -> Result<(), Box<dyn std::error::Error>> {
11513        if Self::l2_v2_on(ncols) {
11514            let f = self.func("l2_norm_pp_v2_f32");
11515            let rows_per_block = 8u32; // 256 threads = 8 warps = 8 rows
11516            let cfg = LaunchConfig {
11517                grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
11518                block_dim: (256, 1, 1),
11519                shared_mem_bytes: 0,
11520            };
11521            let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
11522            // mirror-fold: bf16 twin address by value (0 = skip; matches the nullable param)
11523            let d16: u64 = match dst16 {
11524                Some(d) => self.addr_u8(d),
11525                None => 0,
11526            };
11527            let __s_b = self.gpu.stream();
11528            let mut b = __s_b.launch_builder(&f);
11529            b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
11530            unsafe {
11531                b.launch(cfg)?;
11532            }
11533            return Ok(());
11534        }
11535        self.l2_norm(x, dst, ncols, nrows, eps)
11536    }
11537
11538    pub fn l2_norm(
11539        &self,
11540        x: &CudaSlice<f32>,
11541        dst: &mut CudaSlice<f32>,
11542        ncols: usize,
11543        nrows: usize,
11544        eps: f32,
11545    ) -> Result<(), Box<dyn std::error::Error>> {
11546        let f = self.func("l2_norm_f32");
11547        let cfg = LaunchConfig {
11548            grid_dim: (nrows as u32, 1, 1),
11549            block_dim: (256, 1, 1),
11550            shared_mem_bytes: 0,
11551        };
11552        let (nc, e) = (ncols as i32, eps);
11553        let __s_b = self.gpu.stream();
11554        let mut b = __s_b.launch_builder(&f);
11555        b.arg(x).arg(dst).arg(&nc).arg(&e);
11556        unsafe {
11557            b.launch(cfg)?;
11558        }
11559        Ok(())
11560    }
11561
11562    /// L2-norm with blockDim=32 (warp-tree reduction) — BIT-IDENTICAL to gdn_prep_decode_f32's
11563    /// per-warp L2 norm. The verify path MUST use this to match decode's FP accumulation order:
11564    /// l2_norm at blockDim=256 produces a different shfl-tree reduction of the 128-element
11565    /// squared-sum (pairwise tree vs serial-4-then-warp-tree), causing ULP differences that
11566    /// propagate through gdn_scan and flip argmax on marginal logits.
11567    pub fn l2_norm_decode(
11568        &self,
11569        x: &CudaSlice<f32>,
11570        dst: &mut CudaSlice<f32>,
11571        ncols: usize,
11572        nrows: usize,
11573        eps: f32,
11574    ) -> Result<(), Box<dyn std::error::Error>> {
11575        let f = self.func("l2_norm_f32");
11576        let cfg = LaunchConfig {
11577            grid_dim: (nrows as u32, 1, 1),
11578            block_dim: (32, 1, 1),
11579            shared_mem_bytes: 0,
11580        };
11581        let (nc, e) = (ncols as i32, eps);
11582        let __s_b = self.gpu.stream();
11583        let mut b = __s_b.launch_builder(&f);
11584        b.arg(x).arg(dst).arg(&nc).arg(&e);
11585        unsafe {
11586            b.launch(cfg)?;
11587        }
11588        Ok(())
11589    }
11590
11591    /// RoPE NEOX in-place. x:[head_dim, n_heads, n_tokens], pos:[n_tokens].
11592    pub fn rope_neox(
11593        &self,
11594        x: &mut CudaSlice<f32>,
11595        pos: &CudaSlice<i32>,
11596        head_dim: usize,
11597        n_dims: usize,
11598        n_heads: usize,
11599        n_tokens: usize,
11600        freq_base: f32,
11601        freq_scale: f32,
11602    ) -> Result<(), Box<dyn std::error::Error>> {
11603        let f = self.func("rope_neox_f32");
11604        let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
11605        let grid = (n_heads * n_tokens) as u32;
11606        let cfg = LaunchConfig {
11607            grid_dim: (grid, 1, 1),
11608            block_dim: ((head_dim / 2) as u32, 1, 1),
11609            shared_mem_bytes: 0,
11610        };
11611        let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
11612        let __s_b = self.gpu.stream();
11613        let mut b = __s_b.launch_builder(&f);
11614        b.arg(x)
11615            .arg(pos)
11616            .arg(&hd)
11617            .arg(&nd)
11618            .arg(&nh)
11619            .arg(&theta_scale)
11620            .arg(&freq_scale);
11621        unsafe {
11622            b.launch(cfg)?;
11623        }
11624        Ok(())
11625    }
11626
11627    /// RoPE NEOX with per-dim freq factors (gemma4 global layers, rope_freqs.weight [n_dims/2]).
11628    pub fn rope_neox_ff(
11629        &self,
11630        x: &mut CudaSlice<f32>,
11631        pos: &CudaSlice<i32>,
11632        head_dim: usize,
11633        n_dims: usize,
11634        n_heads: usize,
11635        n_tokens: usize,
11636        freq_base: f32,
11637        freq_scale: f32,
11638        ff: &CudaSlice<f32>,
11639    ) -> Result<(), Box<dyn std::error::Error>> {
11640        let f = self.func("rope_neox_ff_f32");
11641        let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
11642        let grid = (n_heads * n_tokens) as u32;
11643        let cfg = LaunchConfig {
11644            grid_dim: (grid, 1, 1),
11645            block_dim: ((head_dim / 2) as u32, 1, 1),
11646            shared_mem_bytes: 0,
11647        };
11648        let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
11649        let __s_b = self.gpu.stream();
11650        let mut b = __s_b.launch_builder(&f);
11651        b.arg(x)
11652            .arg(pos)
11653            .arg(&hd)
11654            .arg(&nd)
11655            .arg(&nh)
11656            .arg(&theta_scale)
11657            .arg(&freq_scale)
11658            .arg(ff);
11659        unsafe {
11660            b.launch(cfg)?;
11661        }
11662        Ok(())
11663    }
11664
11665    /// gemma4: rope q and k in one launch (per-row chain = rope_neox / rope_neox_ff verbatim).
11666    #[allow(clippy::too_many_arguments)]
11667    pub fn rope_neox2(
11668        &self,
11669        q: &mut CudaSlice<f32>,
11670        k: &mut CudaSlice<f32>,
11671        pos: &CudaSlice<i32>,
11672        head_dim: usize,
11673        n_dims: usize,
11674        nh_q: usize,
11675        nh_k: usize,
11676        n_tokens: usize,
11677        freq_base: f32,
11678        freq_scale: f32,
11679        ff: Option<&CudaSlice<f32>>,
11680    ) -> Result<(), Box<dyn std::error::Error>> {
11681        let f = self.func("rope_neox2_f32");
11682        let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
11683        let grid = ((nh_q + nh_k) * n_tokens) as u32;
11684        let cfg = LaunchConfig {
11685            grid_dim: (grid, 1, 1),
11686            block_dim: ((head_dim / 2) as u32, 1, 1),
11687            shared_mem_bytes: 0,
11688        };
11689        let (hd, nd, nq, nk, nt) = (
11690            head_dim as i32,
11691            n_dims as i32,
11692            nh_q as i32,
11693            nh_k as i32,
11694            n_tokens as i32,
11695        );
11696        let __s_b = self.gpu.stream();
11697        let mut b = __s_b.launch_builder(&f);
11698        b.arg(q)
11699            .arg(k)
11700            .arg(pos)
11701            .arg(&hd)
11702            .arg(&nd)
11703            .arg(&nq)
11704            .arg(&nk)
11705            .arg(&nt)
11706            .arg(&theta_scale)
11707            .arg(&freq_scale);
11708        match ff {
11709            Some(ffv) => {
11710                b.arg(ffv);
11711                unsafe {
11712                    b.launch(cfg)?;
11713                }
11714            }
11715            None => {
11716                let null: u64 = 0;
11717                b.arg(&null);
11718                unsafe {
11719                    b.launch(cfg)?;
11720                }
11721            }
11722        }
11723        Ok(())
11724    }
11725
11726    /// gemma4 R1: dst = GELU_tanh(gate) * up.
11727    pub fn gelu_tanh_mul(
11728        &self,
11729        gate: &CudaSlice<f32>,
11730        up: &CudaSlice<f32>,
11731        dst: &mut CudaSlice<f32>,
11732        n: usize,
11733    ) -> Result<(), Box<dyn std::error::Error>> {
11734        let f = self.func("gelu_tanh_mul_f32");
11735        let cfg = LaunchConfig::for_num_elems(n as u32);
11736        let ni = n as i32;
11737        let __s_b = self.gpu.stream();
11738        let mut b = __s_b.launch_builder(&f);
11739        b.arg(gate).arg(up).arg(dst).arg(&ni);
11740        unsafe {
11741            b.launch(cfg)?;
11742        }
11743        Ok(())
11744    }
11745
11746    pub fn silu_mul(
11747        &self,
11748        gate: &CudaSlice<f32>,
11749        up: &CudaSlice<f32>,
11750        dst: &mut CudaSlice<f32>,
11751        n: usize,
11752    ) -> Result<(), Box<dyn std::error::Error>> {
11753        let f = self.func("silu_mul_f32");
11754        // float4 kernel: one thread per 4 elements (tail handled in-kernel)
11755        let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
11756        let ni = n as i32;
11757        let __s_b = self.gpu.stream();
11758        let mut b = __s_b.launch_builder(&f);
11759        b.arg(gate).arg(up).arg(dst).arg(&ni);
11760        unsafe {
11761            b.launch(cfg)?;
11762        }
11763        Ok(())
11764    }
11765
11766    /// SwiGLU twin using Memra's host-matching expf transcription.
11767    pub fn silu_mul_host_expf(
11768        &self,
11769        gate: &CudaSlice<f32>,
11770        up: &CudaSlice<f32>,
11771        dst: &mut CudaSlice<f32>,
11772        n: usize,
11773    ) -> Result<(), Box<dyn std::error::Error>> {
11774        let f = self.func("silu_mul_host_expf_f32");
11775        let cfg = LaunchConfig::for_num_elems(n as u32);
11776        let ni = n as i32;
11777        let __s_b = self.gpu.stream();
11778        let mut b = __s_b.launch_builder(&f);
11779        b.arg(gate).arg(up).arg(dst).arg(&ni);
11780        unsafe {
11781            b.launch(cfg)?;
11782        }
11783        Ok(())
11784    }
11785
11786    /// Step routed-expert clamp twin using Memra's host-matching expf transcription.
11787    pub fn silu_clamped_mul_host_expf(
11788        &self,
11789        gate: &CudaSlice<f32>,
11790        up: &CudaSlice<f32>,
11791        limit: f32,
11792        dst: &mut CudaSlice<f32>,
11793        n: usize,
11794    ) -> Result<(), Box<dyn std::error::Error>> {
11795        if !limit.is_finite() || limit <= 0.0 {
11796            return Err(
11797                format!("Step routed-expert clamp limit must be positive, got {limit}").into(),
11798            );
11799        }
11800        let f = self.func("silu_clamped_mul_host_expf_f32");
11801        let cfg = LaunchConfig::for_num_elems(n as u32);
11802        let ni = n as i32;
11803        let __s_b = self.gpu.stream();
11804        let mut b = __s_b.launch_builder(&f);
11805        b.arg(gate).arg(up).arg(&limit).arg(dst).arg(&ni);
11806        unsafe {
11807            b.launch(cfg)?;
11808        }
11809        Ok(())
11810    }
11811
11812    /// f16out twin of `silu_mul` (task #17): the epilogue also emits the fp16 GEMM operand
11813    /// for the down projection — kills the standalone convert pass. Bit-identical class.
11814    pub fn silu_mul_f16out(
11815        &self,
11816        gate: &CudaSlice<f32>,
11817        up: &CudaSlice<f32>,
11818        dst: &mut CudaSlice<f32>,
11819        dst16: &mut CudaSlice<u8>,
11820        n: usize,
11821    ) -> Result<(), Box<dyn std::error::Error>> {
11822        let f = self.func("silu_mul_f16out_f32");
11823        let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
11824        let ni = n as i32;
11825        let __s_b = self.gpu.stream();
11826        let mut b = __s_b.launch_builder(&f);
11827        b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
11828        unsafe {
11829            b.launch(cfg)?;
11830        }
11831        Ok(())
11832    }
11833
11834    /// FFN SwiGLU epilogue fusion (RANK3 LEVER 2): `dst = silu(gate*gs) * (up*us)` in ONE launch,
11835    /// folding the per-tensor NVFP4 macro-scale (`gs`,`us`) that would otherwise be two separate
11836    /// `scale_inplace` launches on the gate/up matmul outputs. BIT-IDENTICAL to
11837    /// scale_inplace(gate,gs); scale_inplace(up,us); silu_mul(gate,up,dst) — identical float ops in
11838    /// identical order. For non-NVFP4 weights gs==us==1.0 -> identical to `silu_mul`. Net: -2
11839    /// launches per dense FFN layer (the gate+up post-matmul scales).
11840    pub fn silu_mul_scaled(
11841        &self,
11842        gate: &CudaSlice<f32>,
11843        up: &CudaSlice<f32>,
11844        gs: f32,
11845        us: f32,
11846        dst: &mut CudaSlice<f32>,
11847        n: usize,
11848    ) -> Result<(), Box<dyn std::error::Error>> {
11849        let f = self.func("silu_mul_scaled_f32");
11850        let cfg = LaunchConfig::for_num_elems(n as u32);
11851        let ni = n as i32;
11852        let (gsf, usf) = (gs, us);
11853        let __s_b = self.gpu.stream();
11854        let mut b = __s_b.launch_builder(&f);
11855        b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
11856        unsafe {
11857            b.launch(cfg)?;
11858        }
11859        Ok(())
11860    }
11861
11862    /// swigluoai (MiniMax-M3 / GPT-OSS): clamped SwiGLU epilogue, math 1:1 vs llama.cpp
11863    /// ggml_cuda_op_swiglu_oai_single. `dst = swish_alpha(clamp(gate*gs)) * (1 + clamp(up*us))`.
11864    /// gs/us fold the NVFP4 macro-scales exactly like `silu_mul_scaled`.
11865    #[allow(clippy::too_many_arguments)]
11866    pub fn swigluoai_mul_scaled(
11867        &self,
11868        gate: &CudaSlice<f32>,
11869        up: &CudaSlice<f32>,
11870        gs: f32,
11871        us: f32,
11872        alpha: f32,
11873        limit: f32,
11874        dst: &mut CudaSlice<f32>,
11875        n: usize,
11876    ) -> Result<(), Box<dyn std::error::Error>> {
11877        let f = self.func("swigluoai_mul_scaled_f32");
11878        let cfg = LaunchConfig::for_num_elems(n as u32);
11879        let ni = n as i32;
11880        let __s_b = self.gpu.stream();
11881        let mut b = __s_b.launch_builder(&f);
11882        b.arg(gate)
11883            .arg(up)
11884            .arg(&gs)
11885            .arg(&us)
11886            .arg(&alpha)
11887            .arg(&limit)
11888            .arg(dst)
11889            .arg(&ni);
11890        unsafe {
11891            b.launch(cfg)?;
11892        }
11893        Ok(())
11894    }
11895
11896    /// RANK2 LEVER (q8_1 quant-fold): SwiGLU epilogue that EMITS the q8_1 quantization of `act`
11897    /// directly (aq int8 [n] + ad f32 [n/32]), so ffn_down's standalone `quantize_q8_1` launch is
11898    /// removed — the down-proj activation has one consumer, so the quant folds into the producer for
11899    /// free (no extra HBM read; no f32 `act` write). gs/us fold the gate/up NVFP4 macro-scales like
11900    /// `silu_mul_scaled`. BIT-IDENTICAL q8_1 to silu_mul_scaled(...) then quantize_q8_1(...). Only
11901    /// valid when ffn_down uses the q8_1 dp4a/mmvq path; the caller checks `uses_q8_1_fast(ffn_down)`.
11902    /// n must be a multiple of 32 (n_ff always is).
11903    pub fn silu_mul_scaled_q8_1(
11904        &self,
11905        gate: &CudaSlice<f32>,
11906        up: &CudaSlice<f32>,
11907        gs: f32,
11908        us: f32,
11909        n: usize,
11910    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11911        let f = self.func("silu_mul_scaled_q8_1");
11912        let nblk = n / 32;
11913        let mut aq = self.alloc_uninit::<i8>(n)?; // full-overwrite output
11914        let mut ad = self.alloc_uninit::<f32>(nblk)?; // full-overwrite output
11915        // WARP-PER-BLOCK kernel: one warp (32 lanes) per 32-block -> n threads total.
11916        let cfg = LaunchConfig::for_num_elems(n as u32);
11917        let (gsf, usf, ni) = (gs, us, n as i32);
11918        let __s_b = self.gpu.stream();
11919        let mut b = __s_b.launch_builder(&f);
11920        b.arg(gate)
11921            .arg(up)
11922            .arg(&gsf)
11923            .arg(&usf)
11924            .arg(&mut aq)
11925            .arg(&mut ad)
11926            .arg(&ni);
11927        unsafe {
11928            b.launch(cfg)?;
11929        }
11930        Ok((aq, ad))
11931    }
11932
11933    pub fn add(
11934        &self,
11935        a: &CudaSlice<f32>,
11936        b_in: &CudaSlice<f32>,
11937        dst: &mut CudaSlice<f32>,
11938        n: usize,
11939    ) -> Result<(), Box<dyn std::error::Error>> {
11940        let f = self.func("add_f32");
11941        // float4 kernel: one thread per 4 elements (tail handled in-kernel)
11942        let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
11943        let ni = n as i32;
11944        let __s_bld = self.gpu.stream();
11945        let mut bld = __s_bld.launch_builder(&f);
11946        bld.arg(a).arg(b_in).arg(dst).arg(&ni);
11947        unsafe {
11948            bld.launch(cfg)?;
11949        }
11950        Ok(())
11951    }
11952
11953    pub fn mul(
11954        &self,
11955        a: &CudaSlice<f32>,
11956        b_in: &CudaSlice<f32>,
11957        dst: &mut CudaSlice<f32>,
11958        n: usize,
11959    ) -> Result<(), Box<dyn std::error::Error>> {
11960        let f = self.func("mul_f32");
11961        let cfg = LaunchConfig::for_num_elems(n as u32);
11962        let ni = n as i32;
11963        let __s_bld = self.gpu.stream();
11964        let mut bld = __s_bld.launch_builder(&f);
11965        bld.arg(a).arg(b_in).arg(dst).arg(&ni);
11966        unsafe {
11967            bld.launch(cfg)?;
11968        }
11969        Ok(())
11970    }
11971
11972    /// Unified weight-tensor matmul: dispatches quant tensors to qmatvec (weights packed) and
11973    /// float tensors to cuBLASLt. y[m,out] = x[m,in] @ W[out,in]^T.
11974    pub fn matmul(
11975        &self,
11976        w: &crate::model::GpuTensor,
11977        x: &CudaSlice<f32>,
11978        m: usize,
11979    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11980        use crate::model::GpuTensor;
11981        let in_f = w.in_features();
11982        let out_f = w.out_features();
11983        // PREFILL (T>1) ROOT FIX: batched tensor-core int8 GEMM. Decodes each weight tile to int8
11984        // in smem ONCE and reuses across all tokens via mma — vs the dp4a matvec's per-token weight
11985        // re-read. Only the 4 daily-hot dtypes; m=1 decode keeps dp4a (it's bandwidth-bound, mma
11986        // gives nothing). Quantize the activation once here then call the GEMM.
11987        // m cutoff FIXED at 16: the m=4 MMA-verify A/B (2026-07-06, was MEMRA_GEMM_M) measured
11988        // NEGATIVE — the MMA tile grid starves at m=4 (BN=256 -> grid.y=1) and its FP order
11989        // shifted verify argmax at tight margins. Do not lower without re-running that battery.
11990        #[allow(non_snake_case)]
11991        // VERIFY-EXACT scope pushes the GEMM crossover out of reach (usize::MAX) — the
11992        // t>=16 dflash verify must ride the decode-exact batched class (parity law).
11993        let GEMM_M_THRESHOLD = if self.verify_exact_on() {
11994            usize::MAX
11995        } else {
11996            16usize
11997        };
11998
11999        // PREFILL GEMM (m>=16). ACCURACY-FIRST dispatch (2026-06-28, prefill-gemm-beat-research wf
12000        // wllbyo6vc step 1): the int8 W4A8 GEMM (qmatvec_gemm, q8_1 activation, s32 accumulate) is
12001        // ACCURATE (prefill logit maxdiff 0.159, < dp4a 0.55) and the default. The FP4 W4A4 mxf4 path
12002        // (try_fp4_gemm) quantizes the ACTIVATION to e2m1 4-bit (8 magnitude levels) -> maxdiff 1.0
12003        // when combined — a real accuracy loss, NOT a math bug. So FP4-W4A4 is taken ONLY under the
12004        // explicit MEMRA_FP4 opt-in AND it must come SECOND (int8 W4A8 is the correct default for NVFP4).
12005        // The workflow plan rebuilds the FP4 path (kill per-K repack, widen K, deepen pipeline, TMA) to
12006        // be both fast AND accurate; until then NVFP4 prefill defaults to the accurate int8 GEMM.
12007        // TINY-OUT_F GUARD (2026-06-28, ncu trace): the tiling GEMM's grid is (ceil(out_f/BM=64),
12008        // ceil(m/BN=256)). For tiny out_f (ssm_beta/ssm_alpha out_f=num_v_heads~32), grid.x=1 -> only
12009        // ceil(m/256) CTAs (e.g. 2 for m=512) on 82 SMs = 0.39% SM throughput, 852us EACH (measured
12010        // worst offender). The dp4a path grids (out_f, m) = far more CTAs, filling the GPU. So route
12011        // out_f < 2*BM to dp4a (skip the tiling GEMM which structurally can't fill the SMs here).
12012        const GEMM_MIN_OUT_F: usize = 128; // 2*BM; below this the GEMM grid.x starves the 82 SMs
12013        // VENDORED llama MMQ prefill GEMMs. NVFP4 W4A8 is DEFAULT-ON (2026-07-05 flip: same int8
12014        // accuracy class as the int8 GEMM below at ~1.9x pp512, rp-loader coexists with the A6
12015        // repack; MEMRA_MMQ_W4A8=0 = escape hatch). W4A4 mxf4nvf4 + Q4_K/Q5_K stay behind MEMRA_MMQ=1.
12016        // The env policy lives in mmq_supports/qmatvec_mmq. Feeds raw f32 activation `x` (the
12017        // launcher quantizes internally). out_f>=MMQ_Y/2 keeps the tile grid from starving the SMs.
12018        // FP8-ACT PREFILL (MEMRA_PP_FP8=1, probe verdict 2026-07-08): F8-E4M3-origin projections
12019        // carry their raw e4m3 device bytes (the `fp8` operand stashed at load next to the Q8_0
12020        // re-encode) — cuBLASLt FP8 TN at 620-795 TF vs 47-72 TF for this class's int8 GEMM.
12021        // Weight side EXACT (checkpoint bytes); activation rides ONE per-batch e4m3 scale
12022        // (amax/448) folded with weight_scale in-GEMM. Prefill only; decode keeps Q8_0 untouched.
12023        if m >= GEMM_M_THRESHOLD {
12024            if let Some(y) = self.try_fp8_gemm(w, x, m)? {
12025                return Ok(y);
12026            }
12027            // PER-BLOCK FP8 MMQ (lane/fp8-mmq): the block-128 class try_fp8_gemm skips (cuBLASLt
12028            // takes no block grid on sm_120). Exact per block — the checkpoint's e4m3 bytes and its
12029            // f32 grid go into the tile unchanged. TWO SOURCES, TWO DEFAULTS: the load-time stash is
12030            // opt-in (MEMRA_FP8_MMQ=1), the native-resident QT_F8_E4M3_BLK grid is DEFAULT ON
12031            // (MEMRA_FP8_MMQ=0 reverts it to dequant-per-call) — see fp8_ffi.rs for why the same
12032            // tile defaults differently by operand source.
12033            if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
12034                return Ok(y);
12035            }
12036            // FP16-mirror prefill (MEMRA_PP_F16=1, probe 2026-07-26: 3.2-3.7x the MMQ class).
12037            // Mirror presence IS the gate (only built under the env). Decode never reaches here.
12038            if let Some(y) = self.try_f16_gemm(w, x, m)? {
12039                return Ok(y);
12040            }
12041        }
12042        // F8-E4M3 BLOCK-128 (QT_F8_E4M3_BLK, lane/fp8-blk128-decode). TWO arms, split at the SAME
12043        // m threshold the rest of this method uses:
12044        //   * m >= threshold (prefill): dequant-per-call to the ARM B' Q8_0 slab and recurse, so
12045        //     prefill keeps the floor's kernels AND the floor's bits (try_e4m3_blk_prefill).
12046        //   * m <  threshold: the native per-block GEMV — m=1 decode and the m=2..15 verify tiers.
12047        //     grid.y=m runs the exact m=1 program per (token,row), so the decode-parity law holds
12048        //     across every tier by construction with no batched twin needed.
12049        //
12050        // NOT gated on `fast`: this dtype has no dp4a twin and no Stage-A f32-dequant oracle (the
12051        // generic `deq()` switch has no block-scale input), exactly as QT_F8_E4M3 has none, so
12052        // MEMRA_FAST=0 cannot route it anywhere else. Placed before every GEMM/MMQ arm below
12053        // because gemm_supports/mmq_supports/mmvq_supports all deliberately REFUSE this qtype —
12054        // reaching the generic tail would panic rather than produce wrong numbers, and this pair of
12055        // arms is what makes sure it never gets there.
12056        if let GpuTensor::Quant { qtype, .. } = w {
12057            if *qtype == QT_F8_E4M3_BLK {
12058                if m >= GEMM_M_THRESHOLD {
12059                    if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
12060                        return Ok(y);
12061                    }
12062                }
12063                let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12064                if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
12065                    return Ok(y);
12066                }
12067            }
12068        }
12069        if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
12070            return self.qmatvec_mmq(w, x, m);
12071        }
12072        if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
12073            let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12074            return self.qmatvec_gemm(w, &aq, &ad, m);
12075        }
12076        // FP4 W4A4 only as an explicit speed/accuracy tradeoff opt-in, and only if the int8 GEMM
12077        // above didn't already handle this weight (e.g. NVFP4 with in_f%64!=0, or MEMRA_NO_GEMM set).
12078        if m >= GEMM_M_THRESHOLD {
12079            if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
12080                return Ok(y);
12081            }
12082        }
12083        // Stage-B fast int8 dp4a is the DEFAULT since 2026-07-08 (it has been the daily path
12084        // for weeks; the old opt-in flag was a silent-slow-path landmine). MEMRA_FAST=0 reverts
12085        // to Stage-A f32-dequant (the correctness oracle path).
12086        let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
12087        // PERF-3 decode-GEMV: m=1 warp-per-row MMVQ (MEMRA_MMVQ). The big decode matvecs reach
12088        // `matmul` directly (ffn_down, lm_head output, wo), so route them here too — not only the
12089        // matmul_pre siblings. qmatvec_mmvq_raw quantizes the activation internally (q8_1) like the
12090        // _fast paths; the NVFP4 macro-scale is applied by the `scale != 1.0` block below.
12091        if m == 1 && fast {
12092            if let GpuTensor::Quant {
12093                bytes,
12094                qtype,
12095                row_bytes,
12096                rp,
12097                rp4,
12098                scale,
12099                ..
12100            } = w
12101            {
12102                if self.mmvq_supports(*qtype) {
12103                    // NVFP4 macro-scale rides the kernel's fused epilogue arg (one launch total);
12104                    // non-NVFP4 has scale==1.0 so qmatvec_mmvq skips scale_inplace either way.
12105                    // Q4_0 split-plane mirror (rp4): the decode arm reads it via the _rp twins.
12106                    let (bytes, rp) = match rp4 {
12107                        Some(m4) => (m4, true),
12108                        None => (bytes, *rp),
12109                    };
12110                    let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12111                    return self.qmatvec_mmvq(
12112                        bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
12113                    );
12114                }
12115            }
12116        }
12117        // BATCHED weight-resident matvec for the m=2-4 band (the MTP/verify forward's ffn_down, wo, and
12118        // lm_head `output` reach `matmul` directly at m=T=2..4). Walks the weight ONCE, dp4a vs all m
12119        // activation columns -> 1 weight read for m tokens (vs grid.y=m re-reading m times below). Quant
12120        // the activation once here (q8_1) like the _fast paths; macro-scale applied via the scale!=1.0
12121        // block below. MEMRA_NO_BATCHED -> per-m path.
12122        //
12123        // DECODE-PARITY GATE (2026-07-07, the 9B synth K=3/4/6 spec FAIL root cause): the batched
12124        // kernels are bit-identical per (token,row) to MMVQ's 32-thread warp reduce, NOT to the
12125        // dp4a kernels' 128-thread two-level reduce. Without MEMRA_MMVQ the m=1 decode chain rides
12126        // dp4a, so a verify riding batched here has a DIFFERENT FP order than the decode it must
12127        // match bit-for-bit — greedy spec flips at tight-margin tokens (the old HANDOVER "ENV LAW:
12128        // FAST+MMVQ both required" footgun, closed here). Parity law: the m>1 kernel CLASS must be
12129        // a pure function of (dtype, env) equal to the m=1 class — batched iff MMVQ. Without MMVQ
12130        // the verify falls to the per-m grid.y=m dp4a path below (each column = the exact m=1
12131        // dp4a program). MEMRA_MMVQ=1 (the daily config) is dispatch-unchanged.
12132        if (2..=16).contains(&m)
12133            && fast
12134            && std::env::var("MEMRA_NO_BATCHED").is_err()
12135            && (m <= 4 || Self::b8_enabled())
12136        {
12137            // b16 tier (2026-07-11, spec K>7): Q4_0/Q6_K have base+_rp b16 kernels; Q8_0's
12138            // b16 exists only as the split-plane _rp twin, so it joins iff the q8rp mirror
12139            // is present (rp4) — the mirror pick below then routes to the _rp family.
12140            // QT_F8_E4M3 joins unconditionally (lane/rp-on-st): its b16 IS the base kernel,
12141            // because the native e4m3 row layout is already aligned and needs no mirror.
12142            // NVFP4/Q4_K/Q8_0 all join unconditionally now (lane/rp-on-st): each has base + _rp
12143            // b16 twins, so either residency layout has its aligned form at this width. Q8_0's
12144            // old `rp4.is_some()` precondition is GONE — the mirror is a bandwidth lever, not the
12145            // exact tier's admission ticket (it was refusing FP8-ST over 23.9 MiB of ssm_beta).
12146            let m_ok = m <= 8
12147                || matches!(w, GpuTensor::Quant { qtype, .. }
12148                if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
12149                    || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
12150            if m_ok {
12151                if let GpuTensor::Quant {
12152                    bytes,
12153                    qtype,
12154                    row_bytes,
12155                    rp,
12156                    rp4,
12157                    ..
12158                } = w
12159                {
12160                    if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
12161                        let (bytes, rp) = match rp4 {
12162                            Some(m4) => (m4, true),
12163                            None => (bytes, *rp),
12164                        };
12165                        let mcols = Self::batched_mcols(m);
12166                        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12167                        let mut y = self.qmatvec_mmvq_batched(
12168                            bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
12169                        )?;
12170                        if let GpuTensor::Quant { scale, .. } = w {
12171                            if *scale != 1.0 {
12172                                self.scale_inplace(&mut y, *scale, m * out_f)?;
12173                            }
12174                        }
12175                        return Ok(y);
12176                    }
12177                }
12178            }
12179        }
12180        // F8-E4M3 (MEMRA_ST_E4M3) catch-all for the m<16 band the arms above didn't take (m=9..15,
12181        // the K=8 verify tier; or m=2..8 under MEMRA_NO_BATCHED/MEMRA_B8=0): grid.y=m e4m3 mmvq —
12182        // the SAME per-(token,row) program as the m=1 decode launch (bit-identical by construction),
12183        // weight re-read m times (rare tier; exactness over bandwidth here). There is no _dp4a twin
12184        // for this dtype, so the generic match below must never see it under `fast`.
12185        if fast {
12186            if let GpuTensor::Quant {
12187                bytes,
12188                qtype,
12189                row_bytes,
12190                scale,
12191                ..
12192            } = w
12193            {
12194                if *qtype == QT_F8_E4M3 {
12195                    let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12196                    return self.qmatvec_mmvq(
12197                        bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
12198                    );
12199                }
12200            }
12201        }
12202        let mut y = match w {
12203            GpuTensor::Quant {
12204                bytes,
12205                qtype,
12206                row_bytes,
12207                ..
12208            } if fast && *qtype == QT_Q8_0 => {
12209                self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
12210            }
12211            GpuTensor::Quant {
12212                bytes,
12213                qtype,
12214                row_bytes,
12215                ..
12216            } if fast && *qtype == QT_Q4_K => {
12217                self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
12218            }
12219            GpuTensor::Quant {
12220                bytes,
12221                qtype,
12222                row_bytes,
12223                ..
12224            } if fast && *qtype == QT_Q6_K => {
12225                self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
12226            }
12227            GpuTensor::Quant {
12228                bytes,
12229                qtype,
12230                row_bytes,
12231                ..
12232            } if fast && *qtype == QT_Q5_K => {
12233                self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
12234            }
12235            GpuTensor::Quant {
12236                bytes,
12237                qtype,
12238                row_bytes,
12239                ..
12240            } if fast && *qtype == QT_Q3_K => {
12241                self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
12242            }
12243            GpuTensor::Quant {
12244                bytes,
12245                qtype,
12246                row_bytes,
12247                rp,
12248                ..
12249            } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
12250                if *rp {
12251                    "qmatvec_nvfp4_dp4a_rp"
12252                } else {
12253                    "qmatvec_nvfp4_dp4a"
12254                },
12255                &bytes.slice(0..bytes.len()),
12256                x,
12257                m,
12258                in_f,
12259                out_f,
12260                *row_bytes,
12261            )?,
12262            // IQ4_XS trunk fast path — DEFAULT ON since 2026-08-02 (MEMRA_IQ_FAST=0 reverts to
12263            // Stage-A; see iq_fast_enabled). The old opt-in default was the KAT-Coder decode
12264            // anomaly (research/kat-anomaly-20260802/).
12265            GpuTensor::Quant {
12266                bytes,
12267                qtype,
12268                row_bytes,
12269                ..
12270            } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
12271                self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
12272            }
12273            // B3: IQ3_S uses the Stage-A f32 dequant-in-kernel path. There is NO
12274            // qmatvec_iq3_s_dp4a kernel — do NOT add a `*qtype == QT_IQ3_S` fast guard here
12275            // without first writing the matching kernel, or func() will panic
12276            // "kernel ... not in any fatbin".
12277            GpuTensor::Quant {
12278                bytes,
12279                qtype,
12280                row_bytes,
12281                rp,
12282                ..
12283            } =>
12284            // Stage-A generic: repacked NVFP4 uses the device-side split-plane tag (the
12285            // deq(row,j) form cannot address the planes; same value/product order).
12286            {
12287                self.qmatvec(
12288                    bytes,
12289                    x,
12290                    m,
12291                    in_f,
12292                    out_f,
12293                    if *rp && *qtype == QT_NVFP4 {
12294                        QT_NVFP4_RP
12295                    } else {
12296                        *qtype
12297                    },
12298                    *row_bytes,
12299                )?
12300            }
12301            GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
12302            // MEMRA_FULL_PREC bf16-resident weight: dequant-on-use to f32 scratch, then the same
12303            // cuBLASLt f32 GEMV as the Float arm.
12304            GpuTensor::FloatBf16 { data, .. } => {
12305                // DECODE-TIER ROWS FAST PATH (2 <= m <= 8, bf16-mmv class): the chunked
12306                // arm dequants the WHOLE weight to f32 scratch per call — 4.7 ms/call on
12307                // the 1.24 GB LM head (nsys: 8x591us bf16_to_f32 per batch tick / per
12308                // verify round). matvec_bf16_f32acc_x4_rows runs the t=1 decode head
12309                // program PER ROW (identical dot + reduce), so decode/verify tiers keep
12310                // the t=1 numeric class and skip the convert. Prefill (m>8) keeps GEMM.
12311                if (1..=32).contains(&m) && Self::bf16_mmv_on() && in_f % 8 == 0 {
12312                    let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12313                    self.matvec_bf16_rows_into(data, x, &mut y, in_f, out_f, m)?;
12314                    y
12315                } else {
12316                    self.linear_bf16_chunked(x, data, m, in_f, out_f, false, None)?
12317                }
12318            }
12319        };
12320        // NVFP4 per-tensor macro-scale (post-matmul). scale==1.0 for all other quants/float -> no-op.
12321        if let GpuTensor::Quant { scale, .. } = w {
12322            if *scale != 1.0 {
12323                self.scale_inplace(&mut y, *scale, m * out_f)?;
12324            }
12325        }
12326        Ok(y)
12327    }
12328
12329    /// True when `MEMRA_FAST=0`, i.e. the Stage-A f32 oracle is the requested arithmetic and every
12330    /// `matmul_pre` call will take the raw-f32 escape rather than the q8_1 pair.
12331    ///
12332    /// WHY THIS EXISTS AS ITS OWN PREDICATE: `uses_q8_1_fast` needs a weight, but the callers that
12333    /// have to DECIDE WHETHER TO MATERIALIZE an f32 activation sit one level above any weight — the
12334    /// gemma-4 decode trunk emits a q8_1 pair per layer and hands it down, so the f32 has to be
12335    /// produced (or not) by the loop that owns the residual. Cached in a OnceLock like every other
12336    /// `*_on()` flag here: this is read once per layer per token on the decode path, and the daily
12337    /// path must not pay an env lookup for a flag that is off.
12338    pub fn stage_a_raw_needed() -> bool {
12339        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12340        *ON.get_or_init(|| std::env::var("MEMRA_FAST").as_deref() == Ok("0"))
12341    }
12342
12343    /// True if `w` would take the int8-dp4a fast path under MEMRA_FAST (so its activation can be
12344    /// pre-quantized once and shared across sibling matmuls via `matmul_pre`).
12345    pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
12346        use crate::model::GpuTensor;
12347        if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
12348            return false;
12349        }
12350        match w {
12351            // QT_F8_E4M3_BLK is admitted for the same reason QT_F8_E4M3 is: its ONLY kernel class
12352            // takes the shared q8_1 activation, so callers may pre-quantize once and share it
12353            // across siblings. It is NOT admitted to any of the fused/dual epilogue doors those
12354            // siblings can then open (`q8_fused_params`, `e4m3_fused_params` and
12355            // `matmul_pre_dual_noscale` all match on their own qtype and refuse this one) — the
12356            // block class has no fused twin yet, so each of its projections takes its own launch.
12357            GpuTensor::Quant { qtype, .. } => {
12358                matches!(
12359                    *qtype,
12360                    QT_Q8_0
12361                        | QT_Q4_K
12362                        | QT_Q6_K
12363                        | QT_Q5_K
12364                        | QT_Q3_K
12365                        | QT_NVFP4
12366                        | QT_F8_E4M3
12367                        | QT_F8_E4M3_BLK
12368                        | QT_Q4_0
12369                ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
12370            }
12371            GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
12372        }
12373    }
12374
12375    /// matmul with a PRE-QUANTIZED q8_1 activation (aq,ad from `quantize_q8_1`). Skips the
12376    /// per-matmul re-quantize so sibling matmuls that share an input (gate+up share `z`;
12377    /// q/k/v + wqkv/gate/beta/alpha share `h`) quantize ONCE. Caller MUST have checked
12378    /// `uses_q8_1_fast(w)`; falls back to plain `matmul` otherwise (Stage-A / Float / non-fast).
12379    pub fn matmul_pre(
12380        &self,
12381        w: &crate::model::GpuTensor,
12382        aq: &CudaSlice<i8>,
12383        ad: &CudaSlice<f32>,
12384        x_fallback: &CudaSlice<f32>,
12385        m: usize,
12386    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12387        use crate::model::GpuTensor;
12388        // Every raw-f32 arm below (fp8/f16/MMQ/fp4) reads m*in_f from x_fallback. Callers that
12389        // pre-quantized and dropped the f32 input pass an EMPTY x_fallback (E4B's fusion port:
12390        // h = zeros(0)) — the length guard keeps those on the aq/ad GEMM instead of feeding a
12391        // 0-byte buffer to a convert kernel (illegal address -> cublasLt status 13; the E4B
12392        // rc=30013 dig, 2026-07-31).
12393        let x_raw_ok = x_fallback.len() >= m * w.in_features();
12394        // FP8-ACT PREFILL (MEMRA_PP_FP8=1): same arm as `matmul` — the fp8 operand needs the RAW
12395        // f32 activation (per-batch e4m3 quant differs from q8_1), so x_fallback not aq/ad.
12396        if m >= 16 && x_raw_ok && !self.verify_exact_on() {
12397            if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
12398                return Ok(y);
12399            }
12400            // PER-BLOCK FP8 MMQ — same arm as `matmul` (stash opt-in, native-resident default ON);
12401            // its own quantizer wants the RAW f32 activation, so x_fallback not aq/ad.
12402            if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
12403                return Ok(y);
12404            }
12405            // FP16-mirror prefill (same arm as `matmul` — fp16 wants the RAW f32 activation).
12406            if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
12407                return Ok(y);
12408            }
12409        }
12410        // BLOCK-128 e4m3 (QT_F8_E4M3_BLK) — the same two arms as `matmul`, split at the same m, and
12411        // placed at the same point in the order (after the prefill GEMM hooks, before every arm
12412        // that refuses this qtype). The prefill arm needs the RAW f32 activation for the Q8_0
12413        // dispatch it recurses into, so it takes x_fallback and is skipped when that is empty
12414        // (a pre-quantized caller that dropped its f32 input never runs at prefill m anyway).
12415        if m >= 16 && x_raw_ok && !self.verify_exact_on() {
12416            if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
12417                return Ok(y);
12418            }
12419        }
12420        if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12421            return Ok(y);
12422        }
12423        // VENDORED llama MMQ prefill GEMMs (NVFP4 W4A8 default-on; W4A4/k-quant behind MEMRA_MMQ=1
12424        // — policy in mmq_supports) — use the RAW f32 activation (their own internal quant:
12425        // q8_1 D4 for NVFP4 W4A8, FP8/UE4M3 for W4A4, q8_1 DS4 for Q4_K/Q5_K), so x_fallback not
12426        // aq/ad.
12427        if m >= 16
12428            && w.out_features() >= 128
12429            && self.mmq_supports(w)
12430            && !self.verify_exact_on()
12431            && x_raw_ok
12432        {
12433            return self.qmatvec_mmq(w, x_fallback, m);
12434        }
12435        // Stage-C FP4 prefill (MEMRA_FP4): native mxf4 GEMM needs the f32 activation (FP4-quant differs
12436        // from q8_1), so re-quantize from x_fallback rather than reuse aq/ad. NVFP4 only, m>=16.
12437        if m >= 16 && x_raw_ok && !self.verify_exact_on() {
12438            if let Some(y) =
12439                self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
12440            {
12441                return Ok(y);
12442            }
12443        }
12444        // Prefill GEMM root fix: if T>1 and the dtype has a GEMM kernel, batch via tensor cores
12445        // (reuses the already-quantized aq/ad — no extra quantize). m=1 falls through to dp4a.
12446        if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
12447            return self.qmatvec_gemm(w, aq, ad, m);
12448        }
12449        // THE SEVENTH ARM. Every raw-f32 arm above is guarded by `x_raw_ok`; this one — the
12450        // Stage-A / Float escape — was not, and it is the ONLY one `MEMRA_FAST=0` opens. So the
12451        // 2026-07-31 E4B rc=30013 fix (add the length guard, keep empty-fallback callers off the
12452        // raw-f32 arms) protected the six arms the FAST path can reach and missed the one the
12453        // ORACLE path reaches. Consequence before this guard: the gemma-4 decode arms, which pass
12454        // `e.zeros(0)` because their trunk emits only a q8_1 pair and never materializes an f32
12455        // attn-normed activation, fell through to Stage-A `qmatvec_f32` (cu/qmatvec.cu:5442),
12456        // which reads `m * in_f` floats out of a 0-byte allocation ->
12457        // CUDA_ERROR_ILLEGAL_ADDRESS at layer 0 of the first decode token. The fault is STICKY:
12458        // it poisons the context, so every LATER request in that process fails with an unrelated
12459        // message ("cache alloc failed: ...") and the true cause appears exactly once, in the
12460        // first failure. That is what made `MEMRA_FAST=0` — the reference named in
12461        // `tools/argmax-margin-gate.sh`'s own header — return an opaque HTTP 500 on the gemma
12462        // dense artifact and left the arm with no working truth instrument.
12463        //
12464        // Refuse loudly instead of reading out of bounds. A named error at the true call site is
12465        // strictly better than an illegal address surfacing later at an unrelated sync point, and
12466        // an oracle that cannot run must say so rather than corrupt the context it runs in.
12467        if !self.uses_q8_1_fast(w) {
12468            if !x_raw_ok {
12469                return Err(format!(
12470                    "matmul_pre: q8_1-fast is off for this weight but x_fallback holds {} f32 \
12471                     (need m*in_f = {}*{} = {}). This call site pre-quantized its activation and \
12472                     dropped the f32, so there is nothing to fall back to — pass the real f32 \
12473                     activation (see Engine::rms_norm_decode, which is bit-identical to \
12474                     rms_norm_q8_1's reduction) or keep the weight on the q8_1 path.",
12475                    x_fallback.len(),
12476                    m,
12477                    w.in_features(),
12478                    m * w.in_features()
12479                )
12480                .into());
12481            }
12482            return self.matmul(w, x_fallback, m);
12483        }
12484        let in_f = w.in_features();
12485        let out_f = w.out_features();
12486        let (bytes, qtype, row_bytes, scale, rp) = match w {
12487            GpuTensor::Quant {
12488                bytes,
12489                qtype,
12490                row_bytes,
12491                scale,
12492                rp,
12493                ..
12494            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12495            _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
12496        };
12497        // Q4_0 split-plane mirror: only the mmvq/batched decode arms read it (the _rp twins);
12498        // the dp4a/oracle tails below keep the raw GGUF bytes.
12499        let (mbytes, mrp) = match w {
12500            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12501            _ => (bytes, rp),
12502        };
12503        // PERF-3 decode-GEMV: warp-per-row MMVQ for the m=1 decode arm, gated behind MEMRA_MMVQ.
12504        // Only the 4 daily-hot dtypes have an _mmvq kernel (Q8_0/Q4_K/Q6_K/NVFP4); Q5_K/Q3_K/IQ4_XS
12505        // keep _dp4a (the oracle/fallback). Bit-equivalent to _dp4a up to f32 reduction order.
12506        if m == 1 && self.mmvq_supports(qtype) {
12507            return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
12508        }
12509        // BATCHED weight-resident matvec for the m=2-4 band (the MTP/verify forward: full_attn_verify
12510        // and decode_step_t run their projections at m=T=k=2..4). The plain _dp4a path below launches
12511        // grid.y=m INDEPENDENT blocks per output row -> the weight row is re-read m times from HBM/L2.
12512        // The _b2/_b4 kernels walk the weight ONCE and dp4a vs all m activation columns, so m tokens
12513        // cost ~1 weight read instead of m (decode is weight-BW-bound). BIT-IDENTICAL per (token,row)
12514        // to the _mmvq path (32-thread warp reduce — NOT the dp4a 128-thread reduce below).
12515        // m=2 -> mcols=2; m∈{3,4} -> mcols=4; m∈{5..8} -> mcols=8 (kernel guards c>=m).
12516        // MEMRA_NO_BATCHED forces the per-m grid.y=m path (the A/B reference); MEMRA_B8=0 keeps
12517        // m=5..8 on the old per-m path (b8-tier-only seam).
12518        // DECODE-PARITY GATE (2026-07-07): batched iff mmvq_supports — see matmul's parity note.
12519        // Without MEMRA_MMVQ, m=1 decode rides dp4a (the arm below at m=1); the verify must ride
12520        // the SAME class per column (grid.y=m dp4a = the exact m=1 dp4a program per column).
12521        if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
12522            && std::env::var("MEMRA_NO_BATCHED").is_err()
12523            && (m <= 4 || Self::b8_enabled())
12524            // b16 tier: every class routed here now has base + _rp b16 kernels (Q4_0/Q6_K
12525            // pre-existing; NVFP4/Q4_K/Q8_0-base/F8_E4M3 added lane/rp-on-st 2026-08-06), so
12526            // there is no mirror precondition left — `mrp` still selects the LAYOUT below.
12527            && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
12528                || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
12529        {
12530            let mcols = Self::batched_mcols(m);
12531            return self.qmatvec_mmvq_batched(
12532                mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
12533            );
12534        }
12535        // F8-E4M3 catch-all (m=9..15 / batched-disabled seams): grid.y=m e4m3 mmvq — this dtype
12536        // has NO _dp4a twin, and per (token,row) the mmvq body is the exact m=1 decode program.
12537        // Q4_0 joins the catch-all (2026-07-11): adaptive-K cap 8 makes verify t=9 reachable
12538        // for the first time (past the b8 tier) and Q4_0 has no dp4a twin either. The mirror
12539        // (mbytes/mrp) keeps the rp layout consistent with the m=1 decode program.
12540        if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
12541            let (b2, r2) = if qtype == QT_Q4_0 {
12542                (mbytes, mrp)
12543            } else {
12544                (bytes, rp)
12545            };
12546            return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
12547        }
12548        let name = match qtype {
12549            QT_Q8_0 => "qmatvec_q8_0_dp4a",
12550            QT_Q4_K => "qmatvec_q4_K_dp4a",
12551            QT_Q6_K => "qmatvec_q6_K_dp4a",
12552            QT_Q5_K => "qmatvec_q5_K_dp4a",
12553            QT_Q3_K => "qmatvec_q3_K_dp4a",
12554            QT_NVFP4 => {
12555                if rp {
12556                    "qmatvec_nvfp4_dp4a_rp"
12557                } else {
12558                    "qmatvec_nvfp4_dp4a"
12559                }
12560            }
12561            QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12562            _ => unreachable!(),
12563        };
12564        let f = self.func(name);
12565        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite GEMM output: skip memset
12566        let cfg = LaunchConfig {
12567            grid_dim: (out_f as u32, m as u32, 1),
12568            block_dim: (128, 1, 1),
12569            shared_mem_bytes: 0,
12570        };
12571        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12572        let __s_b = self.gpu.stream();
12573        let mut b = __s_b.launch_builder(&f);
12574        b.arg(bytes)
12575            .arg(aq)
12576            .arg(ad)
12577            .arg(&mut y)
12578            .arg(&inf)
12579            .arg(&outf)
12580            .arg(&mi)
12581            .arg(&rb);
12582        unsafe {
12583            b.launch(cfg)?;
12584        }
12585        if scale != 1.0 {
12586            self.scale_inplace(&mut y, scale, m * out_f)?;
12587        }
12588        Ok(y)
12589    }
12590
12591    /// DECODE-EXACT matmul at any m: guarantees the SAME warp-per-row (MMVQ, 32-thread) FP
12592    /// accumulation order as the T=1 decode path for EVERY token row. The spec-decode verify MUST
12593    /// use this for linear-attn projections to be bit-identical to greedy decode. The dp4a kernel
12594    /// (128 threads, two-level reduction) used by `matmul`/`matmul_pre` at m>=5 has a different
12595    /// shfl-tree shape that produces ULP differences propagating through gdn_scan into argmax flips.
12596    /// The MMVQ kernel with grid.y=m already processes each row independently (same 32-thread warp
12597    /// reduce as m=1); this method just forces that path unconditionally.
12598    pub fn matmul_decode_exact(
12599        &self,
12600        w: &crate::model::GpuTensor,
12601        x: &CudaSlice<f32>,
12602        m: usize,
12603    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12604        use crate::model::GpuTensor;
12605        // FLOAT tensors (35B ssm_beta/ssm_alpha on every linear layer, F32 ne=[2048,32]): the
12606        // generic path is cuBLASLt, whose reduction splits are n-DEPENDENT — m=1 vs m=2 col-0
12607        // outputs differ in every bit (probe 2026-07-06: 32/32 bit-diff, maxdiff 3.5e-3), which
12608        // shifted 35B verify logits 0.26-0.56 vs eager and flipped greedy at tight margins (the
12609        // p3 spec FAIL). Decode-exact contract: per-COLUMN m=1 cuBLASLt calls — each column's
12610        // reduction is the exact kernel the T=1 decode path runs, so verify==decode bit-for-bit.
12611        // m<=10 here (K+2 verify tier), so the extra launches are a handful of 4us gemvs.
12612        if let GpuTensor::Float { data, .. } = w {
12613            return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
12614        }
12615        // MEMRA_FULL_PREC bf16-resident weight: dequant-on-use, then the per-column decode-exact
12616        // float linear (same n-independent reduction contract as the Float arm above).
12617        if let GpuTensor::FloatBf16 { data, .. } = w {
12618            let (in_f, out_f) = (w.in_features(), w.out_features());
12619            // Rows fast path: per-row t=1 program (STRONGER than the chunked per-column
12620            // contract — the whole-weight f32 dequant disappears too).
12621            if (1..=32).contains(&m) && Self::bf16_mmv_on() && in_f % 8 == 0 {
12622                let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12623                self.matvec_bf16_rows_into(data, x, &mut y, in_f, out_f, m)?;
12624                return Ok(y);
12625            }
12626            return self.linear_bf16_chunked(x, data, m, in_f, out_f, true, None);
12627        }
12628        if !self.uses_q8_1_fast(w) {
12629            return self.matmul(w, x, m);
12630        }
12631        let in_f = w.in_features();
12632        let out_f = w.out_features();
12633        let (bytes, qtype, row_bytes, scale, rp) = match w {
12634            GpuTensor::Quant {
12635                bytes,
12636                qtype,
12637                row_bytes,
12638                scale,
12639                rp,
12640                ..
12641            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12642            _ => return self.matmul(w, x, m),
12643        };
12644        // Q4_0 split-plane mirror for the mmvq/batched arms below (dp4a tail = matmul_pre,
12645        // which does its own mirror pick).
12646        let (bytes, rp) = match w {
12647            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12648            _ => (bytes, rp),
12649        };
12650        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12651        // BLOCK-128 e4m3 (QT_F8_E4M3_BLK): the same single kernel every other entry dispatches, so
12652        // the decode-exact contract needs nothing special — grid.y=m runs the m=1 program per
12653        // (token,row) by construction, which is exactly what this method exists to guarantee.
12654        if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
12655            return Ok(y);
12656        }
12657        // Batched weight-resident matvec for m=2-8: BIT-IDENTICAL per (token,row) to MMVQ (exact
12658        // integer dp4a, same warp reduce — kernel-check gate rel=0.00e0), one weight read for m
12659        // tokens. The dispatch the divergence fix must avoid is dp4a's 128-thread two-level
12660        // reduce, NOT this. m=5..8 is the K=4..7 spec-verify tier (b8): pre-b8 T=5 fell to the
12661        // grid.y=m per-row MMVQ below = 5 full weight reads/launch — the measured 27B K=4 cliff.
12662        // DECODE-PARITY GATE (2026-07-07): batched (MMVQ-class order) only when the m=1 decode
12663        // chain rides MMVQ too — without MEMRA_MMVQ decode is dp4a, so the exact-contract here
12664        // must be per-column dp4a (matmul_pre fallthrough), not the MMVQ order.
12665        if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
12666            && std::env::var("MEMRA_NO_BATCHED").is_err()
12667            && (m <= 4 || Self::b8_enabled())
12668            // Every b16 class has base + _rp twins after lane/rp-on-st (see matmul_pre's note):
12669            // no mirror precondition, `rp` selects the layout only.
12670            && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
12671                || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
12672        {
12673            let mcols = Self::batched_mcols(m);
12674            return self.qmatvec_mmvq_batched(
12675                bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
12676            );
12677        }
12678        if self.mmvq_supports(qtype) {
12679            // MMVQ at grid.y=m: each row is processed by its own warp independently — same 32-thread
12680            // accumulation + warp_reduce_sum as m=1 decode. Bit-identical per row.
12681            return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
12682        }
12683        // Fallback for non-MMVQ quant types (Q5_K, Q3_K): use dp4a (the only available kernel).
12684        // These types are not used in the 27B's linear-attn NVFP4+Q4_K layers.
12685        self.matmul_pre(w, &aq, &ad, x, m)
12686    }
12687
12688    /// DECODE-EXACT matmul from a PRE-QUANTIZED q8_1 activation (batched-verify epilogue
12689    /// re-fuse, lane/vt-fixes fix 2, 2026-08-03): the EXACT `matmul_decode_exact` dispatch for
12690    /// q8_1-fast Quant tensors, with the caller's (aq, ad) replacing the internal
12691    /// `quantize_q8_1`. quantize_q8_1 is deterministic (same input bytes -> same q8 bytes), so
12692    /// sharing one quantize across sibling matmuls of the same activation — or consuming the
12693    /// q8 emitted by a fused epilogue (rms_norm_q8_1 / add_rms_norm_q8_1 /
12694    /// silu_mul_scaled_q8_1 / gated_rmsnorm_q8_1, all kernel-check-pinned bit-identical to
12695    /// their unfused chains) — cannot change any dispatched kernel's input bytes.
12696    /// Caller MUST guarantee `uses_q8_1_fast(w)` (the fused epilogues only exist on that path).
12697    pub fn matmul_decode_exact_pre(
12698        &self,
12699        w: &crate::model::GpuTensor,
12700        aq: &CudaSlice<i8>,
12701        ad: &CudaSlice<f32>,
12702        m: usize,
12703    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12704        use crate::model::GpuTensor;
12705        debug_assert!(
12706            self.uses_q8_1_fast(w),
12707            "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
12708        );
12709        // BLOCK-128 e4m3: same single kernel, all m — see matmul_decode_exact's note.
12710        if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12711            return Ok(y);
12712        }
12713        let in_f = w.in_features();
12714        let out_f = w.out_features();
12715        let (bytes, qtype, row_bytes, scale, rp) = match w {
12716            GpuTensor::Quant {
12717                bytes,
12718                qtype,
12719                row_bytes,
12720                scale,
12721                rp,
12722                ..
12723            } => (bytes, *qtype, *row_bytes, *scale, *rp),
12724            _ => {
12725                return Err(
12726                    "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
12727                );
12728            }
12729        };
12730        // Q4_0 split-plane mirror — same pick as matmul_decode_exact.
12731        let (bytes, rp) = match w {
12732            GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12733            _ => (bytes, rp),
12734        };
12735        // Dispatch mirror of matmul_decode_exact's q8_1-fast tail, condition for condition.
12736        if (2..=16).contains(&m)
12737            && self.batched_supports(qtype)
12738            && self.mmvq_supports(qtype)
12739            && std::env::var("MEMRA_NO_BATCHED").is_err()
12740            && (m <= 4 || Self::b8_enabled())
12741            && (m <= 8
12742                || qtype == QT_Q4_0
12743                || qtype == QT_Q6_K
12744                || qtype == QT_F8_E4M3
12745                || qtype == QT_NVFP4
12746                || qtype == QT_Q4_K
12747                || qtype == QT_Q5_K
12748                || qtype == QT_Q8_0)
12749        {
12750            let mcols = Self::batched_mcols(m);
12751            return self.qmatvec_mmvq_batched(
12752                bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
12753            );
12754        }
12755        if self.mmvq_supports(qtype) {
12756            return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
12757        }
12758        // Non-MMVQ quant types (Q5_K/Q3_K under MEMRA_MMVQ=0): dp4a via matmul_pre — the same
12759        // fallback matmul_decode_exact takes. m <= 16 on the verify tier never reads x_fallback.
12760        let x0 = self.zeros(0)?;
12761        self.matmul_pre(w, aq, ad, &x0, m)
12762    }
12763
12764    /// DUAL gate+up batched matvec from a PRE-QUANTIZED activation, macro-scales DEFERRED
12765    /// (lane/vt-fixes fix 2): same eligibility as `matmul_decode_exact_dual`, but the caller's
12766    /// (aq, ad) replaces the internal quantize and the NVFP4 per-tensor scales are RETURNED
12767    /// instead of applied via two `scale_inplace` launches — the fused SwiGLU epilogue
12768    /// (`silu_mul_scaled_q8_1`) folds them, exactly like the m=1 decode chain does. Deferring
12769    /// is value-exact: `y[i]*s` inline in the epilogue is the same IEEE multiply scale_inplace
12770    /// would store (f32 store/load round-trips are exact). None -> caller falls back to the
12771    /// per-tensor path.
12772    pub fn matmul_decode_exact_dual_pre(
12773        &self,
12774        w0: &crate::model::GpuTensor,
12775        w1: &crate::model::GpuTensor,
12776        aq: &CudaSlice<i8>,
12777        ad: &CudaSlice<f32>,
12778        m: usize,
12779    ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
12780    {
12781        use crate::model::GpuTensor;
12782        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12783        let on = *ON.get_or_init(|| {
12784            std::env::var("MEMRA_SPEC_DUAL_T")
12785                .map(|v| v != "0")
12786                .unwrap_or(true)
12787        });
12788        if !on
12789            || !(2..=7).contains(&m)
12790            || std::env::var("MEMRA_NO_BATCHED").is_ok()
12791            || !self.uses_q8_1_fast(w0)
12792            || !self.uses_q8_1_fast(w1)
12793        {
12794            return Ok(None);
12795        }
12796        // DECODE-PARITY GATE (lane/nvfp4-strict, 2026-08-05): batched iff MMVQ — the dual
12797        // kernels are the MMVQ warp-reduce family, and without MEMRA_MMVQ the m=1 decode
12798        // chain this verify must match bit-for-bit rides dp4a (see matmul_decode_exact's
12799        // note). The singles enforce this via `mmvq_supports`; the dual door skipped it.
12800        if !self.mmvq_supports(QT_NVFP4) {
12801            return Ok(None);
12802        }
12803        let (in_f, out_f) = (w0.in_features(), w0.out_features());
12804        if w1.in_features() != in_f || w1.out_features() != out_f {
12805            return Ok(None);
12806        }
12807        let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
12808            (
12809                GpuTensor::Quant {
12810                    bytes: b0,
12811                    qtype: q0,
12812                    row_bytes: rb0,
12813                    scale: s0,
12814                    rp: rp0,
12815                    rp4: None,
12816                    ..
12817                },
12818                GpuTensor::Quant {
12819                    bytes: b1,
12820                    qtype: q1,
12821                    row_bytes: rb1,
12822                    scale: s1,
12823                    rp: rp1,
12824                    rp4: None,
12825                    ..
12826                },
12827            ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
12828                (b0, b1, *rb0, *s0, *s1, *rp0)
12829            }
12830            _ => return Ok(None),
12831        };
12832        // m=5..7: only the exact-width rp duals exist (vt-fixes fix 1b); GGUF layout keeps
12833        // the singles. The b8 dual (MCOLS=8 at m=5..8) measured FLAT and stays dead.
12834        if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
12835        {
12836            return Ok(None);
12837        }
12838        let (y0, y1) =
12839            self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
12840        Ok(Some(((y0, s0), (y1, s1))))
12841    }
12842
12843    /// GROUP-4 GDN-tuple BATCHED matvec (trunk-kernels slice C): the qwen35 linear-layer
12844    /// in-projection 4-tuple (wqkv / wqkv_gate / ssm_beta / ssm_alpha) from ONE pre-quantized
12845    /// activation in ONE launch. Blocks map to the concatenated row space; every out_f must be
12846    /// a multiple of 8 (rows_per_block) so each warp's row pair resolves to one tensor; per
12847    /// (tensor, token, row) the kernel body is `nvfp4_mmvq_batched_rp` VERBATIM with the
12848    /// tensor's macro-scale fused at the write (== the conditional scale_inplace pass,
12849    /// bit-identical) -> BIT-IDENTICAL to the four single launches. Split-plane rp NVFP4 only,
12850    /// m=2..16 (exact-width MCOLS at m=5..7 mirroring the B567 law; m>4 requires b8_enabled
12851    /// like the singles; m=9..=16 rides the b16 form — the E4 width lift that lets
12852    /// `matmul_nvfp4_fused3/4` delegate that class here, lane/orndecode2).
12853    /// None -> caller runs the four singles. MEMRA_TK_GDN_GROUP=0 rollback.
12854    pub fn matmul_decode_exact_group4_pre(
12855        &self,
12856        ws: [&crate::model::GpuTensor; 4],
12857        aq: &CudaSlice<i8>,
12858        ad: &CudaSlice<f32>,
12859        m: usize,
12860    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
12861        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12862        let on = *ON.get_or_init(|| {
12863            std::env::var("MEMRA_TK_GDN_GROUP")
12864                .map(|v| v != "0")
12865                .unwrap_or(true)
12866        });
12867        self.matmul_decode_exact_group_pre(&ws, aq, ad, m, on, "GDN group4")
12868    }
12869
12870    /// GROUP-3 twin for the qwen35 full-attention q/k/v triple (trunk-kernels slice D):
12871    /// the SAME group4 kernels with n3=0 (blocks never reach the fourth range; W3/y3 are
12872    /// never dereferenced) — per (tensor, token, row) bit-identical to the three singles
12873    /// exactly as the group4 door is to its four. MEMRA_TK_FA_GROUP=0 rollback.
12874    pub fn matmul_decode_exact_group3_pre(
12875        &self,
12876        ws: [&crate::model::GpuTensor; 3],
12877        aq: &CudaSlice<i8>,
12878        ad: &CudaSlice<f32>,
12879        m: usize,
12880    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
12881        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12882        let on = *ON.get_or_init(|| {
12883            std::env::var("MEMRA_TK_FA_GROUP")
12884                .map(|v| v != "0")
12885                .unwrap_or(true)
12886        });
12887        self.matmul_decode_exact_group_pre(&ws, aq, ad, m, on, "FA group3")
12888    }
12889
12890    /// Shared core of the group3/group4 doors: eligibility mirror of the singles' batched
12891    /// dispatch, then ONE `qmatvec_nvfp4_mmvq_group4_b*_rp` launch over the concatenated
12892    /// row space (3-tensor callers ride n3=0). Returns one output per input tensor.
12893    fn matmul_decode_exact_group_pre(
12894        &self,
12895        ws: &[&crate::model::GpuTensor],
12896        aq: &CudaSlice<i8>,
12897        ad: &CudaSlice<f32>,
12898        m: usize,
12899        on: bool,
12900        tag: &'static str,
12901    ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
12902        use crate::model::GpuTensor;
12903        if !on
12904            || !(2..=16).contains(&m)
12905            || std::env::var("MEMRA_NO_BATCHED").is_ok()
12906            || (m > 4 && !Self::b8_enabled())
12907            || !self.mmvq_supports(QT_NVFP4)
12908            || !self.batched_supports(QT_NVFP4)
12909        {
12910            return Ok(None);
12911        }
12912        let in_f = ws[0].in_features();
12913        let mut parts: Vec<(&CudaSlice<u8>, usize, f32)> = Vec::with_capacity(4);
12914        for w in ws {
12915            if !self.uses_q8_1_fast(w) || w.in_features() != in_f {
12916                return Ok(None);
12917            }
12918            match w {
12919                GpuTensor::Quant {
12920                    bytes,
12921                    qtype,
12922                    scale,
12923                    rp: true,
12924                    rp4: None,
12925                    ..
12926                } if *qtype == QT_NVFP4 && w.out_features() % 8 == 0 => {
12927                    parts.push((bytes, w.out_features(), *scale));
12928                }
12929                _ => return Ok(None),
12930            }
12931        }
12932        // MCOLS tier mirrors the singles: batched_mcols + the B567 exact-width law at m=5..7.
12933        static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12934        let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
12935        let mcols = if (5..=7).contains(&m) && b567 {
12936            m
12937        } else {
12938            Self::batched_mcols(m)
12939        };
12940        let kname: &'static str = match mcols {
12941            2 => "qmatvec_nvfp4_mmvq_group4_b2_rp",
12942            4 => "qmatvec_nvfp4_mmvq_group4_b4_rp",
12943            5 => "qmatvec_nvfp4_mmvq_group4_b5_rp",
12944            6 => "qmatvec_nvfp4_mmvq_group4_b6_rp",
12945            7 => "qmatvec_nvfp4_mmvq_group4_b7_rp",
12946            8 => "qmatvec_nvfp4_mmvq_group4_b8_rp",
12947            16 => "qmatvec_nvfp4_mmvq_group4_b16_rp",
12948            _ => return Ok(None),
12949        };
12950        // Engagement receipt PER DOOR (dead-arm lesson): one shared Once here suppressed
12951        // the second door's print on the slice-D battery — key the once-set by tag.
12952        if std::env::var("MEMRA_DEBUG").is_ok() {
12953            use std::sync::Mutex;
12954            static SEEN: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
12955            let mut seen = SEEN.lock().unwrap();
12956            if !seen.contains(&tag) {
12957                seen.push(tag);
12958                eprintln!("[memra] {tag} batched ENGAGED (m={m})");
12959            }
12960        }
12961        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
12962        let rows_per_block = ROWS_PER_BLOCK * 2; // WROWS=2 in the group kernel
12963        let total: usize = parts.iter().map(|p| p.1).sum();
12964        let three = parts.len() == 3;
12965        let mut y0 = self.alloc_uninit::<f32>(m * parts[0].1)?;
12966        let mut y1 = self.alloc_uninit::<f32>(m * parts[1].1)?;
12967        let mut y2 = self.alloc_uninit::<f32>(m * parts[2].1)?;
12968        // 3-tensor callers: n3=0 means no block ever resolves to the fourth range — W3/y3
12969        // are never dereferenced; a 1-element dummy keeps the launch ABI without aliasing y0.
12970        let mut y3 = self.alloc_uninit::<f32>(if three { 1 } else { m * parts[3].1 })?;
12971        let cfg = LaunchConfig {
12972            grid_dim: ((total as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
12973            block_dim: (32, ROWS_PER_BLOCK, 1),
12974            shared_mem_bytes: 0,
12975        };
12976        let (inf, mi) = (in_f as i32, m as i32);
12977        let (n0, n1, n2) = (parts[0].1 as i32, parts[1].1 as i32, parts[2].1 as i32);
12978        let n3 = if three { 0i32 } else { parts[3].1 as i32 };
12979        let (s0, s1, s2) = (parts[0].2, parts[1].2, parts[2].2);
12980        let s3 = if three { 1.0f32 } else { parts[3].2 };
12981        let w3 = if three { parts[0].0 } else { parts[3].0 };
12982        let f = self.func(kname);
12983        let __s_b = self.gpu.stream();
12984        let mut b = __s_b.launch_builder(&f);
12985        b.arg(parts[0].0)
12986            .arg(parts[1].0)
12987            .arg(parts[2].0)
12988            .arg(w3)
12989            .arg(aq)
12990            .arg(ad)
12991            .arg(&mut y0)
12992            .arg(&mut y1)
12993            .arg(&mut y2)
12994            .arg(&mut y3)
12995            .arg(&inf)
12996            .arg(&n0)
12997            .arg(&n1)
12998            .arg(&n2)
12999            .arg(&n3)
13000            .arg(&mi)
13001            .arg(&s0)
13002            .arg(&s1)
13003            .arg(&s2)
13004            .arg(&s3);
13005        unsafe {
13006            b.launch(cfg)?;
13007        }
13008        Ok(Some(if three {
13009            vec![y0, y1, y2]
13010        } else {
13011            vec![y0, y1, y2, y3]
13012        }))
13013    }
13014
13015    /// DUAL gate+up BATCHED matvec at verify t=2..8 (lane/verify-economics, 2026-08-02): ONE
13016    /// launch computes both FFN projections of a verify batch — same activation, same shape,
13017    /// blockIdx.y selects the tensor. Per (tensor, token, row) the kernel body is the single
13018    /// batched program on the SAME layout (split-plane rp: b2 rp / b4 rpr2 / b8 rpr2; GGUF:
13019    /// b2 base / b4 r2 / b8 r2) -> BIT-IDENTICAL to the two single `matmul_decode_exact`
13020    /// launches (kernel-check gates bitwise on both layouts; run-spec K=1..8 arbitrates e2e).
13021    /// The one activation quantize replaces two IDENTICAL quantizes of the same `x` (same
13022    /// kernel, same input -> same q8_1 bytes), and the two independent weight streams in one
13023    /// grid restore the memory-level parallelism the two-launch form loses to tail drain +
13024    /// launch gap (m=1 dual_mr2 precedent: DRAM 40% -> 47-50% on the 27B pair).
13025    /// `Some((y0, y1))` only when both tensors are NVFP4, the SAME layout (both rp or both
13026    /// GGUF, no rp4 mirror), identical (in_f, out_f, row_bytes), q8_1-fast, and m in 2..=4
13027    /// (the b2/b4 tiers = verify T for K=1..3, the profitable-K window — the b8 dual measured
13028    /// FLAT vs the rpsc singles x3 interleaved, research/verify-economics-20260802, and was
13029    /// killed per doctrine). None -> caller runs the two singles. MEMRA_SPEC_DUAL_T=0 rollback.
13030    pub fn matmul_decode_exact_dual(
13031        &self,
13032        w0: &crate::model::GpuTensor,
13033        w1: &crate::model::GpuTensor,
13034        x: &CudaSlice<f32>,
13035        m: usize,
13036    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13037        use crate::model::GpuTensor;
13038        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13039        let on = *ON.get_or_init(|| {
13040            std::env::var("MEMRA_SPEC_DUAL_T")
13041                .map(|v| v != "0")
13042                .unwrap_or(true)
13043        });
13044        if !on
13045            || !(2..=4).contains(&m)
13046            || std::env::var("MEMRA_NO_BATCHED").is_ok()
13047            || !self.uses_q8_1_fast(w0)
13048            || !self.uses_q8_1_fast(w1)
13049        {
13050            return Ok(None);
13051        }
13052        // DECODE-PARITY GATE (lane/nvfp4-strict, 2026-08-05): batched iff MMVQ — same law as
13053        // the singles' `batched_supports && mmvq_supports` check in matmul_decode_exact,
13054        // which this dual door bypassed. Without MEMRA_MMVQ the m=1 decode is dp4a; the
13055        // verify must ride the per-column dp4a class, not the MMVQ-family dual.
13056        if !self.mmvq_supports(QT_NVFP4) {
13057            return Ok(None);
13058        }
13059        let (in_f, out_f) = (w0.in_features(), w0.out_features());
13060        if w1.in_features() != in_f || w1.out_features() != out_f {
13061            return Ok(None);
13062        }
13063        let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
13064            (
13065                GpuTensor::Quant {
13066                    bytes: b0,
13067                    qtype: q0,
13068                    row_bytes: rb0,
13069                    scale: s0,
13070                    rp: rp0,
13071                    rp4: None,
13072                    ..
13073                },
13074                GpuTensor::Quant {
13075                    bytes: b1,
13076                    qtype: q1,
13077                    row_bytes: rb1,
13078                    scale: s1,
13079                    rp: rp1,
13080                    rp4: None,
13081                    ..
13082                },
13083            ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
13084                (b0, b1, *rb0, *s0, *s1, *rp0)
13085            }
13086            _ => return Ok(None),
13087        };
13088        // Engagement receipt (MEMRA_DEBUG=1): the first dead-arm A/B lesson — a `rp: false`
13089        // gate silently no-op'd the whole experiment; prove the arm is live in the log.
13090        if std::env::var("MEMRA_DEBUG").is_ok() {
13091            static ONCE: std::sync::Once = std::sync::Once::new();
13092            ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
13093        }
13094        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13095        let (y0, y1) =
13096            self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
13097        let mut y0 = y0;
13098        let mut y1 = y1;
13099        if s0 != 1.0 {
13100            self.scale_inplace(&mut y0, s0, m * out_f)?;
13101        }
13102        if s1 != 1.0 {
13103            self.scale_inplace(&mut y1, s1, m * out_f)?;
13104        }
13105        Ok(Some((y0, y1)))
13106    }
13107
13108    /// Launch body of the dual batched twins from raw NVFP4 weight bytes + a pre-quantized q8_1
13109    /// activation (kernel-check's bit-equivalence entry; matmul_decode_exact_dual's core).
13110    /// mcols tier = batched_mcols(m); macro-scale NOT applied. `rp` selects the split-plane
13111    /// twins (both buffers must be the repacked layout).
13112    #[allow(clippy::too_many_arguments)]
13113    pub fn qmatvec_batched_dual_raw(
13114        &self,
13115        b0: &CudaSlice<u8>,
13116        b1: &CudaSlice<u8>,
13117        aq: &CudaSlice<i8>,
13118        ad: &CudaSlice<f32>,
13119        m: usize,
13120        in_f: usize,
13121        out_f: usize,
13122        row_bytes: usize,
13123        rp: bool,
13124    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13125        const ROWS_PER_BLOCK: u32 = 4;
13126        let mcols = Self::batched_mcols(m);
13127        // EXACT-WIDTH duals at m=5..7 (vt-fixes fix 1b): rp-only; bit-identical to the two
13128        // b5/b6/b7 singles (blockIdx.y selects the tensor, same template body).
13129        let tiny_rp1 = rp
13130            && mcols == 4
13131            && out_f <= 128
13132            && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
13133        let (name, rows_per_block) = if tiny_rp1 {
13134            ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
13135        } else {
13136            match (mcols, rp, m) {
13137                (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
13138                (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
13139                (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
13140                (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
13141                (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
13142                (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
13143                (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
13144                _ => {
13145                    return Err(
13146                        format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
13147                    );
13148                }
13149            }
13150        };
13151        let f = self.func(name);
13152        let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
13153        let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
13154        let cfg = LaunchConfig {
13155            grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
13156            block_dim: (32, ROWS_PER_BLOCK, 1),
13157            shared_mem_bytes: 0,
13158        };
13159        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13160        let __s_b = self.gpu.stream();
13161        let mut b = __s_b.launch_builder(&f);
13162        b.arg(b0)
13163            .arg(b1)
13164            .arg(aq)
13165            .arg(ad)
13166            .arg(&mut y0)
13167            .arg(&mut y1)
13168            .arg(&inf)
13169            .arg(&outf)
13170            .arg(&mi)
13171            .arg(&rb);
13172        unsafe {
13173            b.launch(cfg)?;
13174        }
13175        Ok((y0, y1))
13176    }
13177
13178    /// Like `matmul_pre` but RETURNS THE RAW (un-macro-scaled) matmul output together with the
13179    /// per-tensor NVFP4 scale, instead of applying `scale_inplace` internally. Used by the fused
13180    /// SwiGLU epilogue (RANK3 LEVER 2) so the gate/up scales fold into one `silu_mul_scaled` launch.
13181    /// `Some((y_raw, scale))` only on the m==1 decode fast path (mmvq / dp4a) where the scale is a
13182    /// separate post-launch op we can defer; returns `None` for every other path (prefill GEMM, FP4
13183    /// GEMM, Stage-A, Float) so the caller falls back to the scaled `matmul_pre` + `silu_mul`.
13184    /// DUAL gate+up NVFP4 matvec (mm-fusion): ONE launch computes both projections (same
13185    /// activation, same shape) — grid.y selects the tensor. Bit-identical per element to two
13186    /// mr2 launches at m=1. Returns (gate_raw, up_raw) un-scaled (caller folds the two macro
13187    /// scales into the SwiGLU epilogue, same as the matmul_pre_noscale contract). None unless
13188    /// both tensors are NVFP4 q8_1-fast with identical (in_f, out_f, row_bytes) and m==1.
13189    pub fn matmul_pre_dual_noscale(
13190        &self,
13191        w0: &crate::model::GpuTensor,
13192        w1: &crate::model::GpuTensor,
13193        aq: &CudaSlice<i8>,
13194        ad: &CudaSlice<f32>,
13195        m: usize,
13196    ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
13197    {
13198        use crate::model::GpuTensor;
13199        if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
13200            return Ok(None);
13201        }
13202        // FP-ORDER LAW (lane/nvfp4-strict, 2026-08-05): every kernel this door can dispatch
13203        // (q8_0 fused2, nvfp4 dual_mr2) is the MMVQ family — 32-thread warp reduce. Without
13204        // MEMRA_MMVQ the m=1 singles ride dp4a (128-thread two-level reduce), so fusing here
13205        // would mix dispatch families across the pair — the exact class `q8_fused_params`
13206        // already refuses for Q8_0. The NVFP4 arm lacked this check, which is why
13207        // decode-batch-gate `--mode strict`'s equalizing env (MEMRA_MMVQ=0) never pinned
13208        // NVFP4 models: decode_step_h kept riding dual_mr2 while the batched body fell to
13209        // dp4a (gate1 maxdiff 1.639e-1 / gate2 step-8 divergence at the 2026-08-05 train
13210        // HEAD, research/nvfp4-strict-20260805/). Default env (MMVQ on) is dispatch-unchanged.
13211        if !self.mmvq_supports(QT_NVFP4) {
13212            return Ok(None);
13213        }
13214        let (in_f, out_f) = (w0.in_features(), w0.out_features());
13215        if w1.in_features() != in_f || w1.out_features() != out_f {
13216            return Ok(None);
13217        }
13218        // Q8_0 ARM (lane/q27-deepdive, 2026-08-05): the dense-FFN gate+up pair on a Q8_0 trunk fell
13219        // through this NVFP4-only gate to two `matmul_pre_noscale` launches — measured 128 of the
13220        // 1015 launches/token on q27-Q8_0 decode, the single largest un-fused class in the tick
13221        // (nsys `research/q27-deepdive-20260805/nsys/`). `q8_fused2_core` already serves the same
13222        // pair shape for the shared-expert gate/up, and its kernel body is `qmatvec_q8_0_mmvq`
13223        // VERBATIM per (tensor,row) -> BIT-IDENTICAL to the two separate launches. Q8_0 carries no
13224        // macro-scale (q8_fused_params requires scale==1.0), so the noscale contract is satisfied
13225        // by returning 1.0 for both: the SwiGLU epilogue's fold becomes the identity it already is
13226        // on this dtype today. Seam: MEMRA_Q8_FFN_FUSE2=0 rolls back to the two-launch pair.
13227        // rp4 guard: with MEMRA_Q8RP the singles route to the `_rp` split-plane twin over the
13228        // mirror buffer; the fused2 kernel has no `_rp` form, so fusing there would swap
13229        // dispatch families mid-model. Bail and let the two singles run (mirror lane unchanged).
13230        let no_mirror =
13231            |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
13232        if self.q8_ffn_fuse2_on()
13233            && no_mirror(w0)
13234            && no_mirror(w1)
13235            && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
13236        {
13237            let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
13238            return Ok(Some(((y0, 1.0), (y1, 1.0))));
13239        }
13240        // F8-E4M3 ARM (lane/fp8-decode-v1, 2026-08-05): with native e4m3 residency the FFN gate+up
13241        // pair (and the ssm beta+alpha dual, which routes through this same entry) fell through
13242        // both the NVFP4 gate below and the Q8_0 arm above to two `matmul_pre_noscale` launches —
13243        // native residency was UN-FUSING the trunk relative to the Q8_0 slab it replaces. The
13244        // fused2 kernel body is `qmatvec_e4m3_mmvq` VERBATIM per (tensor,row). Contract match:
13245        // `matmul_pre_noscale` on e4m3 launches with scale 1.0 and RETURNS the per-tensor
13246        // weight_scale for the caller to fold, so we pass ws=1.0 here and return (s0,s1) — same
13247        // bits, and the two macro-scale multiplies still fold into the SwiGLU epilogue.
13248        // MEMRA_E4M3_DUAL=0 rolls back to the two-launch pair.
13249        if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
13250            let (y0, y1) =
13251                self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
13252            return Ok(Some(((y0, p0.3), (y1, p1.3))));
13253        }
13254        let (b0, q0, rb0, s0, rp0) = match w0 {
13255            GpuTensor::Quant {
13256                bytes,
13257                qtype,
13258                row_bytes,
13259                scale,
13260                rp,
13261                ..
13262            } => (bytes, *qtype, *row_bytes, *scale, *rp),
13263            _ => return Ok(None),
13264        };
13265        let (b1, q1, rb1, s1, rp1) = match w1 {
13266            GpuTensor::Quant {
13267                bytes,
13268                qtype,
13269                row_bytes,
13270                scale,
13271                rp,
13272                ..
13273            } => (bytes, *qtype, *row_bytes, *scale, *rp),
13274            _ => return Ok(None),
13275        };
13276        if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
13277            return Ok(None);
13278        }
13279        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
13280        const RPW: u32 = 2;
13281        let rows_per_block = ROWS_PER_BLOCK * RPW;
13282        let f = self.func(if rp0 {
13283            "qmatvec_nvfp4_mmvq_dual_mr2_rp"
13284        } else {
13285            "qmatvec_nvfp4_mmvq_dual_mr2"
13286        });
13287        let mut y0 = self.alloc_uninit::<f32>(out_f)?;
13288        let mut y1 = self.alloc_uninit::<f32>(out_f)?;
13289        let cfg = LaunchConfig {
13290            grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
13291            block_dim: (32, ROWS_PER_BLOCK, 1),
13292            shared_mem_bytes: 0,
13293        };
13294        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
13295        // noscale contract: the caller folds s0/s1 into the SwiGLU epilogue — the kernel's fused
13296        // yscale args stay 1.0 here (they exist for the single-tensor callers).
13297        let one = 1.0f32;
13298        let __s_b = self.gpu.stream();
13299        let mut b = __s_b.launch_builder(&f);
13300        b.arg(b0)
13301            .arg(b1)
13302            .arg(aq)
13303            .arg(ad)
13304            .arg(&mut y0)
13305            .arg(&mut y1)
13306            .arg(&inf)
13307            .arg(&outf)
13308            .arg(&mi)
13309            .arg(&rb)
13310            .arg(&one)
13311            .arg(&one);
13312        unsafe {
13313            b.launch(cfg)?;
13314        }
13315        Ok(Some(((y0, s0), (y1, s1))))
13316    }
13317
13318    /// FUSED NVFP4 matvec TRIPLE with unequal out_f (rig-native decode increment 1,
13319    /// lane/rig-native-nvfp4): wq+wk+wv in ONE launch via the q8_0 fused2 block-offset
13320    /// recipe. Per (tensor,row,t) the kernel body is nvfp4_mmvq_multirow_rp VERBATIM ->
13321    /// bit-identical to three separate `matmul_pre` launches; yscales fold in-kernel exactly
13322    /// as the singles do. grid.y = m (the t-parallel verify rows ride the same launch).
13323    /// None when ineligible (not all rp NVFP4 / in_f mismatch / mmvq off) — callers fall
13324    /// back to the three singles.
13325    #[allow(clippy::too_many_arguments)]
13326    pub fn matmul_nvfp4_fused3(
13327        &self,
13328        w0: &crate::model::GpuTensor,
13329        w1: &crate::model::GpuTensor,
13330        w2: &crate::model::GpuTensor,
13331        aq: &CudaSlice<i8>,
13332        ad: &CudaSlice<f32>,
13333        m: usize,
13334    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
13335    {
13336        use crate::model::GpuTensor;
13337        // m == 1 rides the original fused kernel; m = 2..=8 rides the BATCHED fused twin
13338        // (lane/samplat — the fused4_b8 pattern): the seg body is nvfp4_mmvq_batched_rp_sc
13339        // verbatim, weight rows read once for all m columns, bit-identical per
13340        // (tensor,row,column) to the three bN_rpsc singles. The old "at m>1 the fused
13341        // segments would re-read the weight per row" note described the grid.y=m lift,
13342        // which this twin deliberately is NOT.
13343        if !self.mmvq_supports(QT_NVFP4)
13344            || !self.uses_q8_1_fast(w0)
13345            || !self.uses_q8_1_fast(w1)
13346            || !self.uses_q8_1_fast(w2)
13347        {
13348            return Ok(None);
13349        }
13350        // m = 9..=16 (lane/orndecode2): the exact-16 tier's trio width rides the GROUP3
13351        // door — same family and bit-identity law as the fused4 delegate above.
13352        if (9..=16).contains(&m) {
13353            return Ok(
13354                match self.matmul_decode_exact_group3_pre([w0, w1, w2], aq, ad, m)? {
13355                    Some(mut ys) => {
13356                        let y2 = ys.pop().unwrap();
13357                        let y1 = ys.pop().unwrap();
13358                        let y0 = ys.pop().unwrap();
13359                        Some((y0, y1, y2))
13360                    }
13361                    None => None,
13362                },
13363            );
13364        }
13365        if !(1..=8).contains(&m) {
13366            return Ok(None);
13367        }
13368        if m > 1 {
13369            let in_f = w0.in_features();
13370            if std::env::var("MEMRA_NVFP4_FUSED3B").as_deref() == Ok("0")
13371                || !self.batched_supports(QT_NVFP4)
13372                || std::env::var("MEMRA_NO_BATCHED").is_ok()
13373                || (m > 4 && !Self::b8_enabled())
13374                || in_f % 512 != 0
13375                || in_f / 64 > 272
13376            {
13377                return Ok(None);
13378            }
13379        }
13380        let unpack = |w: &crate::model::GpuTensor| match w {
13381            GpuTensor::Quant {
13382                bytes,
13383                qtype,
13384                scale,
13385                rp,
13386                ..
13387            } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13388            _ => None,
13389        };
13390        let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
13391            return Ok(None);
13392        };
13393        let in_f = w0.in_features();
13394        if w1.in_features() != in_f || w2.in_features() != in_f {
13395            return Ok(None);
13396        }
13397        let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
13398        const ROWS_PER_BLOCK: u32 = 4; // MEMRA_MMVQ_ROWS
13399        const RPW: u32 = 2;
13400        let rows_pb = ROWS_PER_BLOCK * RPW;
13401        let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13402        let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
13403        let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
13404        let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
13405        let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
13406        // SAFETY: the raw pointers come straight from the &GpuTensor borrows above and are
13407        // only dereferenced for the launch-arg build inside this call.
13408        let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
13409        if m > 1 {
13410            // batched twin has no in-kernel scale — refuse scale carriers (GGUF trunk = 1.0).
13411            if p0.1 != 1.0 || p1.1 != 1.0 || p2.1 != 1.0 {
13412                return Ok(None);
13413            }
13414            let f = self.func("qmatvec_nvfp4_mmvq_fused3_b8_rpsc");
13415            let cfg = LaunchConfig {
13416                grid_dim: (nb(o0) + nb(o1) + nb(o2), 1, 1),
13417                block_dim: (32, ROWS_PER_BLOCK, 1),
13418                shared_mem_bytes: 0,
13419            };
13420            let __s_b = self.gpu.stream();
13421            let mut b = __s_b.launch_builder(&f);
13422            b.arg(b0)
13423                .arg(b1)
13424                .arg(b2)
13425                .arg(aq)
13426                .arg(ad)
13427                .arg(&mut y0)
13428                .arg(&mut y1)
13429                .arg(&mut y2)
13430                .arg(&inf)
13431                .arg(&oi0)
13432                .arg(&oi1)
13433                .arg(&oi2)
13434                .arg(&mi);
13435            unsafe {
13436                b.launch(cfg)?;
13437            }
13438            return Ok(Some((y0, y1, y2)));
13439        }
13440        let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
13441        let cfg = LaunchConfig {
13442            grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
13443            block_dim: (32, ROWS_PER_BLOCK, 1),
13444            shared_mem_bytes: 0,
13445        };
13446        let __s_b = self.gpu.stream();
13447        let mut b = __s_b.launch_builder(&f);
13448        b.arg(b0)
13449            .arg(b1)
13450            .arg(b2)
13451            .arg(aq)
13452            .arg(ad)
13453            .arg(&mut y0)
13454            .arg(&mut y1)
13455            .arg(&mut y2)
13456            .arg(&inf)
13457            .arg(&oi0)
13458            .arg(&oi1)
13459            .arg(&oi2)
13460            .arg(&mi)
13461            .arg(&p0.1)
13462            .arg(&p1.1)
13463            .arg(&p2.1);
13464        unsafe {
13465            b.launch(cfg)?;
13466        }
13467        Ok(Some((y0, y1, y2)))
13468    }
13469
13470    /// fused2 twin of `matmul_nvfp4_fused3`, for MIXED-type weight groups: the gemma4
13471    /// dense NVFP4mix recipe keeps `attn_v` and `ffn_down` at Q8_0 (full-NVFP4 was
13472    /// measured garbage on this dense class), so its q/k/v trio and gate/up/down never
13473    /// satisfy an all-NVFP4 fused3 — the pairs that ARE uniformly NVFP4 (q,k and
13474    /// gate,up) fuse here instead. m==1 only, same law as fused3/fused4: per
13475    /// (tensor,row) the kernel seg body is VERBATIM, so the fusion is bit-identical to
13476    /// two separate launches. `MEMRA_NVFP4_FUSED2=0` is the rollback seam and the
13477    /// same-binary interleaved A/B arm.
13478    pub fn matmul_nvfp4_fused2(
13479        &self,
13480        w0: &crate::model::GpuTensor,
13481        w1: &crate::model::GpuTensor,
13482        aq: &CudaSlice<i8>,
13483        ad: &CudaSlice<f32>,
13484        m: usize,
13485    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13486        use crate::model::GpuTensor;
13487        static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13488        let off =
13489            *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
13490        // m==1 ONLY: at m>1 the singles ride the _b16 weight-once column program (one weight
13491        // read serves all m rows); the fused segments would re-read the weight per row.
13492        if off
13493            || m != 1
13494            || !self.mmvq_supports(QT_NVFP4)
13495            || !self.uses_q8_1_fast(w0)
13496            || !self.uses_q8_1_fast(w1)
13497        {
13498            return Ok(None);
13499        }
13500        let unpack = |w: &crate::model::GpuTensor| match w {
13501            GpuTensor::Quant {
13502                bytes,
13503                qtype,
13504                scale,
13505                rp,
13506                ..
13507            } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13508            _ => None,
13509        };
13510        let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
13511            return Ok(None);
13512        };
13513        let in_f = w0.in_features();
13514        if w1.in_features() != in_f {
13515            return Ok(None);
13516        }
13517        let (o0, o1) = (w0.out_features(), w1.out_features());
13518        const ROWS_PER_BLOCK: u32 = 4; // MEMRA_MMVQ_ROWS
13519        const RPW: u32 = 2;
13520        let rows_pb = ROWS_PER_BLOCK * RPW;
13521        let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13522        let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
13523        let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
13524        let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
13525        let cfg = LaunchConfig {
13526            grid_dim: (nb(o0) + nb(o1), m as u32, 1),
13527            block_dim: (32, ROWS_PER_BLOCK, 1),
13528            shared_mem_bytes: 0,
13529        };
13530        let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, m as i32);
13531        // SAFETY: the raw pointers come straight from the &GpuTensor borrows above and are
13532        // only dereferenced for the launch-arg build inside this call.
13533        let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
13534        // PDL wave-B: the kernel carries MEMRA_PDL_ENTRY — launch overlaps the producer's
13535        // drain (quantize/norm emitting aq/ad). Same math, same order, bit-identical.
13536        if Self::pdl_on() && Self::pdl_mmvq_on() && Self::pdl_nvfp4q8_on() {
13537            {
13538                use cudarc::driver::{DevicePtr, DevicePtrMut};
13539                let s = &self.gpu.stream();
13540                let (pw0, _g0) = b0.device_ptr(s);
13541                let (pw1, _g1) = b1.device_ptr(s);
13542                let (paq, _g2) = aq.device_ptr(s);
13543                let (pad, _g3) = ad.device_ptr(s);
13544                let (py0, _g4) = y0.device_ptr_mut(s);
13545                let (py1, _g5) = y1.device_ptr_mut(s);
13546                let (s0, s1) = (p0.1, p1.1);
13547                let mut ps = [
13548                    &pw0 as *const _ as *mut std::ffi::c_void,
13549                    &pw1 as *const _ as *mut _,
13550                    &paq as *const _ as *mut _,
13551                    &pad as *const _ as *mut _,
13552                    &py0 as *const _ as *mut _,
13553                    &py1 as *const _ as *mut _,
13554                    &inf as *const _ as *mut _,
13555                    &oi0 as *const _ as *mut _,
13556                    &oi1 as *const _ as *mut _,
13557                    &mi as *const _ as *mut _,
13558                    &s0 as *const _ as *mut _,
13559                    &s1 as *const _ as *mut _,
13560                ];
13561                unsafe {
13562                    self.launch_pdl(
13563                        "qmatvec_nvfp4_mmvq_fused2_rp",
13564                        cfg.grid_dim,
13565                        cfg.block_dim,
13566                        &mut ps,
13567                    )?;
13568                }
13569            }
13570            return Ok(Some((y0, y1)));
13571        }
13572        let __s_b = self.gpu.stream();
13573        let mut b = __s_b.launch_builder(&f);
13574        b.arg(b0)
13575            .arg(b1)
13576            .arg(aq)
13577            .arg(ad)
13578            .arg(&mut y0)
13579            .arg(&mut y1)
13580            .arg(&inf)
13581            .arg(&oi0)
13582            .arg(&oi1)
13583            .arg(&mi)
13584            .arg(&p0.1)
13585            .arg(&p1.1);
13586        unsafe {
13587            b.launch(cfg)?;
13588        }
13589        Ok(Some((y0, y1)))
13590    }
13591
13592    /// Slot-fed fused2 twin (alloc-free capture lane): identical launch to
13593    /// `matmul_nvfp4_fused2`, caller-owned outputs — the gemma4 dc_slotted graph body
13594    /// needs zero mem nodes, so the allocating wrapper can't serve it. Returns false
13595    /// (decline) on any non-NVFP4/rp pair; the caller chains or refuses.
13596    pub fn matmul_nvfp4_fused2_into(
13597        &self,
13598        w0: &crate::model::GpuTensor,
13599        w1: &crate::model::GpuTensor,
13600        aq: &CudaSlice<i8>,
13601        ad: &CudaSlice<f32>,
13602        y0: &mut CudaSlice<f32>,
13603        y1: &mut CudaSlice<f32>,
13604    ) -> Result<bool, Box<dyn std::error::Error>> {
13605        use crate::model::GpuTensor;
13606        static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13607        let off =
13608            *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
13609        if off
13610            || !self.mmvq_supports(QT_NVFP4)
13611            || !self.uses_q8_1_fast(w0)
13612            || !self.uses_q8_1_fast(w1)
13613        {
13614            return Ok(false);
13615        }
13616        let unpack = |w: &crate::model::GpuTensor| match w {
13617            GpuTensor::Quant {
13618                bytes,
13619                qtype,
13620                scale,
13621                rp,
13622                ..
13623            } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13624            _ => None,
13625        };
13626        let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
13627            return Ok(false);
13628        };
13629        let in_f = w0.in_features();
13630        if w1.in_features() != in_f {
13631            return Ok(false);
13632        }
13633        let (o0, o1) = (w0.out_features(), w1.out_features());
13634        if y0.len() < o0 || y1.len() < o1 {
13635            return Ok(false);
13636        }
13637        const ROWS_PER_BLOCK: u32 = 4; // MEMRA_MMVQ_ROWS
13638        const RPW: u32 = 2;
13639        let rows_pb = ROWS_PER_BLOCK * RPW;
13640        let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13641        let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
13642        let cfg = LaunchConfig {
13643            grid_dim: (nb(o0) + nb(o1), 1, 1),
13644            block_dim: (32, ROWS_PER_BLOCK, 1),
13645            shared_mem_bytes: 0,
13646        };
13647        let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, 1i32);
13648        // SAFETY: the raw pointers come straight from the &GpuTensor borrows above and are
13649        // only dereferenced for the launch-arg build inside this call.
13650        let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
13651        let __s_b = self.gpu.stream();
13652        let mut b = __s_b.launch_builder(&f);
13653        b.arg(b0)
13654            .arg(b1)
13655            .arg(aq)
13656            .arg(ad)
13657            .arg(&mut *y0)
13658            .arg(&mut *y1)
13659            .arg(&inf)
13660            .arg(&oi0)
13661            .arg(&oi1)
13662            .arg(&mi)
13663            .arg(&p0.1)
13664            .arg(&p1.1);
13665        unsafe {
13666            b.launch(cfg)?;
13667        }
13668        Ok(true)
13669    }
13670
13671    /// fused4 twin of `matmul_nvfp4_fused3`: the Linear-mixer projection quartet
13672    /// (wqkv + wqkv_gate + ssm_beta + ssm_alpha) in one launch, m==1 only. Per
13673    /// (tensor,row) the kernel body is nvfp4_mmvq_multirow_rp VERBATIM — bit-identical
13674    /// to four separate launches (rig-native decode increment 2, RIG-NATIVE-DECODE.md).
13675    #[allow(clippy::type_complexity)]
13676    pub fn matmul_nvfp4_fused4(
13677        &self,
13678        w0: &crate::model::GpuTensor,
13679        w1: &crate::model::GpuTensor,
13680        w2: &crate::model::GpuTensor,
13681        w3: &crate::model::GpuTensor,
13682        aq: &CudaSlice<i8>,
13683        ad: &CudaSlice<f32>,
13684        m: usize,
13685    ) -> Result<
13686        Option<(
13687            CudaSlice<f32>,
13688            CudaSlice<f32>,
13689            CudaSlice<f32>,
13690            CudaSlice<f32>,
13691        )>,
13692        Box<dyn std::error::Error>,
13693    > {
13694        use crate::model::GpuTensor;
13695        // MEMRA_NVFP4_FUSED4=0: rollback seam + the same-binary interleaved A/B arm.
13696        // m == 1 rides the original fused kernel; m = 2..=8 rides the BATCHED fused twin
13697        // (lane/samplat, 2026-08-21): same quartet-in-one-launch shape, seg body =
13698        // nvfp4_mmvq_batched_rp_sc verbatim (weight rows read once for all m columns) —
13699        // bit-identical per (tensor,row,column) to the four bN_rpsc singles it replaces.
13700        // Admission mirrors the singles' batched gates below.
13701        if std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
13702            || !self.mmvq_supports(QT_NVFP4)
13703            || !self.uses_q8_1_fast(w0)
13704            || !self.uses_q8_1_fast(w1)
13705            || !self.uses_q8_1_fast(w2)
13706            || !self.uses_q8_1_fast(w3)
13707        {
13708            return Ok(None);
13709        }
13710        // m = 9..=16 (lane/orndecode2, the exact-16 tier's trunk): the rp-sc seg twins stop
13711        // at 8; this width class rides the GROUP4 door instead — nvfp4_mmvq_batched_rp<16,_>
13712        // body, the SAME family as the b16_rp singles the tier would otherwise launch four
13713        // times, bit-identical per (tensor,token,row) incl. the fused write-side scale.
13714        if (9..=16).contains(&m) {
13715            return Ok(
13716                match self.matmul_decode_exact_group4_pre([w0, w1, w2, w3], aq, ad, m)? {
13717                    Some(mut ys) => {
13718                        let y3 = ys.pop().unwrap();
13719                        let y2 = ys.pop().unwrap();
13720                        let y1 = ys.pop().unwrap();
13721                        let y0 = ys.pop().unwrap();
13722                        Some((y0, y1, y2, y3))
13723                    }
13724                    None => None,
13725                },
13726            );
13727        }
13728        if !(1..=8).contains(&m) {
13729            return Ok(None);
13730        }
13731        if m > 1 {
13732            // the batched-twin gates: the bN_rpsc program this must stay byte-identical to
13733            // (matmul_pre's batched arm), plus the rp-sc dispatch shape requirements.
13734            let in_f = w0.in_features();
13735            if !self.batched_supports(QT_NVFP4)
13736                || std::env::var("MEMRA_NO_BATCHED").is_ok()
13737                || (m > 4 && !Self::b8_enabled())
13738                || in_f % 512 != 0
13739                || in_f / 64 > 272
13740            {
13741                return Ok(None);
13742            }
13743        }
13744        let unpack = |w: &crate::model::GpuTensor| match w {
13745            GpuTensor::Quant {
13746                bytes,
13747                qtype,
13748                scale,
13749                rp,
13750                ..
13751            } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13752            _ => None,
13753        };
13754        let (Some(p0), Some(p1), Some(p2), Some(p3)) =
13755            (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
13756        else {
13757            return Ok(None);
13758        };
13759        let in_f = w0.in_features();
13760        if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
13761            return Ok(None);
13762        }
13763        let (o0, o1, o2, o3) = (
13764            w0.out_features(),
13765            w1.out_features(),
13766            w2.out_features(),
13767            w3.out_features(),
13768        );
13769        const ROWS_PER_BLOCK: u32 = 4; // MEMRA_MMVQ_ROWS
13770        const RPW: u32 = 2;
13771        let rows_pb = ROWS_PER_BLOCK * RPW;
13772        let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13773        let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
13774        let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
13775        let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
13776        let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
13777        let (inf, oi0, oi1, oi2, oi3, mi) = (
13778            in_f as i32,
13779            o0 as i32,
13780            o1 as i32,
13781            o2 as i32,
13782            o3 as i32,
13783            m as i32,
13784        );
13785        // SAFETY: the raw pointers come straight from the &GpuTensor borrows above and are
13786        // only dereferenced for the launch-arg build inside this call.
13787        let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
13788        if m > 1 {
13789            // Batched fused twin: no in-kernel scale (the bN_rpsc program has none) — refuse
13790            // scale-carrying tensors so the singles path keeps them (GGUF trunk scales are 1.0).
13791            if p0.1 != 1.0 || p1.1 != 1.0 || p2.1 != 1.0 || p3.1 != 1.0 {
13792                return Ok(None);
13793            }
13794            let f = self.func("qmatvec_nvfp4_mmvq_fused4_b8_rpsc");
13795            let cfg = LaunchConfig {
13796                grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), 1, 1),
13797                block_dim: (32, ROWS_PER_BLOCK, 1),
13798                shared_mem_bytes: 0,
13799            };
13800            let __s_b = self.gpu.stream();
13801            let mut b = __s_b.launch_builder(&f);
13802            b.arg(b0)
13803                .arg(b1)
13804                .arg(b2)
13805                .arg(b3)
13806                .arg(aq)
13807                .arg(ad)
13808                .arg(&mut y0)
13809                .arg(&mut y1)
13810                .arg(&mut y2)
13811                .arg(&mut y3)
13812                .arg(&inf)
13813                .arg(&oi0)
13814                .arg(&oi1)
13815                .arg(&oi2)
13816                .arg(&oi3)
13817                .arg(&mi);
13818            unsafe {
13819                b.launch(cfg)?;
13820            }
13821            return Ok(Some((y0, y1, y2, y3)));
13822        }
13823        let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
13824        let cfg = LaunchConfig {
13825            grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
13826            block_dim: (32, ROWS_PER_BLOCK, 1),
13827            shared_mem_bytes: 0,
13828        };
13829        let __s_b = self.gpu.stream();
13830        let mut b = __s_b.launch_builder(&f);
13831        b.arg(b0)
13832            .arg(b1)
13833            .arg(b2)
13834            .arg(b3)
13835            .arg(aq)
13836            .arg(ad)
13837            .arg(&mut y0)
13838            .arg(&mut y1)
13839            .arg(&mut y2)
13840            .arg(&mut y3)
13841            .arg(&inf)
13842            .arg(&oi0)
13843            .arg(&oi1)
13844            .arg(&oi2)
13845            .arg(&oi3)
13846            .arg(&mi)
13847            .arg(&p0.1)
13848            .arg(&p1.1)
13849            .arg(&p2.1)
13850            .arg(&p3.1);
13851        unsafe {
13852            b.launch(cfg)?;
13853        }
13854        Ok(Some((y0, y1, y2, y3)))
13855    }
13856
13857    /// FUSED Q8_0 m=1 matvec PAIR with UNEQUAL out_f (trunk launch-fusion, 2026-07-05). Folds two
13858    /// same-input q8_0 projections (35B trunk: wqkv+wqkv_gate 8192/4096, gate_shexp+up_shexp
13859    /// 512/512) into ONE launch via a block-offset split (blocks [0,nb0) -> w0, rest -> w1) — the
13860    /// dual-mr2 recipe with the same-out_f restriction lifted. Per (tensor,row) the kernel body is
13861    /// qmatvec_q8_0_mmvq VERBATIM -> BIT-IDENTICAL to two separate m=1 launches. Returns None when
13862    /// ineligible (not both Q8_0 / in_f mismatch / MEMRA_MMVQ off / MEMRA_Q8_DUAL=0) — caller falls
13863    /// back to the per-tensor path.
13864    pub fn matmul_q8_fused2(
13865        &self,
13866        w0: &crate::model::GpuTensor,
13867        w1: &crate::model::GpuTensor,
13868        aq: &CudaSlice<i8>,
13869        ad: &CudaSlice<f32>,
13870    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13871        // e4m3 twin (lane/fp8-decode-v1): this entry is the trunk's generic m=1 pair door
13872        // (wqkv+wqkv_gate, ssm_beta+alpha, gate_shexp+up_shexp), so admitting QT_F8_E4M3 here
13873        // fuses the NATIVE-RESIDENCY FP8 trunk at every existing call site with no call-site
13874        // change. Scale is folded in-kernel per range -> the returned buffers are already scaled,
13875        // exactly like the per-tensor `matmul_pre` e4m3 dispatch this replaces.
13876        if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
13877            return Ok(Some(self.e4m3_fused2_core(
13878                p0.0,
13879                p1.0,
13880                aq,
13881                ad,
13882                w0.in_features(),
13883                p0.1,
13884                p1.1,
13885                p0.2,
13886                p0.3,
13887                p1.3,
13888            )?));
13889        }
13890        let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
13891            return Ok(None);
13892        };
13893        Ok(Some(self.q8_fused2_core(
13894            p0.0,
13895            p1.0,
13896            aq,
13897            ad,
13898            w0.in_features(),
13899            p0.1,
13900            p1.1,
13901            p0.2,
13902        )?))
13903    }
13904
13905    #[allow(clippy::too_many_arguments)]
13906    fn q8_fused2_core(
13907        &self,
13908        b0: &CudaSlice<u8>,
13909        b1: &CudaSlice<u8>,
13910        aq: &CudaSlice<i8>,
13911        ad: &CudaSlice<f32>,
13912        in_f: usize,
13913        out0: usize,
13914        out1: usize,
13915        row_bytes: usize,
13916    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13917        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
13918        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
13919        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
13920        let f = self.func("qmatvec_q8_0_mmvq_fused2");
13921        let mut y0 = self.alloc_uninit::<f32>(out0)?;
13922        let mut y1 = self.alloc_uninit::<f32>(out1)?;
13923        let cfg = LaunchConfig {
13924            grid_dim: (nb0 + nb1, 1, 1),
13925            block_dim: (32, ROWS_PER_BLOCK, 1),
13926            shared_mem_bytes: 0,
13927        };
13928        let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
13929        let __s_b = self.gpu.stream();
13930        let mut b = __s_b.launch_builder(&f);
13931        b.arg(b0)
13932            .arg(b1)
13933            .arg(aq)
13934            .arg(ad)
13935            .arg(&mut y0)
13936            .arg(&mut y1)
13937            .arg(&inf)
13938            .arg(&o0)
13939            .arg(&o1)
13940            .arg(&rbl);
13941        unsafe {
13942            b.launch(cfg)?;
13943        }
13944        Ok((y0, y1))
13945    }
13946
13947    /// f32-activation entry for the fused2 pair: quantizes x to q8_1 ONCE then runs the fused
13948    /// launch — replaces two `matmul(w, x, 1)` calls that would each re-quantize the same x
13949    /// (35B shared-expert gate+up per MoE layer per token). Same bits: quantize_q8_1 is
13950    /// deterministic, the fused body is the MMVQ kernel verbatim. None when ineligible (the
13951    /// callers' m==1-under-MEMRA_FAST dispatch would take MMVQ; anything else falls back).
13952    pub fn matmul_q8_fused2_x(
13953        &self,
13954        w0: &crate::model::GpuTensor,
13955        w1: &crate::model::GpuTensor,
13956        x: &CudaSlice<f32>,
13957    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13958        if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
13959            return Ok(None);
13960        }
13961        if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
13962            let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
13963            return Ok(Some(self.e4m3_fused2_core(
13964                p0.0,
13965                p1.0,
13966                &aq,
13967                &ad,
13968                w0.in_features(),
13969                p0.1,
13970                p1.1,
13971                p0.2,
13972                p0.3,
13973                p1.3,
13974            )?));
13975        }
13976        let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
13977            return Ok(None);
13978        };
13979        let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
13980        Ok(Some(self.q8_fused2_core(
13981            p0.0,
13982            p1.0,
13983            &aq,
13984            &ad,
13985            w0.in_features(),
13986            p0.1,
13987            p1.1,
13988            p0.2,
13989        )?))
13990    }
13991
13992    /// Test entry for the kernel_check gate: launch the fused2 kernel from raw weight bytes,
13993    /// quantizing the f32 activation internally (mirrors qmatvec_mmvq_raw; no env gating).
13994    #[allow(clippy::too_many_arguments)]
13995    pub fn qmatvec_q8_fused2_raw(
13996        &self,
13997        b0: &CudaSlice<u8>,
13998        b1: &CudaSlice<u8>,
13999        x: &CudaSlice<f32>,
14000        in_f: usize,
14001        out0: usize,
14002        out1: usize,
14003        row_bytes: usize,
14004    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14005        let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
14006        self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
14007    }
14008
14009    /// FUSED Q8_0 m=1 matvec TRIPLE (wq+wk+wv on the 35B full-attn layers: out_f 8192/512/512).
14010    /// Same block-offset recipe as `matmul_q8_fused2` with three ranges. BIT-IDENTICAL per
14011    /// (tensor,row) to three separate m=1 MMVQ launches.
14012    /// FUSED Q4_0 m=1 TRIPLE (gemma q/k/v — same quantized input; per (tensor,row) chain
14013    /// identical to the mr2 kernel). Returns None unless all three are Q4_0 with equal in_f.
14014    pub fn matmul_q4_fused3(
14015        &self,
14016        w0: &crate::model::GpuTensor,
14017        w1: &crate::model::GpuTensor,
14018        w2: &crate::model::GpuTensor,
14019        aq: &CudaSlice<i8>,
14020        ad: &CudaSlice<f32>,
14021    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14022    {
14023        use crate::model::GpuTensor;
14024        let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
14025            match w {
14026                GpuTensor::Quant {
14027                    qtype, row_bytes, ..
14028                } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
14029                _ => None,
14030            }
14031        };
14032        let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
14033            return Ok(None);
14034        };
14035        if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
14036            return Ok(None);
14037        }
14038        // Effective (bytes, rp) per tensor: mirror (rp4) OR the in-place swap (rp flag,
14039        // bytes already split). Mixed layouts cannot share one fused launch -> fall back to
14040        // the separate matvecs (each routes its own rp).
14041        fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14042            match w {
14043                GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14044                    Some(m) => (m, true),
14045                    None => (bytes, *rp),
14046                },
14047                _ => unreachable!(),
14048            }
14049        }
14050        let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
14051        if rp0 != rp1 || rp1 != rp2 {
14052            return Ok(None);
14053        }
14054        let rp = rp0;
14055        let rpb: u32 = 4;
14056        // mr1 (one row/warp, 2026-07-14): follows the singles' MEMRA_Q40_MR default — the
14057        // fused t=1 kernels were left on mr2 when the singles flipped (DRAM-duty map:
14058        // fused3 57% / fused2 86%; small qkv segments starve under mr2's half grid).
14059        let mr1 = rp && Self::q40_mr1_on();
14060        let nb = |o: usize| {
14061            if mr1 {
14062                (o as u32).div_ceil(rpb)
14063            } else {
14064                (o as u32).div_ceil(2).div_ceil(rpb)
14065            }
14066        };
14067        let grid = nb(o0) + nb(o1) + nb(o2);
14068        let mut y0 = self.alloc_uninit::<f32>(o0)?;
14069        let mut y1 = self.alloc_uninit::<f32>(o1)?;
14070        let mut y2 = self.alloc_uninit::<f32>(o2)?;
14071        let f = self.func(if mr1 {
14072            "qmatvec_q4_0_mmvq_fused3_mr1_rp"
14073        } else if rp {
14074            "qmatvec_q4_0_mmvq_fused3_rp"
14075        } else {
14076            "qmatvec_q4_0_mmvq_fused3"
14077        });
14078        let cfg = LaunchConfig {
14079            grid_dim: (grid, 1, 1),
14080            block_dim: (32, rpb, 1),
14081            shared_mem_bytes: 0,
14082        };
14083        let inf = w0.in_features() as i32;
14084        let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
14085        let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
14086        // PDL wave-A (2026-07-23): the mr1 kernel carries MEMRA_PDL_ENTRY; only that
14087        // variant may take the programmatic-serialization launch.
14088        if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
14089            {
14090                use cudarc::driver::{DevicePtr, DevicePtrMut};
14091                let s = &self.gpu.stream();
14092                let (p0, _g0) = b0.device_ptr(s);
14093                let (p1, _g1) = b1.device_ptr(s);
14094                let (p2, _g2) = b2.device_ptr(s);
14095                let (paq, _g3) = aq.device_ptr(s);
14096                let (pad, _g4) = ad.device_ptr(s);
14097                let (py0, _g5) = y0.device_ptr_mut(s);
14098                let (py1, _g6) = y1.device_ptr_mut(s);
14099                let (py2, _g7) = y2.device_ptr_mut(s);
14100                let mut ps = [
14101                    &p0 as *const _ as *mut std::ffi::c_void,
14102                    &p1 as *const _ as *mut _,
14103                    &p2 as *const _ as *mut _,
14104                    &paq as *const _ as *mut _,
14105                    &pad as *const _ as *mut _,
14106                    &py0 as *const _ as *mut _,
14107                    &py1 as *const _ as *mut _,
14108                    &py2 as *const _ as *mut _,
14109                    &inf as *const _ as *mut _,
14110                    &oo0 as *const _ as *mut _,
14111                    &oo1 as *const _ as *mut _,
14112                    &oo2 as *const _ as *mut _,
14113                    &r0 as *const _ as *mut _,
14114                    &r1 as *const _ as *mut _,
14115                    &r2 as *const _ as *mut _,
14116                ];
14117                unsafe {
14118                    self.launch_pdl(
14119                        "qmatvec_q4_0_mmvq_fused3_mr1_rp",
14120                        (grid, 1, 1),
14121                        (32, rpb, 1),
14122                        &mut ps,
14123                    )?;
14124                }
14125            }
14126            return Ok(Some((y0, y1, y2)));
14127        }
14128        let __s_b = self.gpu.stream();
14129        let mut b = __s_b.launch_builder(&f);
14130        b.arg(b0)
14131            .arg(b1)
14132            .arg(b2)
14133            .arg(aq)
14134            .arg(ad)
14135            .arg(&mut y0)
14136            .arg(&mut y1)
14137            .arg(&mut y2)
14138            .arg(&inf)
14139            .arg(&oo0)
14140            .arg(&oo1)
14141            .arg(&oo2)
14142            .arg(&r0)
14143            .arg(&r1)
14144            .arg(&r2);
14145        unsafe {
14146            b.launch(cfg)?;
14147        }
14148        Ok(Some((y0, y1, y2)))
14149    }
14150
14151    /// Slot-fed fused3 twin (alloc-free capture lane): identical launch, caller-owned outputs.
14152    /// Returns Ok(false) when the fused path is unavailable (caller falls back).
14153    #[allow(clippy::too_many_arguments)]
14154    pub fn matmul_q4_fused3_into(
14155        &self,
14156        w0: &crate::model::GpuTensor,
14157        w1: &crate::model::GpuTensor,
14158        w2: &crate::model::GpuTensor,
14159        aq: &CudaSlice<i8>,
14160        ad: &CudaSlice<f32>,
14161        y0: &mut CudaSlice<f32>,
14162        y1: &mut CudaSlice<f32>,
14163        y2: &mut CudaSlice<f32>,
14164    ) -> Result<bool, Box<dyn std::error::Error>> {
14165        use crate::model::GpuTensor;
14166        let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
14167            match w {
14168                GpuTensor::Quant {
14169                    qtype, row_bytes, ..
14170                } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
14171                _ => None,
14172            }
14173        };
14174        let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
14175            return Ok(false);
14176        };
14177        if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
14178            return Ok(false);
14179        }
14180        fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14181            match w {
14182                GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14183                    Some(m) => (m, true),
14184                    None => (bytes, *rp),
14185                },
14186                _ => unreachable!(),
14187            }
14188        }
14189        let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
14190        if rp0 != rp1 || rp1 != rp2 {
14191            return Ok(false);
14192        }
14193        let rp = rp0;
14194        let rpb: u32 = 4;
14195        let mr1 = rp && Self::q40_mr1_on();
14196        let nb = |o: usize| {
14197            if mr1 {
14198                (o as u32).div_ceil(rpb)
14199            } else {
14200                (o as u32).div_ceil(2).div_ceil(rpb)
14201            }
14202        };
14203        let grid = nb(o0) + nb(o1) + nb(o2);
14204        debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
14205        let f = self.func(if mr1 {
14206            "qmatvec_q4_0_mmvq_fused3_mr1_rp"
14207        } else if rp {
14208            "qmatvec_q4_0_mmvq_fused3_rp"
14209        } else {
14210            "qmatvec_q4_0_mmvq_fused3"
14211        });
14212        let cfg = LaunchConfig {
14213            grid_dim: (grid, 1, 1),
14214            block_dim: (32, rpb, 1),
14215            shared_mem_bytes: 0,
14216        };
14217        let inf = w0.in_features() as i32;
14218        let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
14219        let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
14220        // PDL wave-A: identical to the owned twin (capture-lane parity).
14221        if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
14222            use cudarc::driver::{DevicePtr, DevicePtrMut};
14223            let s = &self.gpu.stream();
14224            let (p0, _g0) = b0.device_ptr(s);
14225            let (p1, _g1) = b1.device_ptr(s);
14226            let (p2, _g2) = b2.device_ptr(s);
14227            let (paq, _g3) = aq.device_ptr(s);
14228            let (pad, _g4) = ad.device_ptr(s);
14229            let (py0, _g5) = y0.device_ptr_mut(s);
14230            let (py1, _g6) = y1.device_ptr_mut(s);
14231            let (py2, _g7) = y2.device_ptr_mut(s);
14232            let mut ps = [
14233                &p0 as *const _ as *mut std::ffi::c_void,
14234                &p1 as *const _ as *mut _,
14235                &p2 as *const _ as *mut _,
14236                &paq as *const _ as *mut _,
14237                &pad as *const _ as *mut _,
14238                &py0 as *const _ as *mut _,
14239                &py1 as *const _ as *mut _,
14240                &py2 as *const _ as *mut _,
14241                &inf as *const _ as *mut _,
14242                &oo0 as *const _ as *mut _,
14243                &oo1 as *const _ as *mut _,
14244                &oo2 as *const _ as *mut _,
14245                &r0 as *const _ as *mut _,
14246                &r1 as *const _ as *mut _,
14247                &r2 as *const _ as *mut _,
14248            ];
14249            unsafe {
14250                self.launch_pdl(
14251                    "qmatvec_q4_0_mmvq_fused3_mr1_rp",
14252                    (grid, 1, 1),
14253                    (32, rpb, 1),
14254                    &mut ps,
14255                )?;
14256            }
14257            return Ok(true);
14258        }
14259        let __s_b = self.gpu.stream();
14260        let mut b = __s_b.launch_builder(&f);
14261        b.arg(b0)
14262            .arg(b1)
14263            .arg(b2)
14264            .arg(aq)
14265            .arg(ad)
14266            .arg(&mut *y0)
14267            .arg(&mut *y1)
14268            .arg(&mut *y2)
14269            .arg(&inf)
14270            .arg(&oo0)
14271            .arg(&oo1)
14272            .arg(&oo2)
14273            .arg(&r0)
14274            .arg(&r1)
14275            .arg(&r2);
14276        unsafe {
14277            b.launch(cfg)?;
14278        }
14279        Ok(true)
14280    }
14281
14282    /// FUSED Q4_0 m=1 PAIR (gemma shared gate+up).
14283    pub fn matmul_q4_fused2(
14284        &self,
14285        w0: &crate::model::GpuTensor,
14286        w1: &crate::model::GpuTensor,
14287        aq: &CudaSlice<i8>,
14288        ad: &CudaSlice<f32>,
14289    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
14290        use crate::model::GpuTensor;
14291        let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
14292            match w {
14293                GpuTensor::Quant {
14294                    qtype, row_bytes, ..
14295                } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
14296                _ => None,
14297            }
14298        };
14299        let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
14300            return Ok(None);
14301        };
14302        if w0.in_features() != w1.in_features() {
14303            return Ok(None);
14304        }
14305        // Effective (bytes, rp) per tensor (mirror or in-place swap); mixed -> separate matvecs.
14306        fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14307            match w {
14308                GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14309                    Some(m) => (m, true),
14310                    None => (bytes, *rp),
14311                },
14312                _ => unreachable!(),
14313            }
14314        }
14315        let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
14316        if rp0 != rp1 {
14317            return Ok(None);
14318        }
14319        let rp = rp0;
14320        let rpb: u32 = 4;
14321        // mr1 twin — see matmul_q4_fused3.
14322        let mr1 = rp && Self::q40_mr1_on();
14323        let nb = |o: usize| {
14324            if mr1 {
14325                (o as u32).div_ceil(rpb)
14326            } else {
14327                (o as u32).div_ceil(2).div_ceil(rpb)
14328            }
14329        };
14330        let grid = nb(o0) + nb(o1);
14331        let mut y0 = self.alloc_uninit::<f32>(o0)?;
14332        let mut y1 = self.alloc_uninit::<f32>(o1)?;
14333        let f = self.func(if mr1 {
14334            "qmatvec_q4_0_mmvq_fused2_mr1_rp"
14335        } else if rp {
14336            "qmatvec_q4_0_mmvq_fused2_rp"
14337        } else {
14338            "qmatvec_q4_0_mmvq_fused2"
14339        });
14340        let cfg = LaunchConfig {
14341            grid_dim: (grid, 1, 1),
14342            block_dim: (32, rpb, 1),
14343            shared_mem_bytes: 0,
14344        };
14345        let inf = w0.in_features() as i32;
14346        let (oo0, oo1) = (o0 as i32, o1 as i32);
14347        let (r0, r1) = (rb0 as i64, rb1 as i64);
14348        // PDL wave-A: mr1 kernel carries MEMRA_PDL_ENTRY.
14349        if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
14350            {
14351                use cudarc::driver::{DevicePtr, DevicePtrMut};
14352                let s = &self.gpu.stream();
14353                let (p0, _g0) = b0.device_ptr(s);
14354                let (p1, _g1) = b1.device_ptr(s);
14355                let (paq, _g2) = aq.device_ptr(s);
14356                let (pad, _g3) = ad.device_ptr(s);
14357                let (py0, _g4) = y0.device_ptr_mut(s);
14358                let (py1, _g5) = y1.device_ptr_mut(s);
14359                let mut ps = [
14360                    &p0 as *const _ as *mut std::ffi::c_void,
14361                    &p1 as *const _ as *mut _,
14362                    &paq as *const _ as *mut _,
14363                    &pad as *const _ as *mut _,
14364                    &py0 as *const _ as *mut _,
14365                    &py1 as *const _ as *mut _,
14366                    &inf as *const _ as *mut _,
14367                    &oo0 as *const _ as *mut _,
14368                    &oo1 as *const _ as *mut _,
14369                    &r0 as *const _ as *mut _,
14370                    &r1 as *const _ as *mut _,
14371                ];
14372                unsafe {
14373                    self.launch_pdl(
14374                        "qmatvec_q4_0_mmvq_fused2_mr1_rp",
14375                        (grid, 1, 1),
14376                        (32, rpb, 1),
14377                        &mut ps,
14378                    )?;
14379                }
14380            }
14381            return Ok(Some((y0, y1)));
14382        }
14383        let __s_b = self.gpu.stream();
14384        let mut b = __s_b.launch_builder(&f);
14385        b.arg(b0)
14386            .arg(b1)
14387            .arg(aq)
14388            .arg(ad)
14389            .arg(&mut y0)
14390            .arg(&mut y1)
14391            .arg(&inf)
14392            .arg(&oo0)
14393            .arg(&oo1)
14394            .arg(&r0)
14395            .arg(&r1);
14396        unsafe {
14397            b.launch(cfg)?;
14398        }
14399        Ok(Some((y0, y1)))
14400    }
14401
14402    /// Slot-fed fused2 twin (alloc-free capture lane): identical launch, caller-owned outputs.
14403    pub fn matmul_q4_fused2_into(
14404        &self,
14405        w0: &crate::model::GpuTensor,
14406        w1: &crate::model::GpuTensor,
14407        aq: &CudaSlice<i8>,
14408        ad: &CudaSlice<f32>,
14409        y0: &mut CudaSlice<f32>,
14410        y1: &mut CudaSlice<f32>,
14411    ) -> Result<bool, Box<dyn std::error::Error>> {
14412        use crate::model::GpuTensor;
14413        let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
14414            match w {
14415                GpuTensor::Quant {
14416                    qtype, row_bytes, ..
14417                } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
14418                _ => None,
14419            }
14420        };
14421        let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
14422            return Ok(false);
14423        };
14424        if w0.in_features() != w1.in_features() {
14425            return Ok(false);
14426        }
14427        fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14428            match w {
14429                GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14430                    Some(m) => (m, true),
14431                    None => (bytes, *rp),
14432                },
14433                _ => unreachable!(),
14434            }
14435        }
14436        let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
14437        if rp0 != rp1 {
14438            return Ok(false);
14439        }
14440        let rp = rp0;
14441        let rpb: u32 = 4;
14442        let mr1 = rp && Self::q40_mr1_on();
14443        let nb = |o: usize| {
14444            if mr1 {
14445                (o as u32).div_ceil(rpb)
14446            } else {
14447                (o as u32).div_ceil(2).div_ceil(rpb)
14448            }
14449        };
14450        let grid = nb(o0) + nb(o1);
14451        debug_assert!(y0.len() >= o0 && y1.len() >= o1);
14452        let f = self.func(if mr1 {
14453            "qmatvec_q4_0_mmvq_fused2_mr1_rp"
14454        } else if rp {
14455            "qmatvec_q4_0_mmvq_fused2_rp"
14456        } else {
14457            "qmatvec_q4_0_mmvq_fused2"
14458        });
14459        let cfg = LaunchConfig {
14460            grid_dim: (grid, 1, 1),
14461            block_dim: (32, rpb, 1),
14462            shared_mem_bytes: 0,
14463        };
14464        let inf = w0.in_features() as i32;
14465        let (oo0, oo1) = (o0 as i32, o1 as i32);
14466        let (r0, r1) = (rb0 as i64, rb1 as i64);
14467        // PDL wave-A: identical to the owned twin (capture-lane parity).
14468        if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
14469            use cudarc::driver::{DevicePtr, DevicePtrMut};
14470            let s = &self.gpu.stream();
14471            let (p0, _g0) = b0.device_ptr(s);
14472            let (p1, _g1) = b1.device_ptr(s);
14473            let (paq, _g2) = aq.device_ptr(s);
14474            let (pad, _g3) = ad.device_ptr(s);
14475            let (py0, _g4) = y0.device_ptr_mut(s);
14476            let (py1, _g5) = y1.device_ptr_mut(s);
14477            let mut ps = [
14478                &p0 as *const _ as *mut std::ffi::c_void,
14479                &p1 as *const _ as *mut _,
14480                &paq as *const _ as *mut _,
14481                &pad as *const _ as *mut _,
14482                &py0 as *const _ as *mut _,
14483                &py1 as *const _ as *mut _,
14484                &inf as *const _ as *mut _,
14485                &oo0 as *const _ as *mut _,
14486                &oo1 as *const _ as *mut _,
14487                &r0 as *const _ as *mut _,
14488                &r1 as *const _ as *mut _,
14489            ];
14490            unsafe {
14491                self.launch_pdl(
14492                    "qmatvec_q4_0_mmvq_fused2_mr1_rp",
14493                    (grid, 1, 1),
14494                    (32, rpb, 1),
14495                    &mut ps,
14496                )?;
14497            }
14498            return Ok(true);
14499        }
14500        let __s_b = self.gpu.stream();
14501        let mut b = __s_b.launch_builder(&f);
14502        b.arg(b0)
14503            .arg(b1)
14504            .arg(aq)
14505            .arg(ad)
14506            .arg(&mut *y0)
14507            .arg(&mut *y1)
14508            .arg(&inf)
14509            .arg(&oo0)
14510            .arg(&oo1)
14511            .arg(&r0)
14512            .arg(&r1);
14513        unsafe {
14514            b.launch(cfg)?;
14515        }
14516        Ok(true)
14517    }
14518
14519    /// BATCHED fused2 (2026-07-13, megakernel-microcosm probe): gate+up b-tier matvecs in
14520    /// ONE segmented-grid launch — the up segment fills SMs as the gate segment drains
14521    /// (the per-launch tail waves behind the 6x-falsified b-tier plateau). Bit-identical
14522    /// per row to two mr2_rp launches. rp layout required; m in 2..=8 (b16 has no twin).
14523    pub fn matmul_q4_fused2_batched(
14524        &self,
14525        w0: &crate::model::GpuTensor,
14526        w1: &crate::model::GpuTensor,
14527        aq: &CudaSlice<i8>,
14528        ad: &CudaSlice<f32>,
14529        m: usize,
14530    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
14531        use crate::model::GpuTensor;
14532        if m < 2 || m > 8 {
14533            return Ok(None);
14534        }
14535        let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
14536            match w {
14537                GpuTensor::Quant {
14538                    qtype, row_bytes, ..
14539                } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
14540                _ => None,
14541            }
14542        };
14543        let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
14544            return Ok(None);
14545        };
14546        if w0.in_features() != w1.in_features() {
14547            return Ok(None);
14548        }
14549        fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14550            match w {
14551                GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14552                    Some(mr) => (mr, true),
14553                    None => (bytes, *rp),
14554                },
14555                _ => unreachable!(),
14556            }
14557        }
14558        let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
14559        if !rp0 || !rp1 {
14560            return Ok(None);
14561        }
14562        let mcols = Self::batched_mcols(m);
14563        let rpb: u32 = 4;
14564        let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
14565        let grid = nb(o0) + nb(o1);
14566        let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
14567        let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
14568        let f = self.func(match mcols {
14569            2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
14570            4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
14571            _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
14572        });
14573        let cfg = LaunchConfig {
14574            grid_dim: (grid, 1, 1),
14575            block_dim: (32, rpb, 1),
14576            shared_mem_bytes: 0,
14577        };
14578        let inf = w0.in_features() as i32;
14579        let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
14580        let rb = rb0 as i64;
14581        let __s_b = self.gpu.stream();
14582        let mut b = __s_b.launch_builder(&f);
14583        b.arg(b0)
14584            .arg(b1)
14585            .arg(aq)
14586            .arg(ad)
14587            .arg(&mut y0)
14588            .arg(&mut y1)
14589            .arg(&inf)
14590            .arg(&oo0)
14591            .arg(&oo1)
14592            .arg(&mi)
14593            .arg(&rb);
14594        unsafe {
14595            b.launch(cfg)?;
14596        }
14597        Ok(Some((y0, y1)))
14598    }
14599
14600    /// BATCHED fused3 (see matmul_q4_fused2_batched): three-segment single launch for the
14601    /// verify qkv triple. Same-in_f q4_0 rp tensors, m in 2..=8. Bit-identical per row.
14602    #[allow(clippy::too_many_arguments)]
14603    pub fn matmul_q4_fused3_batched(
14604        &self,
14605        w0: &crate::model::GpuTensor,
14606        w1: &crate::model::GpuTensor,
14607        w2: &crate::model::GpuTensor,
14608        aq: &CudaSlice<i8>,
14609        ad: &CudaSlice<f32>,
14610        m: usize,
14611    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14612    {
14613        use crate::model::GpuTensor;
14614        if m < 2 || m > 8 {
14615            return Ok(None);
14616        }
14617        let q4 = |w: &GpuTensor| -> Option<usize> {
14618            match w {
14619                GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
14620                _ => None,
14621            }
14622        };
14623        let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
14624            return Ok(None);
14625        };
14626        if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
14627            return Ok(None);
14628        }
14629        fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14630            match w {
14631                GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14632                    Some(mr) => (mr, true),
14633                    None => (bytes, *rp),
14634                },
14635                _ => unreachable!(),
14636            }
14637        }
14638        let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
14639        if !rp0 || !rp1 || !rp2 {
14640            return Ok(None);
14641        }
14642        let mcols = Self::batched_mcols(m);
14643        let rpb: u32 = 4;
14644        let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
14645        let grid = nb(o0) + nb(o1) + nb(o2);
14646        let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
14647        let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
14648        let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
14649        let f = self.func(match mcols {
14650            2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
14651            4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
14652            _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
14653        });
14654        let cfg = LaunchConfig {
14655            grid_dim: (grid, 1, 1),
14656            block_dim: (32, rpb, 1),
14657            shared_mem_bytes: 0,
14658        };
14659        let inf = w0.in_features() as i32;
14660        let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
14661        let rb = 0i64;
14662        let __s_b = self.gpu.stream();
14663        let mut b = __s_b.launch_builder(&f);
14664        b.arg(b0)
14665            .arg(b1)
14666            .arg(b2)
14667            .arg(aq)
14668            .arg(ad)
14669            .arg(&mut y0)
14670            .arg(&mut y1)
14671            .arg(&mut y2)
14672            .arg(&inf)
14673            .arg(&oo0)
14674            .arg(&oo1)
14675            .arg(&oo2)
14676            .arg(&mi)
14677            .arg(&rb);
14678        unsafe {
14679            b.launch(cfg)?;
14680        }
14681        Ok(Some((y0, y1, y2)))
14682    }
14683
14684    pub fn matmul_q8_fused3(
14685        &self,
14686        w0: &crate::model::GpuTensor,
14687        w1: &crate::model::GpuTensor,
14688        w2: &crate::model::GpuTensor,
14689        aq: &CudaSlice<i8>,
14690        ad: &CudaSlice<f32>,
14691    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14692    {
14693        // e4m3 twin (lane/fp8-decode-v1): the full-attn wq/wk/wv triple — on the NV-27B those three
14694        // are per-tensor FP8, so native residency without this arm meant three separate launches.
14695        if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
14696            return Ok(Some(self.e4m3_fused3_core(
14697                p0.0,
14698                p1.0,
14699                p2.0,
14700                aq,
14701                ad,
14702                w0.in_features(),
14703                p0.1,
14704                p1.1,
14705                p2.1,
14706                p0.2,
14707                p0.3,
14708                p1.3,
14709                p2.3,
14710            )?));
14711        }
14712        let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
14713            return Ok(None);
14714        };
14715        Ok(Some(self.q8_fused3_core(
14716            p0.0,
14717            p1.0,
14718            p2.0,
14719            aq,
14720            ad,
14721            w0.in_features(),
14722            p0.1,
14723            p1.1,
14724            p2.1,
14725            p0.2,
14726        )?))
14727    }
14728
14729    #[allow(clippy::too_many_arguments)]
14730    fn q8_fused3_core(
14731        &self,
14732        b0: &CudaSlice<u8>,
14733        b1: &CudaSlice<u8>,
14734        b2: &CudaSlice<u8>,
14735        aq: &CudaSlice<i8>,
14736        ad: &CudaSlice<f32>,
14737        in_f: usize,
14738        out0: usize,
14739        out1: usize,
14740        out2: usize,
14741        row_bytes: usize,
14742    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14743        const ROWS_PER_BLOCK: u32 = 4;
14744        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14745        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14746        let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
14747        let f = self.func("qmatvec_q8_0_mmvq_fused3");
14748        let mut y0 = self.alloc_uninit::<f32>(out0)?;
14749        let mut y1 = self.alloc_uninit::<f32>(out1)?;
14750        let mut y2 = self.alloc_uninit::<f32>(out2)?;
14751        let cfg = LaunchConfig {
14752            grid_dim: (nb0 + nb1 + nb2, 1, 1),
14753            block_dim: (32, ROWS_PER_BLOCK, 1),
14754            shared_mem_bytes: 0,
14755        };
14756        let (inf, o0, o1, o2, rbl) = (
14757            in_f as i32,
14758            out0 as i32,
14759            out1 as i32,
14760            out2 as i32,
14761            row_bytes as i64,
14762        );
14763        let __s_b = self.gpu.stream();
14764        let mut b = __s_b.launch_builder(&f);
14765        b.arg(b0)
14766            .arg(b1)
14767            .arg(b2)
14768            .arg(aq)
14769            .arg(ad)
14770            .arg(&mut y0)
14771            .arg(&mut y1)
14772            .arg(&mut y2)
14773            .arg(&inf)
14774            .arg(&o0)
14775            .arg(&o1)
14776            .arg(&o2)
14777            .arg(&rbl);
14778        unsafe {
14779            b.launch(cfg)?;
14780        }
14781        Ok((y0, y1, y2))
14782    }
14783
14784    /// Test entry for the kernel_check gate: fused3 from raw weight bytes (internal q8_1 quant).
14785    #[allow(clippy::too_many_arguments)]
14786    pub fn qmatvec_q8_fused3_raw(
14787        &self,
14788        b0: &CudaSlice<u8>,
14789        b1: &CudaSlice<u8>,
14790        b2: &CudaSlice<u8>,
14791        x: &CudaSlice<f32>,
14792        in_f: usize,
14793        out0: usize,
14794        out1: usize,
14795        out2: usize,
14796        row_bytes: usize,
14797    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14798        let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
14799        self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
14800    }
14801
14802    /// BATCHED twin of `matmul_q8_fused2` for the verify t=2-4 tier (MEMRA_SPEC_FUSED_T call
14803    /// sites, lane/close35b): ONE launch computes both same-input Q8_0 projections for m tokens.
14804    /// Per (tensor,token,row) the kernel body is q8_0_mmvq_batched VERBATIM with the identical
14805    /// row mapping (Q8_0's batched_variant is always "base") -> BIT-IDENTICAL to the two
14806    /// per-tensor _b2/_b4 launches `matmul_decode_exact` dispatches at m=2-4, with the caller's
14807    /// single shared q8_1 activation replacing two per-call re-quantizes (quantize_q8_1 is
14808    /// deterministic -> same bytes). None when ineligible (m outside 2..=4 / not both Q8_0 /
14809    /// in_f mismatch / MEMRA_MMVQ=0 / MEMRA_Q8_DUAL=0 / MEMRA_NO_BATCHED set — the last keeps
14810    /// dispatch parity: without batched kernels decode-exact runs grid.y=m MMVQ, and the fused
14811    /// twin must not introduce a batched program the reference path would not run).
14812    pub fn matmul_q8_fused2_t(
14813        &self,
14814        w0: &crate::model::GpuTensor,
14815        w1: &crate::model::GpuTensor,
14816        aq: &CudaSlice<i8>,
14817        ad: &CudaSlice<f32>,
14818        m: usize,
14819    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
14820        // m<=8 (lane/q27-deepdive, 2026-08-05): was 2..=4 (the verify tier's mcols 2/4). The
14821        // serving tick's mcols-8 tier now has its fused2_b8 wrapper, so c=5..8 batched decode
14822        // fuses too — same template body, still bit-identical to the two _b8 launches.
14823        if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
14824            return Ok(None);
14825        }
14826        // e4m3 twin: MEMRA_B8 parity — without it m=5..8 e4m3 decode runs the per-m grid.y=m path,
14827        // so the fused b8 launch would introduce a batched program the reference path would not run.
14828        if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
14829            if m > 4 && !Self::b8_enabled() {
14830                return Ok(None);
14831            }
14832            return Ok(Some(self.e4m3_fused2_t_core(
14833                p0.0,
14834                p1.0,
14835                aq,
14836                ad,
14837                m,
14838                w0.in_features(),
14839                p0.1,
14840                p1.1,
14841                p0.2,
14842                p0.3,
14843                p1.3,
14844            )?));
14845        }
14846        let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
14847            return Ok(None);
14848        };
14849        Ok(Some(self.q8_fused2_t_core(
14850            p0.0,
14851            p1.0,
14852            aq,
14853            ad,
14854            m,
14855            w0.in_features(),
14856            p0.1,
14857            p1.1,
14858            p0.2,
14859        )?))
14860    }
14861
14862    #[allow(clippy::too_many_arguments)]
14863    fn q8_fused2_t_core(
14864        &self,
14865        b0: &CudaSlice<u8>,
14866        b1: &CudaSlice<u8>,
14867        aq: &CudaSlice<i8>,
14868        ad: &CudaSlice<f32>,
14869        m: usize,
14870        in_f: usize,
14871        out0: usize,
14872        out1: usize,
14873        row_bytes: usize,
14874    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14875        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
14876        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14877        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14878        let f = self.func(match Self::batched_mcols(m) {
14879            2 => "qmatvec_q8_0_mmvq_fused2_b2",
14880            4 => "qmatvec_q8_0_mmvq_fused2_b4",
14881            // b8 = the SERVING tier (lane/q27-deepdive): c=5..8 batched decode.
14882            _ => "qmatvec_q8_0_mmvq_fused2_b8",
14883        });
14884        let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
14885        let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
14886        let cfg = LaunchConfig {
14887            grid_dim: (nb0 + nb1, 1, 1),
14888            block_dim: (32, ROWS_PER_BLOCK, 1),
14889            shared_mem_bytes: 0,
14890        };
14891        let (inf, o0, o1, mi, rbl) = (
14892            in_f as i32,
14893            out0 as i32,
14894            out1 as i32,
14895            m as i32,
14896            row_bytes as i64,
14897        );
14898        let __s_b = self.gpu.stream();
14899        let mut b = __s_b.launch_builder(&f);
14900        b.arg(b0)
14901            .arg(b1)
14902            .arg(aq)
14903            .arg(ad)
14904            .arg(&mut y0)
14905            .arg(&mut y1)
14906            .arg(&inf)
14907            .arg(&o0)
14908            .arg(&o1)
14909            .arg(&mi)
14910            .arg(&rbl);
14911        unsafe {
14912            b.launch(cfg)?;
14913        }
14914        Ok((y0, y1))
14915    }
14916
14917    /// Test entry for the kernel_check gate: fused2 batched from raw weight bytes (internal
14918    /// q8_1 quant of the [m, in_f] activation), no env gating.
14919    #[allow(clippy::too_many_arguments)]
14920    pub fn qmatvec_q8_fused2_t_raw(
14921        &self,
14922        b0: &CudaSlice<u8>,
14923        b1: &CudaSlice<u8>,
14924        x: &CudaSlice<f32>,
14925        m: usize,
14926        in_f: usize,
14927        out0: usize,
14928        out1: usize,
14929        row_bytes: usize,
14930    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14931        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14932        self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
14933    }
14934
14935    /// BATCHED twin of `matmul_q8_fused3` (wq+wk+wv at verify t=2-4). Same contract as
14936    /// `matmul_q8_fused2_t` with three ranges.
14937    #[allow(clippy::too_many_arguments)]
14938    pub fn matmul_q8_fused3_t(
14939        &self,
14940        w0: &crate::model::GpuTensor,
14941        w1: &crate::model::GpuTensor,
14942        w2: &crate::model::GpuTensor,
14943        aq: &CudaSlice<i8>,
14944        ad: &CudaSlice<f32>,
14945        m: usize,
14946    ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14947    {
14948        if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
14949            return Ok(None);
14950        }
14951        if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
14952            return Ok(Some(self.e4m3_fused3_t_core(
14953                p0.0,
14954                p1.0,
14955                p2.0,
14956                aq,
14957                ad,
14958                m,
14959                w0.in_features(),
14960                p0.1,
14961                p1.1,
14962                p2.1,
14963                p0.2,
14964                p0.3,
14965                p1.3,
14966                p2.3,
14967            )?));
14968        }
14969        let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
14970            return Ok(None);
14971        };
14972        Ok(Some(self.q8_fused3_t_core(
14973            p0.0,
14974            p1.0,
14975            p2.0,
14976            aq,
14977            ad,
14978            m,
14979            w0.in_features(),
14980            p0.1,
14981            p1.1,
14982            p2.1,
14983            p0.2,
14984        )?))
14985    }
14986
14987    #[allow(clippy::too_many_arguments)]
14988    fn q8_fused3_t_core(
14989        &self,
14990        b0: &CudaSlice<u8>,
14991        b1: &CudaSlice<u8>,
14992        b2: &CudaSlice<u8>,
14993        aq: &CudaSlice<i8>,
14994        ad: &CudaSlice<f32>,
14995        m: usize,
14996        in_f: usize,
14997        out0: usize,
14998        out1: usize,
14999        out2: usize,
15000        row_bytes: usize,
15001    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15002        const ROWS_PER_BLOCK: u32 = 4;
15003        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
15004        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
15005        let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
15006        let f = self.func(if Self::batched_mcols(m) == 2 {
15007            "qmatvec_q8_0_mmvq_fused3_b2"
15008        } else {
15009            "qmatvec_q8_0_mmvq_fused3_b4"
15010        });
15011        let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
15012        let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
15013        let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
15014        let cfg = LaunchConfig {
15015            grid_dim: (nb0 + nb1 + nb2, 1, 1),
15016            block_dim: (32, ROWS_PER_BLOCK, 1),
15017            shared_mem_bytes: 0,
15018        };
15019        let (inf, o0, o1, o2, mi, rbl) = (
15020            in_f as i32,
15021            out0 as i32,
15022            out1 as i32,
15023            out2 as i32,
15024            m as i32,
15025            row_bytes as i64,
15026        );
15027        let __s_b = self.gpu.stream();
15028        let mut b = __s_b.launch_builder(&f);
15029        b.arg(b0)
15030            .arg(b1)
15031            .arg(b2)
15032            .arg(aq)
15033            .arg(ad)
15034            .arg(&mut y0)
15035            .arg(&mut y1)
15036            .arg(&mut y2)
15037            .arg(&inf)
15038            .arg(&o0)
15039            .arg(&o1)
15040            .arg(&o2)
15041            .arg(&mi)
15042            .arg(&rbl);
15043        unsafe {
15044            b.launch(cfg)?;
15045        }
15046        Ok((y0, y1, y2))
15047    }
15048
15049    /// Test entry for the kernel_check gate: fused3 batched from raw weight bytes.
15050    #[allow(clippy::too_many_arguments)]
15051    pub fn qmatvec_q8_fused3_t_raw(
15052        &self,
15053        b0: &CudaSlice<u8>,
15054        b1: &CudaSlice<u8>,
15055        b2: &CudaSlice<u8>,
15056        x: &CudaSlice<f32>,
15057        m: usize,
15058        in_f: usize,
15059        out0: usize,
15060        out1: usize,
15061        out2: usize,
15062        row_bytes: usize,
15063    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15064        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15065        self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
15066    }
15067
15068    /// Rollback seam for the Q8_0 dense-FFN gate+up fusion arm in `matmul_pre_dual_noscale`
15069    /// (lane/q27-deepdive, 2026-08-05). Default ON; `MEMRA_Q8_FFN_FUSE2=0` restores the
15070    /// two-`matmul_pre_noscale` pair. Read once — the dispatch must not vary within a run.
15071    pub fn q8_ffn_fuse2_on(&self) -> bool {
15072        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15073        *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
15074    }
15075
15076    /// Eligibility + param extraction for the fused q8_0 launches: every tensor must be Quant Q8_0
15077    /// with macro-scale 1.0 (always true for GGUF q8_0; only NVFP4 carries scale) and share w[0]'s
15078    /// in_f (q8_0 row_bytes is a pure function of in_f, so equal in_f => equal row_bytes). MEMRA_MMVQ
15079    /// must be on: the fused body is the MMVQ kernel; without it decode m=1 runs dp4a and fusing
15080    /// would mix dispatch families (FP-order law). MEMRA_Q8_DUAL=0 = rollback seam.
15081    #[allow(clippy::type_complexity)]
15082    fn q8_fused_params<'w, const N: usize>(
15083        &self,
15084        ws: &[&'w crate::model::GpuTensor; N],
15085    ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
15086        use crate::model::GpuTensor;
15087        if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
15088            return None;
15089        }
15090        if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
15091            return None;
15092        }
15093        let in_f = ws[0].in_features();
15094        let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
15095        for (i, w) in ws.iter().enumerate() {
15096            match w {
15097                GpuTensor::Quant {
15098                    bytes,
15099                    qtype,
15100                    row_bytes,
15101                    scale,
15102                    ..
15103                } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
15104                    out[i] = Some((bytes, w.out_features(), *row_bytes))
15105                }
15106                _ => return None,
15107            }
15108        }
15109        Some(out.map(|o| o.unwrap()))
15110    }
15111
15112    /// Rollback seam for the F8-E4M3 launch-fusion arm (lane/fp8-decode-v1, 2026-08-05).
15113    /// Default ON; `MEMRA_E4M3_DUAL=0` restores the per-tensor m=1/batched launches.
15114    pub fn e4m3_dual_on(&self) -> bool {
15115        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15116        *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
15117    }
15118
15119    /// Eligibility + param extraction for the FUSED e4m3 launches — the QT_F8_E4M3 twin of
15120    /// `q8_fused_params`. Differences that are inherent to the dtype, not policy:
15121    ///   * each tensor carries its OWN per-tensor `weight_scale` (returned as the 4th field);
15122    ///     Q8_0 hard-requires scale==1.0 because it has no macro-scale at all.
15123    ///   * no MEMRA_MMVQ gate: `mmvq_supports` exempts QT_F8_E4M3 (the e4m3 mmvq family is that
15124    ///     dtype's ONLY int8-act kernel class), so the per-tensor fallback these fused kernels
15125    ///     replace is ALWAYS the same mmvq body under every env — the FP-order law holds.
15126    ///   * `row_bytes == in_f` is asserted rather than derived: the native-residency load arm keeps
15127    ///     the checkpoint's raw [out_f, in_f] rows, and a re-encoded slab must never reach here.
15128    /// Rejects any split-plane mirror (`rp`/`rp4`): there is no `_rp` e4m3 fused form, so fusing
15129    /// there would swap dispatch families mid-model. MEMRA_E4M3_DUAL=0 = rollback seam.
15130    #[allow(clippy::type_complexity)]
15131    fn e4m3_fused_params<'w, const N: usize>(
15132        &self,
15133        ws: &[&'w crate::model::GpuTensor; N],
15134    ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
15135        use crate::model::GpuTensor;
15136        if !self.e4m3_dual_on() {
15137            return None;
15138        }
15139        let in_f = ws[0].in_features();
15140        let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
15141        for (i, w) in ws.iter().enumerate() {
15142            match w {
15143                GpuTensor::Quant {
15144                    bytes,
15145                    qtype,
15146                    row_bytes,
15147                    scale,
15148                    rp,
15149                    rp4,
15150                    ..
15151                } if *qtype == QT_F8_E4M3
15152                    && w.in_features() == in_f
15153                    && *row_bytes == in_f
15154                    && !*rp
15155                    && rp4.is_none() =>
15156                {
15157                    out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
15158                }
15159                _ => return None,
15160            }
15161        }
15162        Some(out.map(|o| o.unwrap()))
15163    }
15164
15165    /// FUSED e4m3 m=1 PAIR. Block-offset split (`qmatvec_e4m3_mmvq_fused2`), per-tensor
15166    /// weight_scale folded at the write like the single-tensor `qmatvec_e4m3_mmvq` — so per
15167    /// (tensor,row) this is BIT-IDENTICAL to two separate m=1 launches, scale included.
15168    #[allow(clippy::too_many_arguments)]
15169    fn e4m3_fused2_core(
15170        &self,
15171        b0: &CudaSlice<u8>,
15172        b1: &CudaSlice<u8>,
15173        aq: &CudaSlice<i8>,
15174        ad: &CudaSlice<f32>,
15175        in_f: usize,
15176        out0: usize,
15177        out1: usize,
15178        row_bytes: usize,
15179        ws0: f32,
15180        ws1: f32,
15181    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15182        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
15183        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
15184        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
15185        let f = self.func("qmatvec_e4m3_mmvq_fused2");
15186        let mut y0 = self.alloc_uninit::<f32>(out0)?;
15187        let mut y1 = self.alloc_uninit::<f32>(out1)?;
15188        let cfg = LaunchConfig {
15189            grid_dim: (nb0 + nb1, 1, 1),
15190            block_dim: (32, ROWS_PER_BLOCK, 1),
15191            shared_mem_bytes: 0,
15192        };
15193        let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
15194        let __s_b = self.gpu.stream();
15195        let mut b = __s_b.launch_builder(&f);
15196        b.arg(b0)
15197            .arg(b1)
15198            .arg(aq)
15199            .arg(ad)
15200            .arg(&mut y0)
15201            .arg(&mut y1)
15202            .arg(&inf)
15203            .arg(&o0)
15204            .arg(&o1)
15205            .arg(&rbl)
15206            .arg(&ws0)
15207            .arg(&ws1);
15208        unsafe {
15209            b.launch(cfg)?;
15210        }
15211        Ok((y0, y1))
15212    }
15213
15214    /// FUSED e4m3 m=1 TRIPLE (`qmatvec_e4m3_mmvq_fused3`). Same contract as the pair.
15215    #[allow(clippy::too_many_arguments)]
15216    fn e4m3_fused3_core(
15217        &self,
15218        b0: &CudaSlice<u8>,
15219        b1: &CudaSlice<u8>,
15220        b2: &CudaSlice<u8>,
15221        aq: &CudaSlice<i8>,
15222        ad: &CudaSlice<f32>,
15223        in_f: usize,
15224        out0: usize,
15225        out1: usize,
15226        out2: usize,
15227        row_bytes: usize,
15228        ws0: f32,
15229        ws1: f32,
15230        ws2: f32,
15231    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15232        const ROWS_PER_BLOCK: u32 = 4;
15233        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
15234        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
15235        let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
15236        let f = self.func("qmatvec_e4m3_mmvq_fused3");
15237        let mut y0 = self.alloc_uninit::<f32>(out0)?;
15238        let mut y1 = self.alloc_uninit::<f32>(out1)?;
15239        let mut y2 = self.alloc_uninit::<f32>(out2)?;
15240        let cfg = LaunchConfig {
15241            grid_dim: (nb0 + nb1 + nb2, 1, 1),
15242            block_dim: (32, ROWS_PER_BLOCK, 1),
15243            shared_mem_bytes: 0,
15244        };
15245        let (inf, o0, o1, o2, rbl) = (
15246            in_f as i32,
15247            out0 as i32,
15248            out1 as i32,
15249            out2 as i32,
15250            row_bytes as i64,
15251        );
15252        let __s_b = self.gpu.stream();
15253        let mut b = __s_b.launch_builder(&f);
15254        b.arg(b0)
15255            .arg(b1)
15256            .arg(b2)
15257            .arg(aq)
15258            .arg(ad)
15259            .arg(&mut y0)
15260            .arg(&mut y1)
15261            .arg(&mut y2)
15262            .arg(&inf)
15263            .arg(&o0)
15264            .arg(&o1)
15265            .arg(&o2)
15266            .arg(&rbl)
15267            .arg(&ws0)
15268            .arg(&ws1)
15269            .arg(&ws2);
15270        unsafe {
15271            b.launch(cfg)?;
15272        }
15273        Ok((y0, y1, y2))
15274    }
15275
15276    /// BATCHED FUSED e4m3 pair (m=2..8). The batched kernels carry no `ws` arg (every batched
15277    /// kernel in the tree is scale-free), so each output takes its own `scale_inplace` — the
15278    /// SAME post-op the per-tensor batched dispatch applies, hence still bit-identical.
15279    #[allow(clippy::too_many_arguments)]
15280    fn e4m3_fused2_t_core(
15281        &self,
15282        b0: &CudaSlice<u8>,
15283        b1: &CudaSlice<u8>,
15284        aq: &CudaSlice<i8>,
15285        ad: &CudaSlice<f32>,
15286        m: usize,
15287        in_f: usize,
15288        out0: usize,
15289        out1: usize,
15290        row_bytes: usize,
15291        ws0: f32,
15292        ws1: f32,
15293    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15294        const ROWS_PER_BLOCK: u32 = 4;
15295        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
15296        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
15297        let f = self.func(match Self::batched_mcols(m) {
15298            2 => "qmatvec_e4m3_mmvq_fused2_b2",
15299            4 => "qmatvec_e4m3_mmvq_fused2_b4",
15300            _ => "qmatvec_e4m3_mmvq_fused2_b8",
15301        });
15302        let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
15303        let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
15304        let cfg = LaunchConfig {
15305            grid_dim: (nb0 + nb1, 1, 1),
15306            block_dim: (32, ROWS_PER_BLOCK, 1),
15307            shared_mem_bytes: 0,
15308        };
15309        let (inf, o0, o1, mi, rbl) = (
15310            in_f as i32,
15311            out0 as i32,
15312            out1 as i32,
15313            m as i32,
15314            row_bytes as i64,
15315        );
15316        let __s_b = self.gpu.stream();
15317        let mut b = __s_b.launch_builder(&f);
15318        b.arg(b0)
15319            .arg(b1)
15320            .arg(aq)
15321            .arg(ad)
15322            .arg(&mut y0)
15323            .arg(&mut y1)
15324            .arg(&inf)
15325            .arg(&o0)
15326            .arg(&o1)
15327            .arg(&mi)
15328            .arg(&rbl);
15329        unsafe {
15330            b.launch(cfg)?;
15331        }
15332        if ws0 != 1.0 {
15333            self.scale_inplace(&mut y0, ws0, m * out0)?;
15334        }
15335        if ws1 != 1.0 {
15336            self.scale_inplace(&mut y1, ws1, m * out1)?;
15337        }
15338        Ok((y0, y1))
15339    }
15340
15341    /// BATCHED FUSED e4m3 triple (m=2..4). Same contract as the batched pair.
15342    #[allow(clippy::too_many_arguments)]
15343    fn e4m3_fused3_t_core(
15344        &self,
15345        b0: &CudaSlice<u8>,
15346        b1: &CudaSlice<u8>,
15347        b2: &CudaSlice<u8>,
15348        aq: &CudaSlice<i8>,
15349        ad: &CudaSlice<f32>,
15350        m: usize,
15351        in_f: usize,
15352        out0: usize,
15353        out1: usize,
15354        out2: usize,
15355        row_bytes: usize,
15356        ws0: f32,
15357        ws1: f32,
15358        ws2: f32,
15359    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15360        const ROWS_PER_BLOCK: u32 = 4;
15361        let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
15362        let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
15363        let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
15364        let f = self.func(if Self::batched_mcols(m) == 2 {
15365            "qmatvec_e4m3_mmvq_fused3_b2"
15366        } else {
15367            "qmatvec_e4m3_mmvq_fused3_b4"
15368        });
15369        let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
15370        let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
15371        let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
15372        let cfg = LaunchConfig {
15373            grid_dim: (nb0 + nb1 + nb2, 1, 1),
15374            block_dim: (32, ROWS_PER_BLOCK, 1),
15375            shared_mem_bytes: 0,
15376        };
15377        let (inf, o0, o1, o2, mi, rbl) = (
15378            in_f as i32,
15379            out0 as i32,
15380            out1 as i32,
15381            out2 as i32,
15382            m as i32,
15383            row_bytes as i64,
15384        );
15385        let __s_b = self.gpu.stream();
15386        let mut b = __s_b.launch_builder(&f);
15387        b.arg(b0)
15388            .arg(b1)
15389            .arg(b2)
15390            .arg(aq)
15391            .arg(ad)
15392            .arg(&mut y0)
15393            .arg(&mut y1)
15394            .arg(&mut y2)
15395            .arg(&inf)
15396            .arg(&o0)
15397            .arg(&o1)
15398            .arg(&o2)
15399            .arg(&mi)
15400            .arg(&rbl);
15401        unsafe {
15402            b.launch(cfg)?;
15403        }
15404        if ws0 != 1.0 {
15405            self.scale_inplace(&mut y0, ws0, m * out0)?;
15406        }
15407        if ws1 != 1.0 {
15408            self.scale_inplace(&mut y1, ws1, m * out1)?;
15409        }
15410        if ws2 != 1.0 {
15411            self.scale_inplace(&mut y2, ws2, m * out2)?;
15412        }
15413        Ok((y0, y1, y2))
15414    }
15415
15416    /// BLOCK-128 e4m3 MMVQ launcher (`qmatvec_e4m3_blk_mmvq`, lane/fp8-blk128-decode 2026-08-05).
15417    /// The per-block-dequant twin of `qmatvec_mmvq`'s QT_F8_E4M3 arm: same grid/block decomposition
15418    /// (warp per output row, ROWS_PER_BLOCK warps per block, grid.y = m), same q8_1 activation, but
15419    /// the weight scale is a resident [rows, cols] f32 grid read per k128 block inside the kernel
15420    /// instead of one scalar folded at the write. It cannot share `qmatvec_mmvq`'s body because
15421    /// that launcher's arg list is fixed at (bytes, aq, ad, y, in_f, out_f, m, row_bytes [, scale]).
15422    ///
15423    /// `mr` and `rp` have no analogue here (no split-plane e4m3 layout exists), so there is exactly
15424    /// one kernel and no name table — a shape this cannot serve must be refused at LOAD, not here.
15425    pub fn qmatvec_e4m3_blk_mmvq(
15426        &self,
15427        bytes: &CudaSlice<u8>,
15428        aq: &CudaSlice<i8>,
15429        ad: &CudaSlice<f32>,
15430        scales: &CudaSlice<f32>,
15431        m: usize,
15432        in_f: usize,
15433        out_f: usize,
15434        row_bytes: usize,
15435        scale_cols: usize,
15436    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15437        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite output: skip memset
15438        self.qmatvec_e4m3_blk_mmvq_into(
15439            bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
15440        )?;
15441        Ok(y)
15442    }
15443
15444    /// Slot-fed twin of `qmatvec_e4m3_blk_mmvq` (caller-owned output; the alloc-free capture lane).
15445    #[allow(clippy::too_many_arguments)]
15446    pub fn qmatvec_e4m3_blk_mmvq_into(
15447        &self,
15448        bytes: &CudaSlice<u8>,
15449        aq: &CudaSlice<i8>,
15450        ad: &CudaSlice<f32>,
15451        scales: &CudaSlice<f32>,
15452        m: usize,
15453        in_f: usize,
15454        out_f: usize,
15455        row_bytes: usize,
15456        scale_cols: usize,
15457        y: &mut CudaSlice<f32>,
15458    ) -> Result<(), Box<dyn std::error::Error>> {
15459        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
15460        let f = self.func("qmatvec_e4m3_blk_mmvq");
15461        let cfg = LaunchConfig {
15462            grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
15463            block_dim: (32, ROWS_PER_BLOCK, 1), // warp-per-row
15464            shared_mem_bytes: 0,                // warp-only reduce
15465        };
15466        let (inf, outf, mi, rb, sc) = (
15467            in_f as i32,
15468            out_f as i32,
15469            m as i32,
15470            row_bytes as i64,
15471            scale_cols as i32,
15472        );
15473        let __s_b = self.gpu.stream();
15474        let mut b = __s_b.launch_builder(&f);
15475        b.arg(bytes)
15476            .arg(aq)
15477            .arg(ad)
15478            .arg(scales)
15479            .arg(&mut *y)
15480            .arg(&inf)
15481            .arg(&outf)
15482            .arg(&mi)
15483            .arg(&rb)
15484            .arg(&sc);
15485        unsafe {
15486            b.launch(cfg)?;
15487        }
15488        Ok(())
15489    }
15490
15491    /// BLOCK-128 e4m3 BATCHED matvec (lane/rp-on-st, 2026-08-06): the weight-read-once twin of
15492    /// `qmatvec_e4m3_blk_mmvq` for m=2..16. Per (token,row) BIT-IDENTICAL to the grid.y=m launch
15493    /// (same fmaf chain, same per-k32 `s * ad` fold, same warp reduce), so it inherits the
15494    /// decode-exactness contract while reading the weight ONCE for up to `mcols` columns instead
15495    /// of `m` times. `mcols` must be one of {2,4,8,16} and satisfy `mcols >= m`.
15496    #[allow(clippy::too_many_arguments)]
15497    pub fn qmatvec_e4m3_blk_mmvq_batched(
15498        &self,
15499        bytes: &CudaSlice<u8>,
15500        aq: &CudaSlice<i8>,
15501        ad: &CudaSlice<f32>,
15502        scales: &CudaSlice<f32>,
15503        m: usize,
15504        in_f: usize,
15505        out_f: usize,
15506        row_bytes: usize,
15507        scale_cols: usize,
15508        mcols: usize,
15509    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15510        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
15511        debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
15512        let name = match mcols {
15513            2 => "qmatvec_e4m3_blk_mmvq_b2",
15514            4 => "qmatvec_e4m3_blk_mmvq_b4",
15515            8 => "qmatvec_e4m3_blk_mmvq_b8",
15516            16 => "qmatvec_e4m3_blk_mmvq_b16",
15517            _ => {
15518                return Err(
15519                    format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
15520                );
15521            }
15522        };
15523        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15524        let f = self.func(name);
15525        let cfg = LaunchConfig {
15526            grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
15527            block_dim: (32, ROWS_PER_BLOCK, 1),
15528            shared_mem_bytes: 0,
15529        };
15530        let (inf, outf, mi, rb, sc) = (
15531            in_f as i32,
15532            out_f as i32,
15533            m as i32,
15534            row_bytes as i64,
15535            scale_cols as i32,
15536        );
15537        let __s_b = self.gpu.stream();
15538        let mut b = __s_b.launch_builder(&f);
15539        b.arg(bytes)
15540            .arg(aq)
15541            .arg(ad)
15542            .arg(scales)
15543            .arg(&mut y)
15544            .arg(&inf)
15545            .arg(&outf)
15546            .arg(&mi)
15547            .arg(&rb)
15548            .arg(&sc);
15549        unsafe {
15550            b.launch(cfg)?;
15551        }
15552        Ok(y)
15553    }
15554
15555    /// Test entry for the kernel_check exactness gate: the block-128 e4m3 batched MMVQ from raw
15556    /// bytes with an internal q8_1 quantize (mirrors `qmatvec_batched_raw`).
15557    #[allow(clippy::too_many_arguments)]
15558    pub fn qmatvec_e4m3_blk_batched_raw(
15559        &self,
15560        bytes: &CudaSlice<u8>,
15561        x: &CudaSlice<f32>,
15562        scales: &CudaSlice<f32>,
15563        m: usize,
15564        in_f: usize,
15565        out_f: usize,
15566        row_bytes: usize,
15567        scale_cols: usize,
15568        mcols: usize,
15569    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15570        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15571        self.qmatvec_e4m3_blk_mmvq_batched(
15572            bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
15573        )
15574    }
15575
15576    /// Test entry for the kernel_check exactness gate: the block-128 e4m3 MMVQ from raw bytes with
15577    /// an internal q8_1 quantize (mirrors `qmatvec_mmvq_raw`).
15578    #[allow(clippy::too_many_arguments)]
15579    pub fn qmatvec_e4m3_blk_mmvq_raw(
15580        &self,
15581        bytes: &CudaSlice<u8>,
15582        x: &CudaSlice<f32>,
15583        scales: &CudaSlice<f32>,
15584        m: usize,
15585        in_f: usize,
15586        out_f: usize,
15587        row_bytes: usize,
15588        scale_cols: usize,
15589    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15590        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15591        self.qmatvec_e4m3_blk_mmvq(
15592            bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
15593        )
15594    }
15595
15596    /// Test entries for the kernel_check bit-parity gate: fused e4m3 launches from raw weight
15597    /// bytes with internal q8_1 quantize, no env gating (mirrors `qmatvec_q8_fused*_raw`).
15598    #[allow(clippy::too_many_arguments)]
15599    pub fn qmatvec_e4m3_fused2_raw(
15600        &self,
15601        b0: &CudaSlice<u8>,
15602        b1: &CudaSlice<u8>,
15603        x: &CudaSlice<f32>,
15604        in_f: usize,
15605        out0: usize,
15606        out1: usize,
15607        row_bytes: usize,
15608        ws0: f32,
15609        ws1: f32,
15610    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15611        let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
15612        self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
15613    }
15614
15615    #[allow(clippy::too_many_arguments)]
15616    pub fn qmatvec_e4m3_fused3_raw(
15617        &self,
15618        b0: &CudaSlice<u8>,
15619        b1: &CudaSlice<u8>,
15620        b2: &CudaSlice<u8>,
15621        x: &CudaSlice<f32>,
15622        in_f: usize,
15623        out0: usize,
15624        out1: usize,
15625        out2: usize,
15626        row_bytes: usize,
15627        ws0: f32,
15628        ws1: f32,
15629        ws2: f32,
15630    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15631        let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
15632        self.e4m3_fused3_core(
15633            b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
15634        )
15635    }
15636
15637    #[allow(clippy::too_many_arguments)]
15638    pub fn qmatvec_e4m3_fused2_t_raw(
15639        &self,
15640        b0: &CudaSlice<u8>,
15641        b1: &CudaSlice<u8>,
15642        x: &CudaSlice<f32>,
15643        m: usize,
15644        in_f: usize,
15645        out0: usize,
15646        out1: usize,
15647        row_bytes: usize,
15648        ws0: f32,
15649        ws1: f32,
15650    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15651        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15652        self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
15653    }
15654
15655    #[allow(clippy::too_many_arguments)]
15656    pub fn qmatvec_e4m3_fused3_t_raw(
15657        &self,
15658        b0: &CudaSlice<u8>,
15659        b1: &CudaSlice<u8>,
15660        b2: &CudaSlice<u8>,
15661        x: &CudaSlice<f32>,
15662        m: usize,
15663        in_f: usize,
15664        out0: usize,
15665        out1: usize,
15666        out2: usize,
15667        row_bytes: usize,
15668        ws0: f32,
15669        ws1: f32,
15670        ws2: f32,
15671    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15672        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15673        self.e4m3_fused3_t_core(
15674            b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
15675        )
15676    }
15677
15678    /// THE single dispatch point for `QT_F8_E4M3_BLK` from a PRE-QUANTIZED q8_1 activation
15679    /// (lane/fp8-blk128-decode). Every `matmul_pre`-family entry calls this first, so the block-128
15680    /// class has exactly ONE code path across `matmul`, `matmul_pre`, `matmul_pre_noscale`,
15681    /// `matmul_decode_exact` and `matmul_decode_exact_pre` — the same kernel at the same grid for
15682    /// every m, which is what makes verify == decode bit-for-bit at every tier for free.
15683    ///
15684    /// Returns None for any other qtype (the caller continues its normal dispatch). The `blk: Some`
15685    /// pattern is part of the match, not an unwrap: qtype and grid presence are set together in the
15686    /// one residency arm that builds this tensor, and a qtype-without-grid would be a construction
15687    /// bug — better to fall through and hit a loud refusal than to unwrap a None here.
15688    fn try_e4m3_blk_pre(
15689        &self,
15690        w: &crate::model::GpuTensor,
15691        aq: &CudaSlice<i8>,
15692        ad: &CudaSlice<f32>,
15693        m: usize,
15694    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
15695        use crate::model::GpuTensor;
15696        if let GpuTensor::Quant {
15697            bytes,
15698            qtype,
15699            row_bytes,
15700            blk: Some(g),
15701            ..
15702        } = w
15703        {
15704            if *qtype == QT_F8_E4M3_BLK {
15705                // BATCHED tier m=2..16 (lane/rp-on-st): weight read ONCE for up to mcols columns
15706                // instead of m grid.y re-reads. Bit-identical per (token,row) to the grid.y=m form
15707                // below, so the decode-exactness contract is preserved at every width. Gated by
15708                // the same seams the other batched families honor (MEMRA_NO_BATCHED, MEMRA_B8) so
15709                // one rollback door covers every dtype's batched tier.
15710                if (2..=16).contains(&m)
15711                    && std::env::var("MEMRA_NO_BATCHED").is_err()
15712                    && (m <= 4 || Self::b8_enabled())
15713                {
15714                    let mcols = Self::batched_mcols(m);
15715                    return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
15716                        bytes,
15717                        aq,
15718                        ad,
15719                        &g.scales,
15720                        m,
15721                        w.in_features(),
15722                        w.out_features(),
15723                        *row_bytes,
15724                        g.cols,
15725                        mcols,
15726                    )?));
15727                }
15728                return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
15729                    bytes,
15730                    aq,
15731                    ad,
15732                    &g.scales,
15733                    m,
15734                    w.in_features(),
15735                    w.out_features(),
15736                    *row_bytes,
15737                    g.cols,
15738                )?));
15739            }
15740        }
15741        Ok(None)
15742    }
15743
15744    /// PREFILL (m >= GEMM_M_THRESHOLD) for `QT_F8_E4M3_BLK` — DEQUANT-PER-CALL to the Q8_0 slab
15745    /// this class's residency replaced, then the ordinary Q8_0 prefill dispatch on the transient.
15746    ///
15747    /// WHY THIS EXISTS AT ALL, i.e. the regression it prevents: the decode kernel is a warp-per-row
15748    /// GEMV. At grid.y=m it re-reads the whole weight once PER TOKEN, so letting a 512-token prefill
15749    /// chunk reach it would be a ~500x weight-traffic blowup on the single most bandwidth-bound part
15750    /// of the forward. Native residency is a DECODE win and must not be paid for in prefill, so
15751    /// prefill keeps the floor's arithmetic and the floor's kernels.
15752    ///
15753    /// WHY DEQUANT-PER-CALL rather than a second resident slab: a resident slab is dual residency —
15754    /// it gives back the entire 1.0-vs-1.0625 B/weight win this lane exists to capture (and then
15755    /// some, since the e4m3 copy stays too). The transient costs one linear device pass per
15756    /// (projection, prefill call) and frees immediately.
15757    ///
15758    /// NUMERICALLY IT IS THE FLOOR, EXACTLY: `fp8_blk_dequant_q8_0` is the merged ARM B' kernel,
15759    /// gate-proven BYTE-IDENTICAL to the host dequant+re-encode (kernel-check `fp8-blk-gpu`). So the
15760    /// slab these bytes form is bit-for-bit the slab the `MEMRA_ST_E4M3_BLK=0` arm makes resident,
15761    /// and every prefill kernel downstream sees identical input — prefill logits under this lane are
15762    /// bit-identical to prefill logits under the floor, which is what makes the decode A/B a clean
15763    /// single-variable comparison instead of a two-variable one.
15764    ///
15765    /// WHAT IT COSTS, MEASURED, AND WHY THAT COST IS MOSTLY STRUCTURAL (27B block-128 ckpt, pp512,
15766    /// this rig = RTX 5090 Laptop, ~896 GB/s GDDR7). This arm makes prefill move the weight THREE
15767    /// times instead of once: read 6.88 GB of e4m3, write 7.31 GB of Q8_0, then the MMQ reads that
15768    /// 7.31 GB back. The two extra passes are 14.19 GB = 15.8 ms at this card's roofline against a
15769    /// ~332 ms pp512, i.e. **~-4.5% pp is a floor no kernel tuning can remove** — only deleting the
15770    /// dequant can. Measured: the dequant kernel costs 27.9 ms/pass (nsys, 208 projections) after
15771    /// the 2026-08-05 vector rewrite (was 66.5 ms at one byte per thread), and e2e pp512 is
15772    /// 1451.4 vs the slab arm's 1541.6 tok/s = -5.8% (N=3 interleaved pairs). So ~1.3pp of the
15773    /// -5.8% is residual kernel inefficiency and ~4.5pp is the extra traffic itself.
15774    ///
15775    /// SO THE DEQUANT IS NO LONGER THE DEFAULT ROUTE — it is the FALLBACK. The per-block FP8 MMQ
15776    /// tile (`try_fp8_blk_mmq`) consumes the resident e4m3 bytes + grid DIRECTLY, deleting both extra
15777    /// passes, and since 2026-08-05 it runs FIRST and by default for the native-resident source
15778    /// (`fp8_blk_mmq_native_enabled`; `MEMRA_FP8_MMQ=0` is the seam back to this dequant). On paper
15779    /// the trade was unassumable — lane/fp8-mmq-v2 measured that tile at 0.85-1.09x the Q8_0 MMQ
15780    /// floor GEMM-only, so it swapped a -4.5% traffic cost for a 0-to-15% GEMM cost of unknown sign.
15781    /// Measured on the 27B (3 arms interleaved, N=3, research/fp8blk-20260805/VERDICT.md): slab
15782    /// 1540.5 / this dequant 1449.1 / the tile 1553.3 tok/s, min(tile) > max(slab). The tile wins
15783    /// because v2's denominator had its slab already resident while this class's floor must build it
15784    /// every call; same tile, opposite sign, because the question changed.
15785    ///
15786    /// THIS ARM STILL RUNS, and is not dead code: every `try_fp8_blk_mmq` precondition (in_f % 16,
15787    /// grid dims vs shape, per-tensor scale == 1.0, the e4m3-NaN scan) refuses by falling through to
15788    /// here, so a checkpoint the tile cannot take keeps exact prefill on the floor's own bits rather
15789    /// than losing the class. It is also what `MEMRA_FP8_MMQ=0` reverts to.
15790    fn try_e4m3_blk_prefill(
15791        &self,
15792        w: &crate::model::GpuTensor,
15793        x: &CudaSlice<f32>,
15794        m: usize,
15795    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
15796        use crate::model::GpuTensor;
15797        let GpuTensor::Quant {
15798            bytes,
15799            qtype,
15800            blk: Some(g),
15801            ..
15802        } = w
15803        else {
15804            return Ok(None);
15805        };
15806        if *qtype != QT_F8_E4M3_BLK {
15807            return Ok(None);
15808        }
15809        // NO-DEQUANT ROUTE, THE DEFAULT (MEMRA_FP8_MMQ=0 reverts): the per-block MMQ tile eats the
15810        // resident e4m3 bytes and grid as-is, so neither extra weight pass happens. Its own
15811        // preconditions (in_f % 16, grid dims, scale == 1.0, no e4m3 NaN code) can refuse — fall
15812        // through to the dequant below when they do, never silently produce nothing.
15813        if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
15814            return Ok(Some(y));
15815        }
15816        let (in_f, out_f) = (w.in_features(), w.out_features());
15817        let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
15818        let tmp = GpuTensor::Quant {
15819            bytes: slab,
15820            qtype: QT_Q8_0,
15821            row_bytes: in_f / 32 * 34,
15822            ne: vec![in_f as u64, out_f as u64],
15823            scale: 1.0,
15824            rp: false,
15825            #[cfg(memra_cutlass)]
15826            cutlass: None,
15827            fp8: None,
15828            blk: None,
15829            f16: None,
15830            rp4: None,
15831        };
15832        // Recursion terminates: `tmp` is QT_Q8_0 with `blk: None`, so it cannot re-enter this arm.
15833        Ok(Some(self.matmul(&tmp, x, m)?))
15834    }
15835
15836    pub fn matmul_pre_noscale(
15837        &self,
15838        w: &crate::model::GpuTensor,
15839        aq: &CudaSlice<i8>,
15840        ad: &CudaSlice<f32>,
15841        m: usize,
15842    ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
15843        use crate::model::GpuTensor;
15844        // BLOCK-128 e4m3: every scale factor is folded inside the kernel per k128, so the
15845        // "separable post-op scale" this entry exists to defer is 1.0 — return it explicitly
15846        // rather than let the tail below refuse and cost the caller a re-dispatch.
15847        if m == 1 {
15848            if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
15849                return Ok(Some((y, 1.0)));
15850            }
15851        }
15852        // Only the m==1 fast path applies the scale as a separable post-op; bail everywhere else.
15853        if m != 1 || !self.uses_q8_1_fast(w) {
15854            return Ok(None);
15855        }
15856        let in_f = w.in_features();
15857        let out_f = w.out_features();
15858        let (bytes, qtype, row_bytes, scale, rp) = match w {
15859            GpuTensor::Quant {
15860                bytes,
15861                qtype,
15862                row_bytes,
15863                scale,
15864                rp,
15865                ..
15866            } => (bytes, *qtype, *row_bytes, *scale, *rp),
15867            _ => return Ok(None),
15868        };
15869        // MMVQ warp-per-row (scale==1.0 passed -> kernel skips its internal scale; we return scale).
15870        if self.mmvq_supports(qtype) {
15871            // Q4_0 split-plane mirror (dp4a fallback below keeps the raw GGUF bytes).
15872            let (mbytes, mrp) = match w {
15873                GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
15874                _ => (bytes, rp),
15875            };
15876            let y = self.qmatvec_mmvq(
15877                mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, /*scale*/ 1.0, mrp,
15878            )?;
15879            return Ok(Some((y, scale)));
15880        }
15881        // dp4a fallback: same launch as matmul_pre but WITHOUT the post scale_inplace.
15882        let name = match qtype {
15883            QT_Q8_0 => "qmatvec_q8_0_dp4a",
15884            QT_Q4_K => "qmatvec_q4_K_dp4a",
15885            QT_Q6_K => "qmatvec_q6_K_dp4a",
15886            QT_Q5_K => "qmatvec_q5_K_dp4a",
15887            QT_Q3_K => "qmatvec_q3_K_dp4a",
15888            QT_NVFP4 => {
15889                if rp {
15890                    "qmatvec_nvfp4_dp4a_rp"
15891                } else {
15892                    "qmatvec_nvfp4_dp4a"
15893                }
15894            }
15895            QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
15896            _ => return Ok(None),
15897        };
15898        let f = self.func(name);
15899        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15900        let cfg = LaunchConfig {
15901            grid_dim: (out_f as u32, m as u32, 1),
15902            block_dim: (128, 1, 1),
15903            shared_mem_bytes: 0,
15904        };
15905        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
15906        let __s_b = self.gpu.stream();
15907        let mut b = __s_b.launch_builder(&f);
15908        b.arg(bytes)
15909            .arg(aq)
15910            .arg(ad)
15911            .arg(&mut y)
15912            .arg(&inf)
15913            .arg(&outf)
15914            .arg(&mi)
15915            .arg(&rb);
15916        unsafe {
15917            b.launch(cfg)?;
15918        }
15919        Ok(Some((y, scale)))
15920    }
15921
15922    /// True if `qtype` has a warp-per-row MMVQ decode kernel AND MEMRA_MMVQ is set. Only the 4
15923    /// daily-hot dtypes (Q8_0, Q4_K, Q6_K, NVFP4) — others keep the _dp4a matvec (oracle/fallback).
15924    pub fn mmvq_supports(&self, qtype: i32) -> bool {
15925        // DEFAULT ON since 2026-07-08 (MEMRA_MMVQ=0 reverts to the _dp4a matvec class).
15926        // QT_F8_E4M3 is exempt from the MEMRA_MMVQ=0 escape: the e4m3 mmvq family is that dtype's
15927        // ONLY int8-act kernel class (there is no _dp4a twin), so its m=1/verify/batched dispatch
15928        // is a pure function of the dtype — the decode-parity law holds under every env.
15929        if qtype == QT_F8_E4M3 {
15930            return true;
15931        }
15932        if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
15933            return false;
15934        }
15935        matches!(
15936            qtype,
15937            QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
15938        )
15939    }
15940
15941    /// PERF-3 warp-per-row MMVQ launcher (decode m=1 hot path). block=(32,ROWS_PER_BLOCK,1):
15942    /// one warp owns one output row, warp-only __shfl reduction (no smem barrier). Bit-equivalent
15943    /// to qmatvec_*_dp4a up to f32 reduction order. Pre-quantized q8_1 activation (aq,ad). NVFP4
15944    /// per-tensor macro-scale applied post (scale==1.0 for other dtypes -> no-op).
15945    pub fn qmatvec_mmvq(
15946        &self,
15947        bytes: &CudaSlice<u8>,
15948        aq: &CudaSlice<i8>,
15949        ad: &CudaSlice<f32>,
15950        m: usize,
15951        in_f: usize,
15952        out_f: usize,
15953        qtype: i32,
15954        row_bytes: usize,
15955        scale: f32,
15956        rp: bool,
15957    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15958        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite GEMM output: skip memset
15959        self.qmatvec_mmvq_into(
15960            bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
15961        )?;
15962        Ok(y)
15963    }
15964
15965    /// Slot-fed MMVQ twin (alloc-free capture lane): full policy body, caller-owned output.
15966    #[allow(clippy::too_many_arguments)]
15967    pub fn qmatvec_mmvq_into(
15968        &self,
15969        bytes: &CudaSlice<u8>,
15970        aq: &CudaSlice<i8>,
15971        ad: &CudaSlice<f32>,
15972        m: usize,
15973        in_f: usize,
15974        out_f: usize,
15975        qtype: i32,
15976        row_bytes: usize,
15977        scale: f32,
15978        rp: bool,
15979        y: &mut CudaSlice<f32>,
15980    ) -> Result<(), Box<dyn std::error::Error>> {
15981        debug_assert!(y.len() >= m * out_f);
15982        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
15983        // SMALL-SHAPE GRID FILL (H100 lane, 2026-07-26 microbench: attn qkv out_f=2048 =
15984        // 0.97 waves at the 4-warp block -> 66% of peak). The g2 twin (2 warps/block)
15985        // doubles the grid when the 4-warp launch would be sub-wave; per-row program
15986        // identical -> bit-identical. MEMRA_Q80_G2=0 reverts.
15987        if qtype == QT_Q8_0
15988            && rp
15989            && m == 1
15990            && out_f >= 64
15991            && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
15992            && {
15993                static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15994                *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
15995            }
15996        {
15997            let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
15998            let cfg = LaunchConfig {
15999                grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
16000                block_dim: (32, 2, 1),
16001                shared_mem_bytes: 0,
16002            };
16003            let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
16004            let __s_b = self.gpu.stream();
16005            let mut b = __s_b.launch_builder(&f);
16006            b.arg(bytes)
16007                .arg(aq)
16008                .arg(ad)
16009                .arg(&mut *y)
16010                .arg(&inf)
16011                .arg(&outf)
16012                .arg(&mi)
16013                .arg(&rb);
16014            unsafe {
16015                b.launch(cfg)?;
16016            }
16017            if scale != 1.0 {
16018                self.scale_inplace(y, scale, out_f)?;
16019            }
16020            return Ok(());
16021        }
16022        // Multi-row-per-warp (mr2) policy, fixed since the 2026-07 sweeps (the MEMRA_MMVQ_MR
16023        // override + mr4 kernel were retired 2026-07-08 — mr4 regressed on register pressure and
16024        // crashed under rp; q4_K/q6_K mr2 measured flat, "no gain = no change"):
16025        //   NVFP4 m=1 -> mr2 (clean +1-2% on 9B: RPW acc chains hide the weight-load latency
16026        //     that pins the single-row kernel at 30-46% DRAM). Bit-identical per row.
16027        //   Q5_K m=1 -> mr2 (2026-07-05: the FR-Spec trimmed draft head is Q5_K 32768 rows = 8%
16028        //     of the 27B p3 spec wall; latency-bound like the other k-quants pre-fix).
16029        //   Q4_K/Q6_K m=1 -> single-row (mr2 measured +0.7% / flat — weight-bandwidth-bound).
16030        let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
16031            2
16032        } else {
16033            1
16034        };
16035        // Q4_0 mr (gemma trunk): DEFAULT 1 since 2026-07-13 (MEMRA_Q40_MR=2 reverts) — the
16036        // mr1 rp twin doubles the block count and wins the tail-quantization/latency battle
16037        // on every gemma model (E4B +3.75%: 198.9 vs 191.7; 26B +0.7%; 31B +0.9%; N=2-3
16038        // valid-window interleaved, bit-identical per row — same dot program).
16039        if m == 1 && qtype == QT_Q4_0 {
16040            static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
16041            // shape policy PROBED NEGATIVE (2026-07-13): tall-only mr1 197.2 vs
16042            // mr1-everywhere 198.7 — mr1 wins wide-output shapes too; arm removed.
16043            mr = *Q40MR.get_or_init(|| {
16044                std::env::var("MEMRA_Q40_MR")
16045                    .ok()
16046                    .and_then(|v| v.parse().ok())
16047                    .unwrap_or(1)
16048            });
16049        }
16050        // q5issue lane (2026-07-08): MEMRA_Q5K_ISSUE swaps the q5_K m=1 mmvq kernels for the
16051        // issue-reduced `_il` bodies (uint4 header/qh/qs loads + branchless scale decode —
16052        // cuts ~34 LDG.U16 + ~5 LDG.U8 + a warp-divergent scale branch per 32-elem group-row
16053        // to 5 LDG.128). Bit-identical per (token,row) to the reference kernels.
16054        // `1` = shape-aware policy (N=3 clock-locked micro-bench, mem P0, synthetic real shapes):
16055        //   out_f <= 65536 (trunk/frspec regime): il at the default mr — mr2_il -9.5%/-10.5%
16056        //     on 4096x4096/4096x8192, -3.1% on the 32768 frspec head vs the mr2-ref default;
16057        //   out_f > 65536 (the 248320-row 27B lm_head, already ~97% of the mem wall): mr2_il
16058        //     REGRESSES +22% there but mr1_il wins -2.1% vs the mr2-ref default -> force mr=1.
16059        // `2` = force il at the current mr for EVERY shape (A/B probe seam). Default OFF.
16060        let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
16061        let q5_force = q5_mode.as_deref() == Some("2");
16062        // DEFAULT ON since 2026-07-08 (MEMRA_Q5K_ISSUE=0 reverts): +1.8% 9B plain e2e N=3
16063        // (128.2 -> 130.4), 27B flat (its big head is already at the mem wall), all gates green.
16064        let q5_il = qtype == QT_Q5_K
16065            && m == 1
16066            && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
16067        if q5_il && !q5_force && out_f > 65536 {
16068            mr = 1;
16069        }
16070        // Q4_0 split-plane rp: mr2 default; MEMRA_Q40_MR=1 reaches the mr1 rp twin
16071        // (2026-07-13 — the tall-input/short-output tail-quantization probe).
16072        if qtype == QT_Q4_0 && rp && mr != 1 {
16073            mr = 2;
16074        }
16075        // Q8_0 rp (H100 lane): mr1 default — the q4_0 mr2 recipe MEASURED NEGATIVE on H100
16076        // (2026-07-26 N=3: mr1 186.2 vs mr2 171.5 tok/s; halving the grid on 132 SMs costs
16077        // more than 2-row ILP buys). mr2 kernel stays behind MEMRA_Q80_MR=2 for the corpus.
16078        if qtype == QT_Q8_0 && rp {
16079            static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
16080            mr = *Q80MR.get_or_init(|| {
16081                std::env::var("MEMRA_Q80_MR")
16082                    .ok()
16083                    .and_then(|v| v.parse().ok())
16084                    .unwrap_or(1)
16085            });
16086        }
16087        let name = match (qtype, mr, rp) {
16088            (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
16089            (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
16090            (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
16091            (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
16092            (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
16093            (QT_Q5_K, 2, _) => {
16094                if q5_il {
16095                    "qmatvec_q5_K_mmvq_mr2_il"
16096                } else {
16097                    "qmatvec_q5_K_mmvq_mr2"
16098                }
16099            }
16100            (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
16101            // rpca (cp.async-staged weight ring): MEASURED NEGATIVE on H100 for Q8_0
16102            // (2026-07-26 N=3: 181.8 vs plain rp 185.5 — the smem round-trip exceeds the
16103            // latency it hides for 8-bit direct-dp4a; the NVFP4 win case overlaps table
16104            // decode with half the bytes). OPT-IN via MEMRA_Q80_CA=1 for the corpus.
16105            (QT_Q8_0, _, true)
16106                if in_f % 1024 == 0 && {
16107                    static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16108                    *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
16109                } =>
16110            {
16111                "qmatvec_q8_0_mmvq_rpca"
16112            }
16113            (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
16114            (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
16115            // K-quant split-plane twins (H100 K-quant coalescing fix, 2026-08-01): the rp4
16116            // mirror routes here; GGUF layout keeps the plain kernels. rp bytes MUST never
16117            // reach a GGUF-layout kernel or vice versa.
16118            (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
16119            (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
16120            (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
16121            (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
16122            (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
16123            (QT_Q5_K, _, _) => {
16124                if q5_il {
16125                    "qmatvec_q5_K_mmvq_il"
16126                } else {
16127                    "qmatvec_q5_K_mmvq"
16128                }
16129            }
16130            (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
16131            (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
16132            (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
16133            _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
16134        };
16135        let f = self.func(name);
16136        // each block still has ROWS_PER_BLOCK warps; with mr rows/warp it covers ROWS_PER_BLOCK*mr rows.
16137        let rows_per_block = ROWS_PER_BLOCK * mr;
16138        let cfg = LaunchConfig {
16139            grid_dim: (
16140                (out_f as u32 + rows_per_block - 1) / rows_per_block,
16141                m as u32,
16142                1,
16143            ),
16144            block_dim: (32, ROWS_PER_BLOCK, 1), // warp-per-row (x mr rows each)
16145            shared_mem_bytes: 0,                // warp-only reduce at m=1
16146        };
16147        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16148        let __s_b = self.gpu.stream();
16149        let mut b = __s_b.launch_builder(&f);
16150        // NVFP4 + e4m3 mmvq kernels take the macro-scale as a fused epilogue arg (applied at the
16151        // write — bit-identical to the old separate scale_inplace pass, minus one launch per matvec:
16152        // 53 scale launches/token on the 9B; for e4m3 the scale is the checkpoint's per-tensor f32
16153        // weight_scale). Other mmvq kernels keep the 8-arg signature.
16154        if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
16155            // PDL wave-B: the nvfp4 mr2_rp single (gemma wo / generic rp singles) joins
16156            // the wave-A launch class — 9-arg flavor (fused macro-scale epilogue).
16157            if Self::pdl_on()
16158                && Self::pdl_mmvq_on()
16159                && Self::pdl_nvfp4q8_on()
16160                && name == "qmatvec_nvfp4_mmvq_mr2_rp"
16161            {
16162                use cudarc::driver::{DevicePtr, DevicePtrMut};
16163                let s = &self.gpu.stream();
16164                let (pw, _g0) = bytes.device_ptr(s);
16165                let (paq, _g1) = aq.device_ptr(s);
16166                let (pad, _g2) = ad.device_ptr(s);
16167                let (py, _g3) = y.device_ptr_mut(s);
16168                let mut ps = [
16169                    &pw as *const _ as *mut std::ffi::c_void,
16170                    &paq as *const _ as *mut _,
16171                    &pad as *const _ as *mut _,
16172                    &py as *const _ as *mut _,
16173                    &inf as *const _ as *mut _,
16174                    &outf as *const _ as *mut _,
16175                    &mi as *const _ as *mut _,
16176                    &rb as *const _ as *mut _,
16177                    &scale as *const _ as *mut _,
16178                ];
16179                unsafe {
16180                    self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
16181                }
16182                return Ok(());
16183            }
16184            b.arg(bytes)
16185                .arg(aq)
16186                .arg(ad)
16187                .arg(&mut *y)
16188                .arg(&inf)
16189                .arg(&outf)
16190                .arg(&mi)
16191                .arg(&rb)
16192                .arg(&scale);
16193            unsafe {
16194                b.launch(cfg)?;
16195            }
16196        } else if Self::pdl_on()
16197            && Self::pdl_mmvq_on()
16198            && (matches!(
16199                name,
16200                "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
16201            ) || (Self::pdl_nvfp4q8_on()
16202                && matches!(name, "qmatvec_q8_0_mmvq_rp" | "qmatvec_q8_0_mmvq_mr2_rp")))
16203        {
16204            // PDL wave-A (2026-07-23): the two decode-hot single-matvec kernels carry
16205            // MEMRA_PDL_ENTRY — grid launches while the producer drains. ONLY the marked
16206            // names may take this launch (unmarked kernels would read unordered).
16207            {
16208                use cudarc::driver::{DevicePtr, DevicePtrMut};
16209                let s = &self.gpu.stream();
16210                let (pw, _g0) = bytes.device_ptr(s);
16211                let (paq, _g1) = aq.device_ptr(s);
16212                let (pad, _g2) = ad.device_ptr(s);
16213                let (py, _g3) = y.device_ptr_mut(s);
16214                let mut ps = [
16215                    &pw as *const _ as *mut std::ffi::c_void,
16216                    &paq as *const _ as *mut _,
16217                    &pad as *const _ as *mut _,
16218                    &py as *const _ as *mut _,
16219                    &inf as *const _ as *mut _,
16220                    &outf as *const _ as *mut _,
16221                    &mi as *const _ as *mut _,
16222                    &rb as *const _ as *mut _,
16223                ];
16224                unsafe {
16225                    self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
16226                }
16227            }
16228            if scale != 1.0 {
16229                self.scale_inplace(y, scale, m * out_f)?;
16230            }
16231        } else {
16232            b.arg(bytes)
16233                .arg(aq)
16234                .arg(ad)
16235                .arg(&mut *y)
16236                .arg(&inf)
16237                .arg(&outf)
16238                .arg(&mi)
16239                .arg(&rb);
16240            unsafe {
16241                b.launch(cfg)?;
16242            }
16243            if scale != 1.0 {
16244                self.scale_inplace(y, scale, m * out_f)?;
16245            }
16246        }
16247        Ok(())
16248    }
16249
16250    /// Test entry for the kernel_check bit-equivalence gate: run the warp-per-row MMVQ directly
16251    /// from raw weight bytes (quantize the f32 activation `x` to q8_1 internally). NVFP4 per-tensor
16252    /// macro-scale is NOT applied (caller compares bare, like qmatvec_*_fast). Mirrors qmatvec_gemm_raw.
16253    pub fn qmatvec_mmvq_raw(
16254        &self,
16255        bytes: &CudaSlice<u8>,
16256        x: &CudaSlice<f32>,
16257        m: usize,
16258        in_f: usize,
16259        out_f: usize,
16260        qtype: i32,
16261        row_bytes: usize,
16262        rp: bool,
16263    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16264        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
16265        self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
16266    }
16267
16268    /// True if `qtype` has a batched weight-resident (`_b2`/`_b4`) matvec kernel. These mirror the
16269    /// `_mmvq` kernels but iterate the m token columns INSIDE one warp/row, so the weight bytes leave
16270    /// HBM/L2 once for m tokens (vs grid.y=m re-reading m times). The 5 daily-hot dtypes have them.
16271    pub fn batched_supports(&self, qtype: i32) -> bool {
16272        matches!(
16273            qtype,
16274            QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
16275        )
16276    }
16277
16278    /// IQ4_XS trunk fast seam: MEMRA_IQ_FAST=0 reverts non-expert IQ4_XS matmuls to the Stage-A
16279    /// f32 oracle path. Default ON since 2026-08-02 (research/kat-anomaly-20260802/): the old
16280    /// opt-in default left every IQ4_XS-trunk artifact (KAT-Coder IQ4_XS: attn_qkv/attn_gate/
16281    /// ssm_out/shexp, ~0.52GB re-read per decode tick) on the oracle kernel — decode 106.7 ->
16282    /// 193.4 tok/s (x5 interleaved), pp512 228 -> 697, same bytes, via qmatvec_iq4_XS_dp4a. The
16283    /// supported artifacts carry IQ4_XS only in EXPERT banks (their own dispatch, not this seam),
16284    /// so this admission is dispatch-unchanged for every non-IQ4_XS-trunk model.
16285    pub fn iq_fast_enabled() -> bool {
16286        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16287        *ON.get_or_init(|| {
16288            std::env::var("MEMRA_IQ_FAST")
16289                .map(|v| v != "0")
16290                .unwrap_or(true)
16291        })
16292    }
16293
16294    /// b8 tier seam: MEMRA_B8=0 keeps m=5..8 on the per-m grid.y=m path (m=2..4 batched dispatch
16295    /// unaffected). Default ON — the K=4..7 spec-verify weight-read-once fix.
16296    pub fn b8_enabled() -> bool {
16297        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16298        *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
16299    }
16300
16301    /// Compile-time column batch for a runtime m: 2 -> b2, 3..4 -> b4, 5..8 -> b8.
16302    pub fn batched_mcols(m: usize) -> usize {
16303        if m == 2 {
16304            2
16305        } else if m <= 4 {
16306            4
16307        } else if m <= 8 {
16308            8
16309        } else {
16310            16
16311        }
16312    }
16313
16314    /// Kernel name for the batched matvec of `(qtype, mcols)`. mcols ∈ {2,4,8}. The b8 tier is the
16315    /// K=4..7 spec-verify fix (T=5..8): pre-b8 those T fell to grid.y=m per-row MMVQ = m full
16316    /// weight reads/launch — the measured 27B K=4 cliff (101 -> 73 tok/s at p3 despite acceptance
16317    /// holding 54%). One b8 launch reads the weight ONCE for up to 8 columns (c >= m masked).
16318    fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
16319        Some(match (qtype, mcols) {
16320            (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
16321            (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
16322            (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
16323            // b16 now has BOTH forms (lane/rp-on-st, 2026-08-06). It used to be rp-ONLY, which
16324            // made the q8rp mirror the exact-16 tier's admission ticket for any model carrying a
16325            // single Q8_0 matmul — measured as the FP8-ST refusal (`L0.ssm_beta qtype=0
16326            // rp4=false`, 96 t / 23.9 MiB = 0.143% of resident weight). The mirror stays a
16327            // BANDWIDTH lever on Q8_0-dominant GGUFs; it is no longer a correctness prerequisite.
16328            (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
16329            (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
16330            (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
16331            (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
16332            // b16 base + _rp (lane/rp-on-st): the 9B NVFP4 GGUF's blocker — real NVFP4 GGUFs keep
16333            // Q4_K attention next to NVFP4 MLP, and the tier's predicate is an ALL.
16334            (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
16335            (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
16336            (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
16337            (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
16338            // b16 base only (lane/rp-on-st): Q5_K has no rp twins at any width, so there is
16339            // nothing to mirror. Named by the diagnostic as `L0.wqkv_gate qtype=3` on the 9B.
16340            (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
16341            (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
16342            (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
16343            (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
16344            (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
16345            (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
16346            (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
16347            (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
16348            // b16 (lane/rp-on-st): no mirror needed — NVFP4's 36 B/k32 block is already the
16349            // aligned form its own kernel walks. Unlocks the exact-16 tier for every NVFP4 model
16350            // AND for the mixed FP8-ST artifact, whose 193 NVFP4 tensors were refusing it.
16351            (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
16352            (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
16353            (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
16354            (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
16355            // b16 tier (lane/rp-on-st): e4m3 needs NO split-plane mirror to reach it — its native
16356            // row-major layout is already 32B-aligned per k32 block, so the base kernel IS the
16357            // aligned form. Contrast Q8_0, whose b16 exists only as the `_rp` twin (hence q8rp).
16358            (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
16359            (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
16360            (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
16361            (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
16362            (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
16363            _ => return None,
16364        })
16365    }
16366
16367    /// BATCHED weight-tile-resident matvec from a PRE-QUANTIZED q8_1 activation (the m=2-8 verify/MTP
16368    /// win). One warp walks the weight row ONCE, dp4a vs all m activation columns -> weight HBM/L2
16369    /// traffic 1x for m tokens (vs grid.y=m re-reading it m times). `mcols` ∈ {2,4,8} is the
16370    /// compile-time batch; m must be <= mcols (the c >= m columns are masked in-kernel). y is
16371    /// [m, out_f] token-major. NVFP4 per-tensor macro-scale applied post
16372    /// (scale==1.0 for other dtypes -> no-op). BIT-IDENTICAL per (token,row) to qmatvec_*_mmvq.
16373    ///
16374    /// NVFP4 VARIANT DISPATCH: the batched NVFP4 kernel measured memory-LATENCY bound on the real
16375    /// 27B verify (ncu --set full, 12 steady launches: long_scoreboard 18-30 stalls/issue vs <=1.7
16376    /// for every other reason, DRAM only 41-51% active, lg_throttle 0.7, L1 hit 94% — ONE 6-LDG
16377    /// weight wavefront in flight per warp is the binding constraint, NOT bandwidth and NOT the
16378    /// column-unroll break). Two exactness-free fixes, chosen PER SHAPE from the DRAM-cold 8-copy
16379    /// msweep on all six 27B shapes (2026-07-03):
16380    ///   `pf` = next-g weight-prefetch double-buffer (48 regs, occupancy intact) — wins everywhere
16381    ///          it applies for b4 (-3..-14%), never loses;
16382    ///   `r2` = two rows/warp (67 regs -> 7 resident blocks/SM) — the bigger win (-8.5..-30%) but
16383    ///          wave-quantization-sensitive: with the grid halved to ceil(out_f/8) blocks, a
16384    ///          fractional straggler wave (waves in ~1.05-1.5) costs a full extra latency round on
16385    ///          a latency-bound kernel (27B ffn_down 640 blocks / 574 resident = 1.11 waves: +17%),
16386    ///          while <=1 wave (9B ffn_down 0.89: -30%) or >=2 waves (tail amortized; qkv 2.2:
16387    ///          -8.5%, ffn_gate 3.8: -12.5%) win. For b2, r2 wins on DEEP k-loops (in_f>=6144:
16388    ///          -8..-19%) where the 2-col body starves weight MLP hardest; pf measured negative.
16389    /// b4: r2 when waves(out_f) <= 1 (and grid fills >=half the SMs) or >= 2, else pf.
16390    /// b2: in_f>=6144 -> r2, else base.
16391    /// MEMRA_MMVQ_BV=base|pf|r2|pfr2 forces one variant everywhere (A/B + rollback seam).
16392    /// All variants BIT-IDENTICAL per (token,row): same dp4a order, scales, adg factor, reduce —
16393    /// only load issue time and the row->warp mapping change (kernel-check gates all of them).
16394    /// `rp` = the weight buffer is the A6 SPLIT-PLANE repacked layout (NVFP4 only): the same
16395    /// wave-aware auto rule applies, mapped onto the `_rp` twins (rp/rpr2/rpr2w8 mirror
16396    /// pf/r2/r2w8 — regs 44/67/64 land in the same residency classes).
16397    /// The variant the batched dispatch will pick for this (shape, m, mcols, layout) — exposed so
16398    /// gates can distinguish bit-identical variants (bit-bad==0 required) from the k-split family
16399    /// (deterministic but k-reduce-order-shifted: rel<1e-3 + run-to-run bit-identity required).
16400    /// Device SM count (cached) — grid-fill policy input.
16401    pub fn sm_count(&self) -> i32 {
16402        static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
16403        *SMS.get_or_init(|| {
16404            use cudarc::driver::sys::CUdevice_attribute_enum as A;
16405            self.gpu
16406                .ctx
16407                .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
16408                .unwrap_or(82)
16409        })
16410    }
16411
16412    pub fn batched_variant(
16413        &self,
16414        _m: usize,
16415        in_f: usize,
16416        out_f: usize,
16417        qtype: i32,
16418        row_bytes: usize,
16419        mcols: usize,
16420        rp: bool,
16421    ) -> &'static str {
16422        // Q8_0 never joined the auto variant machinery (on sm_120 its only batched shapes
16423        // were tiny aux tensors). On Q8_0-trunk models the layout is the whole game: the
16424        // split-plane mirror (rp) routes to the _rp twins (H100 coalescing fix, 2026-07-26);
16425        // GGUF layout stays "base". rp bytes MUST never reach the base kernel or vice versa.
16426        if qtype == QT_Q8_0 {
16427            return if rp { "rp" } else { "base" };
16428        }
16429        static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
16430        let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
16431            Ok("base") => "base",
16432            Ok("pf") => "pf",
16433            Ok("r2") => "r2",
16434            Ok("r2w8") => "r2w8",
16435            Ok("pfr2") => "pfr2",
16436            Ok("ca") => "ca",
16437            Ok("car2") => "car2",
16438            // rp* = SPLIT-PLANE REPACKED layout kernels (A6 prototype): W must already be the
16439            // repacked buffer (msweep MSWEEP_RP harness) — never valid on GGUF-layout weights.
16440            Ok("rp") => "rp",
16441            Ok("rpr2") => "rpr2",
16442            Ok("rpr2w8") => "rpr2w8",
16443            // rpca* = cp.async software-pipelined split-plane (2026-07-05): hides the _rp
16444            // long_scoreboard load stall. rp-layout only; b4/b2 (no b8 twin).
16445            Ok("rpca") => "rpca",
16446            Ok("rpcar2") => "rpcar2",
16447            // 2026-07-06 m-small latency arc: rpsc = rpr2 + per-warp smem scale prestage (kills
16448            // the scale-plane global dependency, zero reg growth); rpms/rpmsc = m-split x2
16449            // across warp pairs (2x blocks of rpr2, column halves per warp, BIT-identical to
16450            // _rp); rpks/rpksc = k-split x2 (fastest microbench cells but k-reduce-order-shifted:
16451            // run-spec self-consistency FAILED on the 27B daily driver — verify logits must be
16452            // bit-identical to the decode path — measurement corpus ONLY, never auto).
16453            Ok("rpsc") => "rpsc",
16454            Ok("rpms") => "rpms",
16455            Ok("rpmsc") => "rpmsc",
16456            Ok("rpks") => "rpks",
16457            Ok("rpksc") => "rpksc",
16458            _ => "auto",
16459        });
16460        // cp.async ring variants need 16B-aligned rows (in_f%256==0 -> (in_f/64)*36 % 16 == 0)
16461        // and whole 32-group warp iterations (nsb%32==0 <=> in_f%1024==0). All 27B/9B trunk
16462        // shapes qualify; anything else falls back to the register variants.
16463        let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
16464        // rpsc: smem scale plane fits (nsb64 <= 272) + int4-aligned staging (nsb64 % 4 == 0).
16465        // rpks/rpksc: half-plane staging alignment needs nsb64 % 8 == 0 (in_f % 512 == 0).
16466        // MEMRA_KS=0 removes the 2026-07-06 rpsc/rpks/rpksc entries from AUTO (rollback seam;
16467        // forced MEMRA_MMVQ_BV values still work).
16468        static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16469        let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
16470        let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
16471        let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
16472        static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
16473        let sms = *SMS.get_or_init(|| {
16474            use cudarc::driver::sys::CUdevice_attribute_enum as A;
16475            self.gpu
16476                .ctx
16477                .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
16478                .unwrap_or(82)
16479        });
16480        // k-quant r2 port (2026-07-04): q4_K/q5_K/q6_K have _r2/_r2w8 twins. ncu on the DRAM-cold
16481        // 9B msweep showed q4_K/q5_K b4 memory-latency bound like NVFP4 pre-fix (long_scoreboard
16482        // 19.6/16.4 per issue, DRAM 47.7/38.2%, L2 weight hit ~13%); q6_K lm_head is the exception
16483        // at DRAM 90-91% = wall-bound (yet r2 still wins -8%: deeper MLP raises achieved DRAM).
16484        // No _pf port (a k-quant group stages 10+ words vs NVFP4's 5 — register cost outweighs;
16485        // r2 covers the same MLP) and no rp (GGUF layout only). Q8_0 stays base: its only real
16486        // batched shapes are the tiny out_f=32 ssm_alpha/beta (8-block grids never fill one SM).
16487        // AUTO RULE = the measured winners table (differs from NVFP4's!):
16488        //   r2w8 NEVER in auto — the reg squeeze (72 -> 64 regs = stack spill) loses to unbounded
16489        //     r2 on every measured k-quant cell, incl. the wave-crossing lm_heads (q6_K 1316 vs
16490        //     r2 1258us) — kernels kept behind the force seam for the corpus;
16491        //   q4_K: r2 whenever the halved grid fills the SMs (blocks >= 4*SMs), INCLUDING the
16492        //     1.05-2.0 straggler window where NVFP4's r2 lost (qkv 1.78 waves: r2 -15% here; the
16493        //     k-quant base kernel leaves more latency on the table than a straggler wave costs);
16494        //   q5_K/q6_K: r2 only at waves >= 2 (the 248320-row lm_heads, 48+ waves: q6_K -8%, q5_K
16495        //     -2%); mid shapes measured base-or-flat (q5_K qkv 49.1 base vs 49.7 r2, attn_gate
16496        //     flat, attn_k base) — the 5/6-bit two-stream unpack makes r2's staging pricier.
16497        //   b2 same table with 8-row blocks: q4_K r2 when filled (-3..-22% all measured shapes),
16498        //     q5_K/q6_K r2 at waves >= 2 (27B lm_head -2.9%; 9B q6_K flat, harmless).
16499        let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
16500        // MEMRA_KQ_BV=base|r2|r2w8 forces the k-quant variant WITHOUT touching the NVFP4 dispatch
16501        // (MEMRA_MMVQ_BV is global — an interleaved k-quant-only e2e A/B needs this narrower seam).
16502        static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
16503        let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
16504            Ok("base") => "base",
16505            Ok("r2") => "r2",
16506            Ok("r2w8") => "r2w8",
16507            _ => "auto",
16508        });
16509        let variant: &'static str = if qtype == QT_Q4_0 {
16510            // Q4_0 r2 (gemma verify trunk, 2026-07-10): shared activation loads + the
16511            // row-independent ones-sum computed once per (col,group) for 2 rows. Same
16512            // fill rule as q4_K: r2 when the halved grid still fills the SMs.
16513            static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
16514            let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
16515                // ms/sm/la = force-only measurement seams (ALL FLAT/NEGATIVE 2026-07-13,
16516                // never auto): m-split flat (nvcc keeps 72 regs); smem-slab −11% (staging
16517                // + syncs cost more than the stalls, bank-pad made no difference);
16518                // register load-ahead flat (nvcc already reorders). The b-tier limiter
16519                // is still unidentified — see the jsonl row.
16520                Ok("base") => "base",
16521                Ok("r2") => "r2",
16522                Ok("ms") => "ms",
16523                Ok("sm") => "sm",
16524                Ok("la") => "la",
16525                _ => "auto",
16526            });
16527            let v = if q40 != "auto" {
16528                q40
16529            } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
16530                "r2"
16531            } else {
16532                "base"
16533            };
16534            // split-plane mirror twins (2026-07-10): same fill rule, _rp names.
16535            // (m-split r2 pair twin PROBED FLAT 2026-07-13 — nvcc kept 72 regs either way
16536            // and the limiter is the per-column activation load chain (long_scoreboard
16537            // 42.5%), not occupancy; arm killed per doctrine, jsonl row is the record.)
16538            if rp {
16539                match v {
16540                    "ms" => "r2ms_rp",
16541                    "sm" => "r2sm_rp",
16542                    "la" => "r2la_rp",
16543                    "r2" => "r2_rp",
16544                    _ => "rp",
16545                }
16546            } else if matches!(v, "ms" | "sm" | "la") {
16547                "r2"
16548            } else {
16549                v
16550            }
16551        } else if qtype != QT_NVFP4 && !kq_r2 {
16552            "base"
16553        } else if kq_r2 && rp {
16554            // K-quant split-plane mirror (2026-08-01): only the plain _rp batched twins are
16555            // compiled for q4_K/q6_K — rp is a LAYOUT, it must survive every heuristic
16556            // (split-plane bytes through a GGUF-layout kernel = NaN). q5_K never mirrors.
16557            "rp"
16558        } else if kq_r2 {
16559            // k-quant r2w8 only exists at b4 (b2_r2 already 8-resident; b8 has no w8 twin) ->
16560            // mcols != 4 forced r2w8 falls to unbounded r2.
16561            if kq_bv != "auto" {
16562                if kq_bv == "r2w8" && mcols != 4 {
16563                    "r2"
16564                } else {
16565                    kq_bv
16566                }
16567            } else if bv != "auto" {
16568                match bv {
16569                    "r2" | "pfr2" | "rpr2" | "car2" => "r2",
16570                    "r2w8" | "rpr2w8" => {
16571                        if mcols != 4 {
16572                            "r2"
16573                        } else {
16574                            "r2w8"
16575                        }
16576                    }
16577                    _ => "base", // base/pf/ca/rp forced -> base (no such k-quant kernels)
16578                }
16579            } else {
16580                let blocks = (out_f + 7) / 8;
16581                let waves = blocks as f64 / (7 * sms as usize) as f64;
16582                let filled = blocks >= 4 * sms as usize;
16583                let use_r2 = if qtype == QT_Q4_K {
16584                    filled
16585                } else {
16586                    waves >= 2.0
16587                };
16588                if use_r2 { "r2" } else { "base" }
16589            }
16590        } else if bv != "auto" {
16591            // r2w8 only exists for b4/b8 (the b2_r2 kernel is already 8-blocks-resident at 60 regs).
16592            // ca/car2 need the alignment gate AND have no b8 twins; pfr2 has no b8 twin either —
16593            // unsupported (shape, mcols) combos fall back to pf/r2.
16594            // On rp buffers, forced legacy names map to their rp twins (layout law).
16595            let v = if bv == "r2w8" && mcols == 2 {
16596                "r2"
16597            } else if bv == "ca" && (!ca_ok || mcols == 8) {
16598                "pf"
16599            } else if bv == "car2" && (!ca_ok || mcols == 8) {
16600                "r2"
16601            } else if bv == "pfr2" && mcols == 8 {
16602                "r2"
16603            } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
16604                "rpr2"
16605            }
16606            // rpca* has no b8 twin (falls to rpr2w8/rpr2); needs the ca alignment gate.
16607            else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
16608                if mcols == 8 { "rpr2w8" } else { "rpr2" }
16609            } else if bv == "rpcar2" && mcols == 2 {
16610                "rpca"
16611            }
16612            // rpsc/rpmsc/rpks* gate on smem-fit + alignment; fall to rpr2 outside it
16613            // (rpms has no smem and no alignment need — always valid on rp buffers).
16614            else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
16615                "rpr2"
16616            } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
16617                "rpr2"
16618            } else {
16619                bv
16620            };
16621            if rp {
16622                match v {
16623                    "base" | "pf" | "ca" | "rp" => "rp",
16624                    "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
16625                    "r2w8" | "rpr2w8" => {
16626                        if mcols == 2 {
16627                            "rpr2"
16628                        } else {
16629                            "rpr2w8"
16630                        }
16631                    }
16632                    other => other, // rpca/rpcar2/rpsc/rpks/rpksc pass through (already rp-layout)
16633                }
16634            } else {
16635                v
16636            }
16637        } else if mcols == 8 {
16638            // b8 AUTO (2026-07-06 m-small latency arc, rtx6000 DRAM-cold rp msweep m=5/6/8 all five
16639            // 27B shapes): rpsc — the rpr2w8 schedule with the warp's scale rows prestaged to
16640            // smem, leaving ONE global dependency (the quant stream) in the k-loop at zero reg
16641            // growth. BIT-identical to rpr2w8 and wins or ties EVERY b8 cell: ffn_gate m5
16642            // 50.7->46.9 m8 64.1->57.1 (-11%), qkv m8 34.6->33.0, ssm_out m8 29.7->28.8,
16643            // attn_gate m8 26.9->26.1, ffn_down m5 58.2->56.9. The faster split-grid twins are
16644            // OUT: rpksc (k-split, ffn_down m5 -21%) broke run-spec self-consistency (k-reduce
16645            // order shifts verify argmax at tie margins — verify must stay bit-identical to the
16646            // m=1 decode chain); rpmsc (m-split, bit-identical) measured NEGATIVE everywhere
16647            // (twin warp's duplicated weight stream: ffn_down m5 85.7 vs 56.9).
16648            if rp {
16649                if sc_ok { "rpsc" } else { "rpr2w8" }
16650            } else {
16651                "r2w8"
16652            }
16653        } else if mcols >= 4 {
16654            // r2 runs 7 resident blocks/SM (67 regs); its __launch_bounds__(128,8) twin `r2w8`
16655            // (64 regs) runs 8. grid = ceil(out_f/8) for both. rp twins land in the same
16656            // residency classes (rp 44 regs ~ pf-class occupancy, rpr2 67, rpr2w8 64).
16657            let blocks = (out_f + 7) / 8;
16658            let r7 = 7 * sms as usize;
16659            let r8 = 8 * sms as usize;
16660            let waves = blocks as f64 / r7 as f64;
16661            let filled = blocks >= 4 * sms as usize;
16662            // 2026-07-06 m-small latency arc: b4 keeps the wave rule (rpms/rpmsc measured
16663            // flat-to-negative at m=3/4 on every shape — the m-split twin duplicates the weight
16664            // stream; rpsc b4 also negative on r2-class picks, ffn_down m4 51.1 vs 46.5).
16665            if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
16666                // the extra residency drops the INTEGER wave count -> the straggler wave a
16667                // latency-bound kernel pays in full disappears (ffn_down 1.11 -> 0.98 waves:
16668                // 112.5 -> 81.6us, beats pf 90.1; qkv 2.23 -> 1.95: 58.1 -> 51.1).
16669                if rp { "rpr2w8" } else { "r2w8" }
16670            } else if waves >= 2.0 || (waves <= 1.0 && filled) {
16671                // tail amortized (>=2 waves) or single wave: unbounded r2 (no reg-squeeze tax —
16672                // gate/up 81.1 vs 83.9 bounded, attn_q 61.0 vs 63.4).
16673                if rp { "rpr2" } else { "r2" }
16674            } else {
16675                // fractional straggler-wave window with no crossing, or grid too small to fill
16676                // the SMs (tiny out_f<=1024 shapes want max row-parallelism): prefetch variant
16677                // (rp = the r1 split-plane twin — measured the attn_gate winner, 35.4 vs pf 36.4).
16678                if rp { "rp" } else { "pf" }
16679            }
16680        } else if in_f >= 6144 {
16681            // b2 deep-k (2026-07-06): every new twin measured flat-to-negative here (rpms 44.1
16682            // vs rpr2 40.8 ffn_down; rpsc 43.6; the winning rpks is banned on k-order) — rpr2
16683            // stays.
16684            if rp { "rpr2" } else { "r2" }
16685        } else if rp {
16686            // b2 shallow-k: qkv (out_f=10240, 0.97 waves at 7-resident) is the one measured cell
16687            // where the r2-schedule scale-prestage twin beats the r1 rp pick (24.7 vs 28.9us
16688            // -15%); the wider (ffn_gate 1.65 waves) and smaller (attn_gate 0.58) shapes LOSE
16689            // (41.8 vs 38.2 / 16.6 vs 14.6) — gate on the single-wave window.
16690            let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
16691            if sc_ok && waves >= 0.9 && waves <= 1.1 {
16692                "rpsc"
16693            } else {
16694                "rp"
16695            }
16696        } else {
16697            "base"
16698        };
16699        variant
16700    }
16701
16702    pub fn qmatvec_mmvq_batched(
16703        &self,
16704        bytes: &CudaSlice<u8>,
16705        aq: &CudaSlice<i8>,
16706        ad: &CudaSlice<f32>,
16707        m: usize,
16708        in_f: usize,
16709        out_f: usize,
16710        qtype: i32,
16711        row_bytes: usize,
16712        mcols: usize,
16713        scale: f32,
16714        rp: bool,
16715    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16716        const ROWS_PER_BLOCK: u32 = 4;
16717        // TUNE SEAM (H100 lane): MEMRA_BVAR forces the batched-variant pick for the whole
16718        // process — the auto heuristics were tuned on sm_120 (82 SMs / 858 GB/s) and the
16719        // sm_90a re-tune sweeps this seam empirically. Layout variants stay safe: an rp
16720        // weight keeps its rp-layout kernel family regardless of the override.
16721        let forced: Option<&'static str> = {
16722            static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
16723            V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
16724                .as_deref()
16725                .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
16726        };
16727        let variant = match forced {
16728            Some(v) if !rp || v.contains("rp") => v,
16729            _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
16730        };
16731        let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
16732            format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
16733        })?;
16734        // b16 tier (t=9..16 verify): only base/_rp b16 kernels are compiled — the b2..b8
16735        // per-shape perf variants (r2/pf/...) do not apply at this width. rp is a LAYOUT,
16736        // not a perf variant: it must survive (base kernel on split-plane bytes = NaN).
16737        let variant = if mcols == 16 {
16738            if rp { "rp" } else { "base" }
16739        } else {
16740            variant
16741        };
16742        // EXACT-WIDTH b5/b6/b7 twins (lane/vt-fixes fix 1, 2026-08-03): the b8 kernels
16743        // allocate acc[WROWS][8] at ANY m, so T=5..7 verify paid the full 8-wide register
16744        // tax — the measured T=4->5 cliff. The same template at MCOLS=m runs the identical
16745        // per-(token,row) chain (columns c >= m never execute in either form) ->
16746        // BIT-IDENTICAL to the b8 launch. NVFP4 split-plane only (the sm_120 default trunk);
16747        // covers both b8 auto schedules (rpsc, rpr2w8). MEMRA_B567=0 rollback.
16748        static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16749        let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
16750        if b567
16751            && qtype == QT_NVFP4
16752            && rp
16753            && mcols == 8
16754            && (5..=7).contains(&m)
16755            && matches!(variant, "rpsc" | "rpr2w8")
16756        {
16757            let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
16758            let rows_per_block = ROWS_PER_BLOCK * 2; // r2-class schedules: 2 rows/warp
16759            let mut y = self.alloc_uninit::<f32>(m * out_f)?;
16760            let cfg = LaunchConfig {
16761                grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
16762                block_dim: (32, ROWS_PER_BLOCK, 1),
16763                shared_mem_bytes: 0,
16764            };
16765            let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16766            let __s_b = self.gpu.stream();
16767            let mut b = __s_b.launch_builder(&f);
16768            b.arg(bytes)
16769                .arg(aq)
16770                .arg(ad)
16771                .arg(&mut y)
16772                .arg(&inf)
16773                .arg(&outf)
16774                .arg(&mi)
16775                .arg(&rb);
16776            unsafe {
16777                b.launch(cfg)?;
16778            }
16779            if scale != 1.0 {
16780                self.scale_inplace(&mut y, scale, m * out_f)?;
16781            }
16782            return Ok(y);
16783        }
16784        let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
16785            "base" => (base_name.into(), ROWS_PER_BLOCK),
16786            "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
16787            "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
16788            "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
16789            "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), // 1 row/warp cp.async
16790            // split families: 2 warp-pairs x 2 rows = 4 rows/block (the k-range or column set
16791            // splits across the pair's two warps; grid.x doubles vs rpr2 at the same regs).
16792            "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
16793            "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
16794            "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
16795            "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
16796            "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
16797            "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
16798            "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
16799            v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), // r2-class: 2 rows/warp
16800        };
16801        debug_assert!(
16802            !rp || name.contains("_rp"),
16803            "rp weight dispatched to a GGUF-layout kernel"
16804        );
16805        let f = self.func(&name);
16806        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
16807        // r2sm_rp: [MCOLS][32 blk][8 int] activation slab + [MCOLS][32] f32 scales.
16808        let smem = if name.contains("_r2sm_rp") {
16809            (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
16810        } else {
16811            0
16812        };
16813        let cfg = LaunchConfig {
16814            grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
16815            block_dim: (32, ROWS_PER_BLOCK, 1),
16816            shared_mem_bytes: smem,
16817        };
16818        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16819        let __s_b = self.gpu.stream();
16820        let mut b = __s_b.launch_builder(&f);
16821        b.arg(bytes)
16822            .arg(aq)
16823            .arg(ad)
16824            .arg(&mut y)
16825            .arg(&inf)
16826            .arg(&outf)
16827            .arg(&mi)
16828            .arg(&rb);
16829        unsafe {
16830            b.launch(cfg)?;
16831        }
16832        if scale != 1.0 {
16833            self.scale_inplace(&mut y, scale, m * out_f)?;
16834        }
16835        Ok(y)
16836    }
16837
16838    /// BATCHED weight-tile-resident matvec from raw weight bytes (quantizes the f32 activation `x` to
16839    /// q8_1 internally; macro-scale NOT applied — caller compares bare, like qmatvec_*_fast). For the
16840    /// kernel_check bit-equivalence gate. `mcols` ∈ {2,4,8}. Works for Q8_0/Q4_K/Q5_K/Q6_K/NVFP4.
16841    pub fn qmatvec_batched_raw(
16842        &self,
16843        bytes: &CudaSlice<u8>,
16844        x: &CudaSlice<f32>,
16845        m: usize,
16846        in_f: usize,
16847        out_f: usize,
16848        qtype: i32,
16849        row_bytes: usize,
16850        mcols: usize,
16851        rp: bool,
16852    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16853        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
16854        self.qmatvec_mmvq_batched(
16855            bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
16856        )
16857    }
16858
16859    /// Back-compat NVFP4-only batched raw launcher (used by older gates). Delegates to the generic one.
16860    pub fn qmatvec_nvfp4_batched_raw(
16861        &self,
16862        bytes: &CudaSlice<u8>,
16863        x: &CudaSlice<f32>,
16864        m: usize,
16865        in_f: usize,
16866        out_f: usize,
16867        row_bytes: usize,
16868        mcols: usize,
16869        rp: bool,
16870    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16871        self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
16872    }
16873
16874    /// Stage-C FP4 gate (MEMRA_FP4): if `w` is an NVFP4 weight with in_f%64==0, run the native mxf4
16875    /// block-scale GEMM and apply the per-tensor macro-scale, returning Some(y). Else None (caller
16876    /// falls through to the int8 GEMM / dp4a). Strict opt-in over the proven int8 path; m>=16 only.
16877    fn try_fp4_gemm(
16878        &self,
16879        w: &crate::model::GpuTensor,
16880        x: &CudaSlice<f32>,
16881        m: usize,
16882        in_f: usize,
16883        out_f: usize,
16884    ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16885        use crate::model::GpuTensor;
16886        if cfg!(memra_portable_cuda) {
16887            return Ok(None);
16888        }
16889        // MEMRA_FP4 reaches qmatvec_gemm_nvfp4_fp4, which cu/qmatvec_gemm.cu:1234 omits on a
16890        // portable build (the mxf4 block-scale MMA is sm_120a-only). Refuse at the door.
16891        if std::env::var("MEMRA_FP4").is_ok() {
16892            refuse_portable_force("MEMRA_FP4", "the sm_120a mxf4 block-scale MMA");
16893        }
16894        if std::env::var("MEMRA_FP4").is_err() {
16895            return Ok(None);
16896        }
16897        // CUTLASS prefill branch (m>=128 + MEMRA_FP4_CUTLASS + a repacked CutlassWeight present): route
16898        // to the CUTLASS sm120 NVFP4 GEMM, folding the per-tensor macro-scale into the epilogue alpha
16899        // (1/scale) — no post-matmul scale_inplace. Decode (m<128) and the m∈[16,128) middle band keep
16900        // the hand-roll below: CUTLASS's 128-row M-tile wastes work under 128.
16901        // The hand-roll applies the per-tensor macro-scale as a POST-matmul MULTIPLY (scale_inplace(y,
16902        // scale)); CUTLASS's epilogue does D = alpha * (A@B^T), so alpha == scale reproduces it exactly
16903        // (NOT 1/scale — the plan sketch had this inverted; the kernel_check arm gates it). scale==1.0
16904        // for the common no-macro-scale case.
16905        #[cfg(memra_cutlass)]
16906        if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
16907            if let GpuTensor::Quant {
16908                bytes,
16909                qtype,
16910                scale,
16911                row_bytes,
16912                cutlass,
16913                ..
16914            } = w
16915            {
16916                if *qtype == QT_NVFP4 && in_f % 64 == 0 {
16917                    if let Some(cw) = cutlass {
16918                        // Resident fast path: load-time-repacked B + swizzled SFB (no per-call repack).
16919                        let y = self.cutlass_fp4_gemm(
16920                            &cw.b_packed,
16921                            &cw.sfb_swizzled,
16922                            x,
16923                            *scale,
16924                            m,
16925                            out_f,
16926                            in_f,
16927                        )?;
16928                        return Ok(Some(y));
16929                    } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
16930                        // On-the-fly repack (MEMRA_FP4_CUTLASS_OTF): de-interleave + swizzle the B operand
16931                        // from raw bytes per prefill call. No resident doubling of the NVFP4 weight VRAM
16932                        // (the load-time repack ~doubles it) — needed for models that don't fit the
16933                        // resident path (e.g. the 27B on 24GB). Slower (per-call repack) but argmax-exact.
16934                        let (b_packed, sfb_sw) =
16935                            self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
16936                        let y =
16937                            self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
16938                        return Ok(Some(y));
16939                    }
16940                }
16941            }
16942        }
16943        if let GpuTensor::Quant {
16944            bytes,
16945            qtype,
16946            row_bytes,
16947            scale,
16948            rp,
16949            ..
16950        } = w
16951        {
16952            // A6: the hand-rolled W4A4 mxf4 GEMM reads 36B GGUF blocks — no rp port (MEMRA_FP4 is
16953            // an opt-in accuracy tradeoff); repacked tensors fall through to the int8 GEMM.
16954            if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
16955                let y =
16956                    self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
16957                return Ok(Some(y));
16958            }
16959        }
16960        Ok(None)
16961    }
16962
16963    /// rms_norm + fused fp16 twin (task #14): f32 output verbatim `rms_norm` + the fp16
16964    /// copy the f16-mirror GEMM group would otherwise produce with a standalone convert
16965    /// launch. BIT-IDENTICAL end-to-end (same reduction, same __float2half values).
16966    pub fn rms_norm_f16out(
16967        &self,
16968        x: &CudaSlice<f32>,
16969        w: &CudaSlice<f32>,
16970        dst: &mut CudaSlice<f32>,
16971        dst16: &mut CudaSlice<u8>,
16972        ncols: usize,
16973        nrows: usize,
16974        eps: f32,
16975    ) -> Result<(), Box<dyn std::error::Error>> {
16976        let f = self.func("rms_norm_f16out_f32");
16977        let cfg = LaunchConfig {
16978            grid_dim: (nrows as u32, 1, 1),
16979            block_dim: (rms_block(), 1, 1),
16980            shared_mem_bytes: 0,
16981        };
16982        let (nc, e) = (ncols as i32, eps);
16983        let __s_b = self.gpu.stream();
16984        let mut b = __s_b.launch_builder(&f);
16985        b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
16986        unsafe {
16987            b.launch(cfg)?;
16988        }
16989        Ok(())
16990    }
16991
16992    /// add+norm(+f16out) fusion for the prefill trunk (round 28; add_rms_norm precedent —
16993    /// bit-identical to add_f32 -> rms_norm_f16out). block_dim matches rms_norm_f16out's.
16994    #[allow(clippy::too_many_arguments)]
16995    pub fn add_rms_norm_f16out(
16996        &self,
16997        a: &CudaSlice<f32>,
16998        b: &CudaSlice<f32>,
16999        w: &CudaSlice<f32>,
17000        res: &mut CudaSlice<f32>,
17001        dst: &mut CudaSlice<f32>,
17002        dst16: &mut CudaSlice<u8>,
17003        ncols: usize,
17004        nrows: usize,
17005        eps: f32,
17006    ) -> Result<(), Box<dyn std::error::Error>> {
17007        let f = self.func("add_rms_norm_f16out_f32");
17008        let cfg = LaunchConfig {
17009            grid_dim: (nrows as u32, 1, 1),
17010            block_dim: (rms_block(), 1, 1),
17011            shared_mem_bytes: 0,
17012        };
17013        let (nc, e) = (ncols as i32, eps);
17014        let __s_lb = self.gpu.stream();
17015        let mut lb = __s_lb.launch_builder(&f);
17016        lb.arg(a)
17017            .arg(b)
17018            .arg(w)
17019            .arg(res)
17020            .arg(dst)
17021            .arg(dst16)
17022            .arg(&nc)
17023            .arg(&e);
17024        unsafe {
17025            lb.launch(cfg)?;
17026        }
17027        Ok(())
17028    }
17029
17030    /// matmul_group with a PRE-EMITTED fp16 activation (task #14: the producer norm fused
17031    /// the convert). Mirror-less members fall back to `matmul` on the f32 activation.
17032    pub fn matmul_group_xh(
17033        &self,
17034        ws: &[&crate::model::GpuTensor],
17035        x: &CudaSlice<f32>,
17036        xh: &CudaSlice<u8>,
17037        m: usize,
17038    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17039        let mut out = Vec::with_capacity(ws.len());
17040        let in_f = ws[0].in_features();
17041        for w in ws {
17042            if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
17043                if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
17044                    out.push(y);
17045                    continue;
17046                }
17047            }
17048            out.push(self.matmul(w, x, m)?);
17049        }
17050        Ok(out)
17051    }
17052
17053    /// task #14 pad-proofing: zero beta/g_log at rows >= len_d[0] (pads become identity
17054    /// GDN steps). Layouts [T, H].
17055    pub fn gdn_pad_mask(
17056        &self,
17057        beta: &mut CudaSlice<f32>,
17058        g_log: &mut CudaSlice<f32>,
17059        len_d: &CudaSlice<i32>,
17060        h: usize,
17061        t: usize,
17062    ) -> Result<(), Box<dyn std::error::Error>> {
17063        let f = self.func("gdn_pad_mask_f32");
17064        let cfg = LaunchConfig::for_num_elems((t * h) as u32);
17065        let (hi, ti) = (h as i32, t as i32);
17066        let __s_b = self.gpu.stream();
17067        let mut b = __s_b.launch_builder(&f);
17068        b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
17069        unsafe {
17070            b.launch(cfg)?;
17071        }
17072        Ok(())
17073    }
17074
17075    /// task #14 pad-proofing: dst[ncols] = src row (len_d[0]-1) — device-indexed last-row
17076    /// gather for the padded prime graph's h_seed/hlast.
17077    pub fn row_gather_dev(
17078        &self,
17079        src: &CudaSlice<f32>,
17080        dst: &mut CudaSlice<f32>,
17081        len_d: &CudaSlice<i32>,
17082        ncols: usize,
17083    ) -> Result<(), Box<dyn std::error::Error>> {
17084        let f = self.func("row_gather_dev_f32");
17085        let cfg = LaunchConfig::for_num_elems(ncols as u32);
17086        let nc = ncols as i32;
17087        let __s_b = self.gpu.stream();
17088        let mut b = __s_b.launch_builder(&f);
17089        b.arg(src).arg(dst).arg(len_d).arg(&nc);
17090        unsafe {
17091            b.launch(cfg)?;
17092        }
17093        Ok(())
17094    }
17095
17096    /// Grouped matmul: several weights consuming ONE activation (hybrid layers: the GDN
17097    /// 4-tuple wqkv/gate/beta/alpha, attention q/k/v, ffn gate/up). Semantics identical to
17098    /// calling `matmul` per weight; the f16-mirror arm converts the activation ONCE for the
17099    /// whole group instead of once per GEMM (the standalone converts were ~250 launches/prime
17100    /// of small-kernel gap fuel — nsys 2026-07-26). Any member without a mirror (or with a
17101    /// different in_f) falls back to its own `matmul` — behavior unchanged.
17102    pub fn matmul_group(
17103        &self,
17104        ws: &[&crate::model::GpuTensor],
17105        x: &CudaSlice<f32>,
17106        m: usize,
17107    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
17108        use crate::model::GpuTensor;
17109        let mut out = Vec::with_capacity(ws.len());
17110        let any_mirror = ws
17111            .iter()
17112            .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
17113        if m >= 16 && any_mirror && !self.verify_exact_on() {
17114            let in_f = ws[0].in_features();
17115            let xh = self.f16_act(x, m * in_f, in_f)?;
17116            for w in ws {
17117                if w.in_features() == in_f {
17118                    if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
17119                        out.push(y);
17120                        continue;
17121                    }
17122                }
17123                out.push(self.matmul(w, x, m)?);
17124            }
17125            return Ok(out);
17126        }
17127        for w in ws {
17128            out.push(self.matmul(w, x, m)?);
17129        }
17130        Ok(out)
17131    }
17132
17133    /// Cross-request grouped matmul (task #13): run ONE projection group over the
17134    /// CONCATENATION of several sequences' activations (m = sum of per-seq rows — the
17135    /// GEMM-batch win vLLM gets from continuous batching), then split each output back
17136    /// into per-seq buffers. Zero view plumbing: gather/scatter are stream-ordered D2D
17137    /// copies (~us at prime sizes). NUMERIC CONFIG NOTE: a GEMM at m=sum tiles K
17138    /// differently than per-seq GEMMs — argmax-gated like every prefill GEMM change.
17139    pub fn matmul_group_multi(
17140        &self,
17141        ws: &[&crate::model::GpuTensor],
17142        xs: &[&CudaSlice<f32>],
17143        ms: &[usize],
17144    ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
17145        assert_eq!(xs.len(), ms.len());
17146        let in_f = ws[0].in_features();
17147        let total: usize = ms.iter().sum();
17148        let mut xcat = self.uninit(total * in_f)?;
17149        let mut off = 0usize;
17150        for (x, &m) in xs.iter().zip(ms) {
17151            self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
17152            off += m;
17153        }
17154        let ys = self.matmul_group(ws, &xcat, total)?;
17155        let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
17156        for (w, y) in ws.iter().zip(ys) {
17157            let out_f = w.out_features();
17158            let mut off = 0usize;
17159            for (s, &m) in ms.iter().enumerate() {
17160                let mut ys_s = self.uninit(m * out_f)?;
17161                let src = y.slice(off * out_f..(off + m) * out_f);
17162                self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
17163                out[s].push(ys_s);
17164                off += m;
17165            }
17166        }
17167        Ok(out)
17168    }
17169
17170    /// True if `w`'s qtype has a batched tensor-core GEMM kernel (the prefill T>1 root fix).
17171    /// Only the 4 daily-hot dtypes: Q8_0, Q4_K, Q6_K, NVFP4. NVFP4 needs in_f % 64 == 0.
17172    /// DEFAULT-ON (2026-06-28): measured pp512 9B-NVFP4 = 1413 tok/s WITH this GEMM vs 298 with the
17173    /// dp4a fallback (4.7x) AND MORE accurate (prefill logit maxdiff 0.159 vs dp4a 0.55, both argmax
17174    /// MATCH). The int8 tensor-core GEMM is unconditional (its historical MEMRA_GEMM opt-in gate
17175    /// shipped with Phase 0 — mma + smem swizzle + cp.async — and was removed). Prefill-only
17176    /// (m>=GEMM_M_THRESHOLD); m=1 decode keeps dp4a/MMVQ (this returns true but matmul only calls it
17177    /// at m>=threshold). Portable CUDA targets always use the correctness fallback; on sm_120a,
17178    /// MEMRA_NO_GEMM forces that same dp4a fallback (the bit-reference).
17179    pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
17180        use crate::model::GpuTensor;
17181        if !legacy_quant_gemm_allowed(
17182            cfg!(memra_portable_cuda),
17183            cfg!(memra_hopper_mma),
17184            std::env::var_os("MEMRA_NO_GEMM").is_some(),
17185        ) {
17186            return false;
17187        }
17188        match w {
17189            GpuTensor::Quant { qtype, .. } => {
17190                matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
17191                    || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
17192            }
17193            GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
17194        }
17195    }
17196
17197    /// Batched tensor-core int8 GEMM with a PRE-QUANTIZED q8_1 activation (aq,ad). The prefill
17198    /// (T>1) root fix: decode each weight 32-block to int8 in shared memory ONCE per (row-tile,
17199    /// K-step) and reuse it across all BN tokens via mma.sync.m16n8k32.s8 — amortizing the weight
17200    /// read/decode N-fold (vs the dp4a matvec's per-token re-read). s32 accumulate is exact vs
17201    /// dp4a; only the final f32 block-scale rounding differs. Caller MUST have checked
17202    /// `gemm_supports(w)`. y[m,out] token-major. NVFP4 per-tensor macro-scale applied post.
17203    pub fn qmatvec_gemm(
17204        &self,
17205        w: &crate::model::GpuTensor,
17206        aq: &CudaSlice<i8>,
17207        ad: &CudaSlice<f32>,
17208        m: usize,
17209    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17210        use crate::model::GpuTensor;
17211        let in_f = w.in_features();
17212        let out_f = w.out_features();
17213        let (bytes, qtype, row_bytes, scale, rp) = match w {
17214            GpuTensor::Quant {
17215                bytes,
17216                qtype,
17217                row_bytes,
17218                scale,
17219                rp,
17220                ..
17221            } => (bytes, *qtype, *row_bytes, *scale, *rp),
17222            _ => unreachable!("gemm_supports guaranteed Quant"),
17223        };
17224        // wgmma arm (sm_90a, task 8): the m64n64k32 warpgroup kernel reads the rp4 split-plane
17225        // mirror AS-IS (qplane rows = its A operand, the half dplane its scales) and the same
17226        // (aq, ad) activation planes. Same numeric class as the mma kernel below (exact s32 per
17227        // 32-block, one f32 scale fold per block, ascending K) — argmax/tolerance gated like
17228        // every prefill GEMM, not bit-gated. MEMRA_WGMMA=0 restores the portable kernel.
17229        if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
17230            if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
17231                let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
17232                if scale != 1.0 {
17233                    self.scale_inplace(&mut y, scale, m * out_f)?;
17234                }
17235                return Ok(y);
17236            }
17237        }
17238        let name = match qtype {
17239            QT_Q8_0 => "qmatvec_gemm_q8_0",
17240            QT_Q4_K => "qmatvec_gemm_q4_K",
17241            QT_Q4_0 => {
17242                if rp {
17243                    "qmatvec_gemm_q4_0_rp"
17244                } else {
17245                    "qmatvec_gemm_q4_0"
17246                }
17247            }
17248            QT_Q5_K => "qmatvec_gemm_q5_K",
17249            QT_Q6_K => "qmatvec_gemm_q6_K",
17250            QT_NVFP4 => {
17251                if rp {
17252                    "qmatvec_gemm_nvfp4_rp"
17253                } else {
17254                    "qmatvec_gemm_nvfp4"
17255                }
17256            }
17257            _ => unreachable!(),
17258        };
17259        let f = self.func(name);
17260        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite GEMM output: skip memset
17261        // CTA tile MUST match the .cu per-kernel tile. MMQ-PORT: kernel1 (Q8_0/Q4_K/Q5_K) runs llama's
17262        // 128x128 SQUARE tile (K1_BM=128 x K1_BN=128, 8 warps); kernel2 (Q6_K/NVFP4) keeps 64x256, 4 warps
17263        // (the macro BM/BN in the .cu). Grid dims are selected by qtype so each launches its own tile.
17264        let is_k1 = matches!(qtype, QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q4_0);
17265        // TUNE SEAM: MEMRA_GEMM_K1_LAUNCH overrides kernel1's launch tile to match a -D-swept fatbin.
17266        let k1_tile = if is_k1 {
17267            k1_launch_override().unwrap_or((128, 128, 8))
17268        } else {
17269            (128, 128, 8)
17270        };
17271        let (bm, bn): (u32, u32) = if is_k1 {
17272            (k1_tile.0, k1_tile.1)
17273        } else {
17274            (64, 256)
17275        };
17276        let warps: u32 = if is_k1 {
17277            k1_tile.2
17278        } else {
17279            match qtype {
17280                QT_NVFP4 => 8,
17281                _ => 4,
17282            }
17283        };
17284        let cfg = LaunchConfig {
17285            grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
17286            block_dim: (32, warps, 1),
17287            shared_mem_bytes: 0,
17288        };
17289        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
17290        let __s_b = self.gpu.stream();
17291        let mut b = __s_b.launch_builder(&f);
17292        b.arg(bytes)
17293            .arg(aq)
17294            .arg(ad)
17295            .arg(&mut y)
17296            .arg(&inf)
17297            .arg(&outf)
17298            .arg(&mi)
17299            .arg(&rb);
17300        unsafe {
17301            b.launch(cfg)?;
17302        }
17303        if scale != 1.0 {
17304            self.scale_inplace(&mut y, scale, m * out_f)?;
17305        }
17306        Ok(y)
17307    }
17308
17309    /// Test entry: run the GEMM directly from raw weight bytes + qtype (no GpuTensor). Quantizes
17310    /// the f32 activation `x` to q8_1 internally then launches the tensor-core GEMM. NVFP4 per-tensor
17311    /// macro-scale is NOT applied here (caller passes it separately, like the dp4a path). Used by
17312    /// kernel_check for the bit-equivalence gate vs qmatvec_*_dp4a.
17313    pub fn qmatvec_gemm_raw(
17314        &self,
17315        bytes: &CudaSlice<u8>,
17316        x: &CudaSlice<f32>,
17317        m: usize,
17318        in_f: usize,
17319        out_f: usize,
17320        qtype: i32,
17321        row_bytes: usize,
17322    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17323        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
17324        let name = match qtype {
17325            QT_Q8_0 => "qmatvec_gemm_q8_0",
17326            QT_Q4_K => "qmatvec_gemm_q4_K",
17327            QT_Q4_0 => "qmatvec_gemm_q4_0",
17328            QT_Q5_K => "qmatvec_gemm_q5_K",
17329            QT_Q6_K => "qmatvec_gemm_q6_K",
17330            QT_NVFP4 => "qmatvec_gemm_nvfp4",
17331            QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
17332            _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
17333        };
17334        let f = self.func(name);
17335        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite GEMM output: skip memset
17336        // MMQ-PORT: kernel1 (Q8_0/Q4_K/Q5_K) = llama 128x128 tile, 8 warps; kernel2 (Q6_K/NVFP4) = 64x256,
17337        // 4/8 warps. Grid tile per qtype (must match the .cu K1_BM/K1_BN vs BM/BN). KEEP IN SYNC w/ qmatvec_gemm.
17338        let is_k1 = matches!(qtype, QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q4_0);
17339        // TUNE SEAM: MEMRA_GEMM_K1_LAUNCH overrides kernel1's launch tile to match a -D-swept fatbin.
17340        let k1_tile = if is_k1 {
17341            k1_launch_override().unwrap_or((128, 128, 8))
17342        } else {
17343            (128, 128, 8)
17344        };
17345        let (bm, bn): (u32, u32) = if is_k1 {
17346            (k1_tile.0, k1_tile.1)
17347        } else {
17348            (64, 256)
17349        };
17350        let warps: u32 = if is_k1 {
17351            k1_tile.2
17352        } else {
17353            match qtype {
17354                QT_NVFP4 | QT_NVFP4_RP => 8,
17355                _ => 4,
17356            }
17357        };
17358        let cfg = LaunchConfig {
17359            grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
17360            block_dim: (32, warps, 1),
17361            shared_mem_bytes: 0,
17362        };
17363        let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
17364        let __s_b = self.gpu.stream();
17365        let mut b = __s_b.launch_builder(&f);
17366        b.arg(bytes)
17367            .arg(&aq)
17368            .arg(&ad)
17369            .arg(&mut y)
17370            .arg(&inf)
17371            .arg(&outf)
17372            .arg(&mi)
17373            .arg(&rb);
17374        unsafe {
17375            b.launch(cfg)?;
17376        }
17377        Ok(y)
17378    }
17379
17380    /// H100 warpgroup GEMM raw entry (task 8): launch `qmatvec_gemm_q8_0_wgmma` on an rp4
17381    /// split-plane mirror + pre-quantized (aq, ad) activation planes. One warpgroup (128 thr)
17382    /// owns a 64x64 C tile; grid (out_f/64, ceil(m/64)). out_f % 64 == 0 REQUIRED (row loads
17383    /// and dplane scale reads are unguarded); the token edge is guarded in-kernel.
17384    /// Standalone harness verdict (tools/bench_q8_gemm_wgmma.cu, 4096x4096x512): rel 1.6e-05
17385    /// vs CPU ref, 179us vs the portable mma kernel's 688us (3.84x, unpipelined).
17386    pub fn qmatvec_gemm_q8_0_wgmma_raw(
17387        &self,
17388        rp4: &CudaSlice<u8>,
17389        aq: &CudaSlice<i8>,
17390        ad: &CudaSlice<f32>,
17391        m: usize,
17392        in_f: usize,
17393        out_f: usize,
17394    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17395        assert!(
17396            out_f % 64 == 0 && in_f % 32 == 0,
17397            "wgmma GEMM needs out_f%64==0, in_f%32==0"
17398        );
17399        let f = self.func("qmatvec_gemm_q8_0_wgmma");
17400        let mut y = self.alloc_uninit::<f32>(m * out_f)?; // full-overwrite GEMM output
17401        let cfg = LaunchConfig {
17402            grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
17403            block_dim: (128, 1, 1),
17404            shared_mem_bytes: 0,
17405        };
17406        let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
17407        let __s_b = self.gpu.stream();
17408        let mut b = __s_b.launch_builder(&f);
17409        b.arg(rp4)
17410            .arg(aq)
17411            .arg(ad)
17412            .arg(&mut y)
17413            .arg(&inf)
17414            .arg(&outf)
17415            .arg(&mi);
17416        unsafe {
17417            b.launch(cfg)?;
17418        }
17419        Ok(y)
17420    }
17421
17422    /// y[i] *= s. NVFP4 per-tensor macro-scale broadcast over the whole output.
17423    pub fn scale_inplace(
17424        &self,
17425        y: &mut CudaSlice<f32>,
17426        s: f32,
17427        n: usize,
17428    ) -> Result<(), Box<dyn std::error::Error>> {
17429        let f = self.func("scale_f32");
17430        let cfg = LaunchConfig::for_num_elems(n as u32);
17431        let (sf, ni) = (s, n as i32);
17432        let __s_b = self.gpu.stream();
17433        let mut b = __s_b.launch_builder(&f);
17434        b.arg(y).arg(&sf).arg(&ni);
17435        unsafe {
17436            b.launch(cfg)?;
17437        }
17438        Ok(())
17439    }
17440
17441    /// MEMRA_FULL_PREC dequant-on-use: expand a bf16-resident weight (`GpuTensor::FloatBf16`, raw
17442    /// bf16 bytes) to a transient f32 scratch of `n` elements, which then feeds the existing f32
17443    /// cuBLASLt GEMV. The scratch is freed when the caller drops it, so peak VRAM = resident bf16
17444    /// weights + ONE (largest) weight's f32 expansion + activations. SLOW IS FINE (research mode).
17445    pub fn bf16_to_f32(
17446        &self,
17447        data: &cudarc::driver::CudaView<'_, u8>,
17448        n: usize,
17449    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17450        let mut out = self.alloc_uninit::<f32>(n)?;
17451        let f = self.func("bf16_to_f32");
17452        let cfg = LaunchConfig::for_num_elems(n as u32);
17453        let ni = n as i32;
17454        let __s_b = self.gpu.stream();
17455        let mut b = __s_b.launch_builder(&f);
17456        b.arg(data).arg(&mut out).arg(&ni);
17457        unsafe {
17458            b.launch(cfg)?;
17459        }
17460        Ok(out)
17461    }
17462
17463    /// Chunked bf16 linear (MEMRA_FULL_PREC): y[m,out] = x @ W_bf16^T with the f32 dequant scratch
17464    /// bounded to CHUNK_ROWS rows (256MB at in_f=4096) instead of the whole weight — the 4GB
17465    /// lm_head expansion OOM'd the 24GB budget. Row-chunking partitions OUTPUT rows; each row's
17466    /// dot is computed by the identical kernel on identical bytes, so per-(token,row) results are
17467    /// bit-identical to the unchunked form. `exact` selects linear_decode_exact (per-column m=1
17468    /// calls, the spec-verify contract) vs plain linear.
17469    fn linear_bf16_chunked(
17470        &self,
17471        x: &CudaSlice<f32>,
17472        data: &CudaSlice<u8>,
17473        m: usize,
17474        in_f: usize,
17475        out_f: usize,
17476        exact: bool,
17477        canonical_chunk_rows: Option<usize>,
17478    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17479        // MEMRA_STEP_TP_TIMING=1: cumulative cost of the per-call BF16->F32 expansion class
17480        // (alloc + convert kernel + f32 cuBLASLt = ~5x weight traffic). Prints every 1024 calls.
17481        // The stream sync per call perturbs wall; diagnostic only, never in a receipts run.
17482        static EXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
17483        static EXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
17484        static EXP_WBYTES: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
17485        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
17486        let started = timing.then(std::time::Instant::now);
17487        let result =
17488            self.linear_bf16_chunked_inner(x, data, m, in_f, out_f, exact, canonical_chunk_rows);
17489        if let Some(started) = started {
17490            use std::sync::atomic::Ordering;
17491            self.stream().synchronize()?;
17492            let ns = EXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
17493                + started.elapsed().as_nanos() as u64;
17494            let wb = EXP_WBYTES.fetch_add((in_f * out_f * 2) as u64, Ordering::Relaxed)
17495                + (in_f * out_f * 2) as u64;
17496            let calls = EXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
17497            if calls % 1024 == 0 {
17498                eprintln!(
17499                    "[bf16-expand-timing] calls={calls} total_ms={:.1} avg_us={:.1} \
17500                     weight_gb={:.2}",
17501                    ns as f64 / 1.0e6,
17502                    ns as f64 / calls as f64 / 1.0e3,
17503                    wb as f64 / 1.0e9,
17504                );
17505            }
17506        }
17507        result
17508    }
17509
17510    /// MEMRA_BF16_MMV=1: decode-time (m=1) BF16-resident matvec door. Numeric class change vs
17511    /// the expansion path (single-kernel deterministic tree reduce vs f32 cuBLASLt), so it is
17512    /// default OFF and gated by the run-gen argmax gate + boot battery like the other
17513    /// numeric-class doors (DEV_ROUTES precedent).
17514    pub(crate) fn bf16_mmv_on() -> bool {
17515        static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17516        *ON.get_or_init(|| std::env::var("MEMRA_BF16_MMV").as_deref() == Ok("1"))
17517    }
17518
17519    /// One-block-per-row BF16 matvec: y[out_f] = W_bf16[out_f, in_f] @ x[in_f], f32 accumulate.
17520    /// Weights read once as raw bf16 (same bits<<16 expansion contract as `deq`'s QT_BF16 arm).
17521    fn matvec_bf16(
17522        &self,
17523        data: &CudaSlice<u8>,
17524        x: &CudaSlice<f32>,
17525        in_f: usize,
17526        out_f: usize,
17527    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17528        if data.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 {
17529            return Err(format!(
17530                "matvec_bf16 geometry bytes={} x={} in={in_f} out={out_f}",
17531                data.len(),
17532                x.len()
17533            )
17534            .into());
17535        }
17536        let mut y = self.alloc_uninit::<f32>(out_f)?;
17537        let f = self.func("matvec_bf16_f32acc");
17538        let cfg = LaunchConfig {
17539            grid_dim: (out_f as u32, 1, 1),
17540            block_dim: (mmv_block(), 1, 1),
17541            shared_mem_bytes: 0,
17542        };
17543        let ini = in_f as i32;
17544        let __s_bld = self.gpu.stream();
17545        let mut bld = __s_bld.launch_builder(&f);
17546        bld.arg(data).arg(x).arg(&mut y).arg(&ini);
17547        unsafe {
17548            bld.launch(cfg)?;
17549        }
17550        Ok(y)
17551    }
17552
17553    /// Fused QK rms-norm + neox rope (t=1): one launch per rank replaces two rms_norm
17554    /// launches, a position upload, and the rope launch; the position is read directly from
17555    /// the caller's device counter (UVA). Numeric-class door (see the kernel doc).
17556    #[allow(clippy::too_many_arguments)]
17557    /// FUSION #1: qk norms + rope + dcw KV append + last-block len inc, one launch
17558    /// (replaces qk_norm_rope_into + append_kv_quantized_dcw + inc_i32 on the dcw path).
17559    /// Bit-identical to the split kernels; requires head_dim == 128 and
17560    /// kv_dim_v == kv_dim_k == nh_k * head_dim (caller-guarded fallback otherwise).
17561    #[allow(clippy::too_many_arguments)]
17562    /// T-ROW twin of `qk_norm_rope_append_inc_dcw` over a per-row session table (six u64
17563    /// words per row: K plane, V plane, len_ptr, base_ptr, done_ctr, pos_ptr). Raw q/k/v
17564    /// come from the [t, dim] tcol slabs; roped q lands in the [t, nh_q*head_dim] slab.
17565    /// Per-(row, head) block program == the t=1 kernel — bit-identical per row.
17566    #[allow(clippy::too_many_arguments)]
17567    pub fn qk_norm_rope_append_inc_dcw_rows(
17568        &self,
17569        q_raw_t: &CudaSlice<f32>,
17570        k_raw_t: &CudaSlice<f32>,
17571        v_raw_t: &CudaSlice<f32>,
17572        qw: &CudaSlice<f32>,
17573        kw: &CudaSlice<f32>,
17574        q_out_t: &mut CudaSlice<f32>,
17575        k_out_t: &mut CudaSlice<f32>,
17576        tab: &CudaSlice<u64>,
17577        pos_t: &CudaSlice<i32>,
17578        same_session: bool,
17579        t: usize,
17580        kv_dim_k: usize,
17581        kv_dim_v: usize,
17582        k_tok_bytes: usize,
17583        v_tok_bytes: usize,
17584        head_dim: usize,
17585        n_dims: usize,
17586        nh_q: usize,
17587        nh_k: usize,
17588        eps: f32,
17589        freq_base: f32,
17590        freq_scale: f32,
17591        ff: Option<&CudaSlice<f32>>,
17592    ) -> Result<(), Box<dyn std::error::Error>> {
17593        if head_dim != 128
17594            || kv_dim_v != kv_dim_k
17595            || kv_dim_k != nh_k * head_dim
17596            || t == 0
17597            || t > 32
17598            || tab.len() < t * 6
17599            || pos_t.len() < t
17600            || q_raw_t.len() < t * nh_q * head_dim
17601            || k_raw_t.len() < t * nh_k * head_dim
17602            || v_raw_t.len() < t * kv_dim_v
17603            || q_out_t.len() < t * nh_q * head_dim
17604            || k_out_t.len() < t * nh_k * head_dim
17605        {
17606            return Err(format!(
17607                "qk_norm_rope_append_inc_rows geometry head_dim={head_dim} t={t} \
17608                 nh_q={nh_q} nh_k={nh_k}"
17609            )
17610            .into());
17611        }
17612        let f = self.func("qk_norm_rope_append_inc_dcw_rows");
17613        let same_t: i32 = if same_session { t as i32 } else { 0 };
17614        let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
17615        let cfg = LaunchConfig {
17616            grid_dim: ((nh_q + nh_k) as u32, 1, t as u32),
17617            block_dim: (128, 1, 1),
17618            shared_mem_bytes: 0,
17619        };
17620        let (kvk, kvv) = (kv_dim_k as i32, kv_dim_v as i32);
17621        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17622        let (hd, nd, nq, nk) = (head_dim as i32, n_dims as i32, nh_q as i32, nh_k as i32);
17623        let null: u64 = 0;
17624        let __s_b = self.gpu.stream();
17625        let mut b = __s_b.launch_builder(&f);
17626        b.arg(q_raw_t)
17627            .arg(k_raw_t)
17628            .arg(v_raw_t)
17629            .arg(qw)
17630            .arg(kw)
17631            .arg(q_out_t)
17632            .arg(k_out_t)
17633            .arg(tab)
17634            .arg(pos_t)
17635            .arg(&same_t)
17636            .arg(&kvk)
17637            .arg(&kvv)
17638            .arg(&ktb)
17639            .arg(&vtb)
17640            .arg(&hd)
17641            .arg(&nd)
17642            .arg(&nq)
17643            .arg(&nk)
17644            .arg(&eps)
17645            .arg(&theta_scale)
17646            .arg(&freq_scale);
17647        match ff {
17648            Some(freqs) => {
17649                b.arg(freqs);
17650            }
17651            None => {
17652                b.arg(&null);
17653            }
17654        }
17655        unsafe {
17656            b.launch(cfg)?;
17657        }
17658        Ok(())
17659    }
17660
17661    pub fn qk_norm_rope_append_inc_dcw(
17662        &self,
17663        q_raw: &CudaSlice<f32>,
17664        k_raw: &CudaSlice<f32>,
17665        v_raw: &CudaSlice<f32>,
17666        qw: &CudaSlice<f32>,
17667        kw: &CudaSlice<f32>,
17668        q_out: &mut CudaSlice<f32>,
17669        k_out: &mut CudaSlice<f32>,
17670        pos: &CudaSlice<i32>,
17671        k_plane: &mut CudaSlice<u8>,
17672        v_plane: &mut CudaSlice<u8>,
17673        // Shared ref by the planes_and_counters_mut split-borrow contract; the kernel is the
17674        // (single) writer, exactly like the split append+inc pair it replaces.
17675        len_dev: &CudaSlice<i32>,
17676        base_dev: Option<&CudaSlice<i32>>,
17677        done_ctr: &mut CudaSlice<u32>,
17678        kv_dim_k: usize,
17679        kv_dim_v: usize,
17680        k_tok_bytes: usize,
17681        v_tok_bytes: usize,
17682        head_dim: usize,
17683        n_dims: usize,
17684        nh_q: usize,
17685        nh_k: usize,
17686        eps: f32,
17687        freq_base: f32,
17688        freq_scale: f32,
17689        ff: Option<&CudaSlice<f32>>,
17690    ) -> Result<(), Box<dyn std::error::Error>> {
17691        if head_dim != 128
17692            || kv_dim_v != kv_dim_k
17693            || kv_dim_k != nh_k * head_dim
17694            || q_raw.len() < nh_q * head_dim
17695            || k_raw.len() < nh_k * head_dim
17696            || v_raw.len() < kv_dim_v
17697            || q_out.len() < nh_q * head_dim
17698            || k_out.len() < nh_k * head_dim
17699            || pos.is_empty()
17700            || done_ctr.is_empty()
17701        {
17702            return Err(format!(
17703                "qk_norm_rope_append_inc geometry head_dim={head_dim} nh_q={nh_q} nh_k={nh_k}                  kv_k={kv_dim_k} kv_v={kv_dim_v}"
17704            )
17705            .into());
17706        }
17707        let f = self.func("qk_norm_rope_append_inc_dcw");
17708        let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
17709        let cfg = LaunchConfig {
17710            grid_dim: ((nh_q + nh_k) as u32, 1, 1),
17711            block_dim: (128, 1, 1),
17712            shared_mem_bytes: 0,
17713        };
17714        let (kvk, kvv) = (kv_dim_k as i32, kv_dim_v as i32);
17715        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17716        let (hd, nd, nq) = (head_dim as i32, n_dims as i32, nh_q as i32);
17717        let null: u64 = 0;
17718        let __s_b = self.gpu.stream();
17719        let mut b = __s_b.launch_builder(&f);
17720        b.arg(q_raw)
17721            .arg(k_raw)
17722            .arg(v_raw)
17723            .arg(qw)
17724            .arg(kw)
17725            .arg(q_out)
17726            .arg(k_out)
17727            .arg(pos)
17728            .arg(&mut *k_plane)
17729            .arg(&mut *v_plane)
17730            .arg(len_dev);
17731        match base_dev {
17732            Some(base) => {
17733                b.arg(base);
17734            }
17735            None => {
17736                b.arg(&null);
17737            }
17738        }
17739        b.arg(&mut *done_ctr)
17740            .arg(&kvk)
17741            .arg(&kvv)
17742            .arg(&ktb)
17743            .arg(&vtb)
17744            .arg(&hd)
17745            .arg(&nd)
17746            .arg(&nq)
17747            .arg(&eps)
17748            .arg(&theta_scale)
17749            .arg(&freq_scale);
17750        match ff {
17751            Some(freqs) => {
17752                b.arg(freqs);
17753            }
17754            None => {
17755                b.arg(&null);
17756            }
17757        }
17758        unsafe {
17759            b.launch(cfg)?;
17760        }
17761        Ok(())
17762    }
17763
17764    pub fn qk_norm_rope_into(
17765        &self,
17766        q_raw: &CudaSlice<f32>,
17767        k_raw: &CudaSlice<f32>,
17768        qw: &CudaSlice<f32>,
17769        kw: &CudaSlice<f32>,
17770        q_out: &mut CudaSlice<f32>,
17771        k_out: &mut CudaSlice<f32>,
17772        pos: &CudaSlice<i32>,
17773        head_dim: usize,
17774        n_dims: usize,
17775        nh_q: usize,
17776        nh_k: usize,
17777        eps: f32,
17778        freq_base: f32,
17779        freq_scale: f32,
17780        ff: Option<&CudaSlice<f32>>,
17781    ) -> Result<(), Box<dyn std::error::Error>> {
17782        if head_dim > 512
17783            || q_raw.len() < nh_q * head_dim
17784            || k_raw.len() < nh_k * head_dim
17785            || q_out.len() < nh_q * head_dim
17786            || k_out.len() < nh_k * head_dim
17787            || qw.len() < head_dim
17788            || kw.len() < head_dim
17789            || pos.is_empty()
17790        {
17791            return Err(format!(
17792                "qk_norm_rope geometry head_dim={head_dim} nh_q={nh_q} nh_k={nh_k}"
17793            )
17794            .into());
17795        }
17796        let f = self.func("qk_norm_rope_f32");
17797        let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
17798        let cfg = LaunchConfig {
17799            grid_dim: ((nh_q + nh_k) as u32, 1, 1),
17800            block_dim: (128, 1, 1),
17801            shared_mem_bytes: 0,
17802        };
17803        let (hd, nd, nq) = (head_dim as i32, n_dims as i32, nh_q as i32);
17804        let __s_b = self.gpu.stream();
17805        let mut b = __s_b.launch_builder(&f);
17806        b.arg(q_raw)
17807            .arg(k_raw)
17808            .arg(qw)
17809            .arg(kw)
17810            .arg(q_out)
17811            .arg(k_out)
17812            .arg(pos)
17813            .arg(&hd)
17814            .arg(&nd)
17815            .arg(&nq)
17816            .arg(&eps)
17817            .arg(&theta_scale)
17818            .arg(&freq_scale);
17819        match ff {
17820            Some(ffv) => {
17821                b.arg(ffv);
17822                unsafe {
17823                    b.launch(cfg)?;
17824                }
17825            }
17826            None => {
17827                let null: u64 = 0;
17828                b.arg(&null);
17829                unsafe {
17830                    b.launch(cfg)?;
17831                }
17832            }
17833        }
17834        Ok(())
17835    }
17836
17837    /// Four-block F32 matvec with in-order block accumulation (see the kernel doc): one
17838    /// launch computes a rank's whole O partial from its four canonical column blocks.
17839    #[allow(clippy::too_many_arguments)]
17840    pub fn matvec_f32_b4_into(
17841        &self,
17842        w: [&CudaSlice<f32>; 4],
17843        x: &CudaSlice<f32>,
17844        y: &mut CudaSlice<f32>,
17845        block_cols: usize,
17846        out_f: usize,
17847    ) -> Result<(), Box<dyn std::error::Error>> {
17848        if block_cols % 4 != 0
17849            || x.len() < 4 * block_cols
17850            || y.len() < out_f
17851            || w.iter().any(|w| w.len() != out_f * block_cols)
17852        {
17853            return Err(format!(
17854                "matvec_f32_b4 geometry block_cols={block_cols} out={out_f} x={}",
17855                x.len()
17856            )
17857            .into());
17858        }
17859        let f = self.func("matvec_f32_b4");
17860        let cfg = LaunchConfig {
17861            grid_dim: (out_f as u32, 1, 1),
17862            block_dim: (128, 1, 1),
17863            shared_mem_bytes: 0,
17864        };
17865        let (bc, of) = (block_cols as i32, out_f as i32);
17866        let __s_b = self.gpu.stream();
17867        let mut b = __s_b.launch_builder(&f);
17868        b.arg(w[0])
17869            .arg(w[1])
17870            .arg(w[2])
17871            .arg(w[3])
17872            .arg(x)
17873            .arg(y)
17874            .arg(&bc)
17875            .arg(&of);
17876        unsafe {
17877            b.launch(cfg)?;
17878        }
17879        Ok(())
17880    }
17881
17882    /// Sequential weighted row-sum: y[i] = sum_p w[p] * x[p*width+i] in row order — the exact
17883    /// per-element FP chain of a zero-reset plus n_rows sequential axpy launches.
17884    pub fn axpy_rows_seq_into(
17885        &self,
17886        x: &CudaSlice<f32>,
17887        w: &CudaSlice<f32>,
17888        y: &mut CudaSlice<f32>,
17889        width: usize,
17890        n_rows: usize,
17891    ) -> Result<(), Box<dyn std::error::Error>> {
17892        if x.len() < n_rows * width || w.len() < n_rows || y.len() < width {
17893            return Err(format!(
17894                "axpy_rows_seq geometry x={} w={} y={} width={width} rows={n_rows}",
17895                x.len(),
17896                w.len(),
17897                y.len()
17898            )
17899            .into());
17900        }
17901        let f = self.func("axpy_rows_seq_f32");
17902        let cfg = LaunchConfig::for_num_elems(width as u32);
17903        let (wi, nr) = (width as i32, n_rows as i32);
17904        let __s_b = self.gpu.stream();
17905        let mut b = __s_b.launch_builder(&f);
17906        b.arg(x).arg(w).arg(y).arg(&wi).arg(&nr);
17907        unsafe {
17908            b.launch(cfg)?;
17909        }
17910        Ok(())
17911    }
17912
17913    /// Row-offset twin of `axpy_rows_seq_md_into` (spec verify t-column combine): the
17914    /// accumulation runs over rows [row0, row0+n_rows) of a taller partial slab — the
17915    /// exact sequential FP chain of the base kernel over that window.
17916    #[allow(clippy::too_many_arguments)]
17917    pub fn axpy_rows_seq_md_off_into(
17918        &self,
17919        x: &CudaSlice<f32>,
17920        w_route: &CudaSlice<f32>,
17921        md: &CudaSlice<f32>,
17922        sel: &CudaSlice<i32>,
17923        y: &mut CudaSlice<f32>,
17924        width: usize,
17925        n_rows: usize,
17926        row0: usize,
17927    ) -> Result<(), Box<dyn std::error::Error>> {
17928        if x.len() < (row0 + n_rows) * width
17929            || w_route.len() < row0 + n_rows
17930            || sel.len() < row0 + n_rows
17931            || y.len() < width
17932        {
17933            return Err(format!(
17934                "axpy_rows_seq_md_off geometry x={} w={} sel={} y={} width={width} \
17935                 rows={n_rows} row0={row0}",
17936                x.len(),
17937                w_route.len(),
17938                sel.len(),
17939                y.len()
17940            )
17941            .into());
17942        }
17943        let f = self.func("axpy_rows_seq_md_off_f32");
17944        let cfg = LaunchConfig::for_num_elems(width as u32);
17945        let (wi, nr, r0) = (width as i32, n_rows as i32, row0 as i32);
17946        let __s_b = self.gpu.stream();
17947        let mut b = __s_b.launch_builder(&f);
17948        b.arg(x)
17949            .arg(w_route)
17950            .arg(md)
17951            .arg(sel)
17952            .arg(y)
17953            .arg(&wi)
17954            .arg(&nr)
17955            .arg(&r0);
17956        unsafe {
17957            b.launch(cfg)?;
17958        }
17959        Ok(())
17960    }
17961
17962    /// Device-routed twin of `axpy_rows_seq_into`: the per-row weight folds in-kernel
17963    /// (w_route[p] * md[sel[p]] — the same single f32 multiply the host fold performs).
17964    #[allow(clippy::too_many_arguments)]
17965    pub fn axpy_rows_seq_md_into(
17966        &self,
17967        x: &CudaSlice<f32>,
17968        w_route: &CudaSlice<f32>,
17969        md: &CudaSlice<f32>,
17970        sel: &CudaSlice<i32>,
17971        y: &mut CudaSlice<f32>,
17972        width: usize,
17973        n_rows: usize,
17974    ) -> Result<(), Box<dyn std::error::Error>> {
17975        if x.len() < n_rows * width
17976            || w_route.len() < n_rows
17977            || sel.len() < n_rows
17978            || y.len() < width
17979        {
17980            return Err(format!(
17981                "axpy_rows_seq_md geometry x={} w={} sel={} y={} width={width} rows={n_rows}",
17982                x.len(),
17983                w_route.len(),
17984                sel.len(),
17985                y.len()
17986            )
17987            .into());
17988        }
17989        let f = self.func("axpy_rows_seq_md_f32");
17990        let cfg = LaunchConfig::for_num_elems(width as u32);
17991        let (wi, nr) = (width as i32, n_rows as i32);
17992        let __s_b = self.gpu.stream();
17993        let mut b = __s_b.launch_builder(&f);
17994        b.arg(x)
17995            .arg(w_route)
17996            .arg(md)
17997            .arg(sel)
17998            .arg(y)
17999            .arg(&wi)
18000            .arg(&nr);
18001        unsafe {
18002            b.launch(cfg)?;
18003        }
18004        Ok(())
18005    }
18006
18007    /// BF16 twin of `matvec_f32_qkv_into` (weights as raw checkpoint bf16 bytes).
18008    #[allow(clippy::too_many_arguments)]
18009    /// T-COLUMN twin of `matvec_bf16_qkvg_into` (spec verify): weights read once, T input
18010    /// columns accumulated with per-column FP order identical to the t=1 kernel. Outputs
18011    /// land column-major-of-rows: yq[c*out_q + row] etc.
18012    #[allow(clippy::too_many_arguments)]
18013    pub fn matvec_bf16_qkvg_tcol_into(
18014        &self,
18015        wq: &CudaSlice<u8>,
18016        wk: &CudaSlice<u8>,
18017        wv: &CudaSlice<u8>,
18018        wg: &CudaSlice<u8>,
18019        x_t: &CudaSlice<f32>,
18020        yq: &mut CudaSlice<f32>,
18021        yk: &mut CudaSlice<f32>,
18022        yv: &mut CudaSlice<f32>,
18023        yg: &mut CudaSlice<f32>,
18024        in_f: usize,
18025        out_q: usize,
18026        out_kv: usize,
18027        out_g: usize,
18028        t: usize,
18029    ) -> Result<(), Box<dyn std::error::Error>> {
18030        if t == 0
18031            || t > 8
18032            || in_f % 8 != 0
18033            || x_t.len() < t * in_f
18034            || yq.len() < t * out_q
18035            || yk.len() < t * out_kv
18036            || yv.len() < t * out_kv
18037            || (out_g > 0 && yg.len() < t * out_g)
18038        {
18039            return Err("matvec_bf16_qkvg_tcol geometry".into());
18040        }
18041        let grid = out_q + 2 * out_kv + out_g;
18042        let cfg = LaunchConfig {
18043            grid_dim: (grid as u32, 1, 1),
18044            block_dim: (mmv_block(), 1, 1),
18045            shared_mem_bytes: 0,
18046        };
18047        let (ini, oq, okv, og, ti) = (
18048            in_f as i32,
18049            out_q as i32,
18050            out_kv as i32,
18051            out_g as i32,
18052            t as i32,
18053        );
18054        let __s_b = self.gpu.stream();
18055        // One runtime-T program for every live width. The compile-time 2/4/8 twins are
18056        // retained in the fatbin as research controls, but dispatching them by the current
18057        // batch width changes kernels inside a request when peers arrive or retire. That is
18058        // a load-history numeric-program switch, and their pre-twin TOKFP receipts did not
18059        // qualify it (Hermes `64fa2b55baf0d887`).
18060        let f = self.func("matvec_bf16_qkvg_tcol");
18061        let mut b = __s_b.launch_builder(&f);
18062        b.arg(wq)
18063            .arg(wk)
18064            .arg(wv)
18065            .arg(wg)
18066            .arg(x_t)
18067            .arg(yq)
18068            .arg(yk)
18069            .arg(yv)
18070            .arg(yg)
18071            .arg(&ini)
18072            .arg(&oq)
18073            .arg(&okv)
18074            .arg(&og)
18075            .arg(&ti);
18076        unsafe {
18077            b.launch(cfg)?;
18078        }
18079        Ok(())
18080    }
18081
18082    pub fn matvec_bf16_qkvg_into(
18083        &self,
18084        wq: &CudaSlice<u8>,
18085        wk: &CudaSlice<u8>,
18086        wv: &CudaSlice<u8>,
18087        wg: &CudaSlice<u8>,
18088        x: &CudaSlice<f32>,
18089        yq: &mut CudaSlice<f32>,
18090        yk: &mut CudaSlice<f32>,
18091        yv: &mut CudaSlice<f32>,
18092        yg: &mut CudaSlice<f32>,
18093        in_f: usize,
18094        out_q: usize,
18095        out_kv: usize,
18096        out_g: usize,
18097    ) -> Result<(), Box<dyn std::error::Error>> {
18098        if in_f % 8 != 0
18099            || wq.len() != out_q * in_f * 2
18100            || wk.len() != out_kv * in_f * 2
18101            || wv.len() != out_kv * in_f * 2
18102            || wg.len() < out_g * in_f * 2
18103            || x.len() < in_f
18104            || yq.len() < out_q
18105            || yk.len() < out_kv
18106            || yv.len() < out_kv
18107            || (out_g > 0 && yg.len() < out_g)
18108        {
18109            return Err(format!(
18110                "fused bf16 QKV geometry in={in_f} out_q={out_q} out_kv={out_kv} out_g={out_g}"
18111            )
18112            .into());
18113        }
18114        let f = self.func("matvec_bf16_qkvg");
18115        let cfg = LaunchConfig {
18116            grid_dim: ((out_q + 2 * out_kv + out_g) as u32, 1, 1),
18117            block_dim: (mmv_block(), 1, 1),
18118            shared_mem_bytes: 0,
18119        };
18120        let (inf, oq, okv, og) = (in_f as i32, out_q as i32, out_kv as i32, out_g as i32);
18121        let __s_b = self.gpu.stream();
18122        let mut b = __s_b.launch_builder(&f);
18123        b.arg(wq)
18124            .arg(wk)
18125            .arg(wv)
18126            .arg(wg)
18127            .arg(x)
18128            .arg(yq)
18129            .arg(yk)
18130            .arg(yv)
18131            .arg(yg)
18132            .arg(&inf)
18133            .arg(&oq)
18134            .arg(&okv)
18135            .arg(&og);
18136        unsafe {
18137            b.launch(cfg)?;
18138        }
18139        Ok(())
18140    }
18141
18142    /// BF16 twin of `matvec_f32_b4_into` (weights as raw checkpoint bf16 bytes).
18143    pub fn matvec_bf16_b4_into(
18144        &self,
18145        w: [&CudaSlice<u8>; 4],
18146        x: &CudaSlice<f32>,
18147        y: &mut CudaSlice<f32>,
18148        block_cols: usize,
18149        out_f: usize,
18150    ) -> Result<(), Box<dyn std::error::Error>> {
18151        if block_cols % 8 != 0
18152            || x.len() < 4 * block_cols
18153            || y.len() < out_f
18154            || w.iter().any(|w| w.len() != out_f * block_cols * 2)
18155        {
18156            return Err(format!(
18157                "bf16 b4 geometry block_cols={block_cols} out={out_f} x={}",
18158                x.len()
18159            )
18160            .into());
18161        }
18162        // MEMRA_B4_X2=1: the #2b grid-halving twin — half the blocks, two rows each,
18163        // bit-identical per row (the second row's stream hides the first's reduce tail).
18164        static B4_X2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18165        let x2 = *B4_X2.get_or_init(|| std::env::var("MEMRA_B4_X2").as_deref() == Ok("1"));
18166        let f = self.func(if x2 {
18167            "matvec_bf16_b4_x2"
18168        } else {
18169            "matvec_bf16_b4"
18170        });
18171        let grid = if x2 { out_f.div_ceil(2) } else { out_f };
18172        let cfg = LaunchConfig {
18173            grid_dim: (grid as u32, 1, 1),
18174            block_dim: (mmv_block(), 1, 1),
18175            shared_mem_bytes: 0,
18176        };
18177        let (bc, of) = (block_cols as i32, out_f as i32);
18178        let __s_b = self.gpu.stream();
18179        let mut b = __s_b.launch_builder(&f);
18180        b.arg(w[0])
18181            .arg(w[1])
18182            .arg(w[2])
18183            .arg(w[3])
18184            .arg(x)
18185            .arg(y)
18186            .arg(&bc)
18187            .arg(&of);
18188        unsafe {
18189            b.launch(cfg)?;
18190        }
18191        Ok(())
18192    }
18193
18194    /// T-COLUMN twin of `matvec_bf16_b4_into` (spec verify o_proj): weights read once, T
18195    /// gated rows (each 4*block_cols wide) accumulated with per-column FP order identical
18196    /// to the t=1 kernel. Outputs land y[c*out_f + row]. Same blockDim as the t=1 launch —
18197    /// the shared-memory reduce order depends on it. Refuses under MEMRA_B4_X2 (different
18198    /// t=1 program).
18199    pub fn matvec_bf16_b4_tcol_into(
18200        &self,
18201        w: [&CudaSlice<u8>; 4],
18202        x_t: &CudaSlice<f32>,
18203        y_t: &mut CudaSlice<f32>,
18204        block_cols: usize,
18205        out_f: usize,
18206        t: usize,
18207    ) -> Result<(), Box<dyn std::error::Error>> {
18208        if block_cols % 8 != 0
18209            || t == 0
18210            || t > 8
18211            || x_t.len() < t * 4 * block_cols
18212            || y_t.len() < t * out_f
18213            || w.iter().any(|w| w.len() != out_f * block_cols * 2)
18214        {
18215            return Err(format!(
18216                "bf16 b4 tcol geometry block_cols={block_cols} out={out_f} t={t} x={}",
18217                x_t.len()
18218            )
18219            .into());
18220        }
18221        if std::env::var("MEMRA_B4_X2").as_deref() == Ok("1") {
18222            return Err(
18223                "b4 tcol verify is qualified against the plain b4 kernel only \
18224                        (MEMRA_B4_X2=1 is a different t=1 program)"
18225                    .into(),
18226            );
18227        }
18228        // Keep one runtime-T program at every live width. Compile-time twins remain research
18229        // controls only; selecting them from the changing batch width switches programs
18230        // mid-request.
18231        let cfg = LaunchConfig {
18232            grid_dim: (out_f as u32, 1, 1),
18233            block_dim: (mmv_block(), 1, 1),
18234            shared_mem_bytes: 0,
18235        };
18236        let (bc, of, ti) = (block_cols as i32, out_f as i32, t as i32);
18237        let __s_b = self.gpu.stream();
18238        let f = self.func("matvec_bf16_b4_tcol");
18239        let mut b = __s_b.launch_builder(&f);
18240        b.arg(w[0])
18241            .arg(w[1])
18242            .arg(w[2])
18243            .arg(w[3])
18244            .arg(x_t)
18245            .arg(y_t)
18246            .arg(&bc)
18247            .arg(&of)
18248            .arg(&ti);
18249        unsafe {
18250            b.launch(cfg)?;
18251        }
18252        Ok(())
18253    }
18254
18255    /// `matvec_bf16` writing into a caller-owned output (persistent-workspace form).
18256    /// q8_0 row bytes for an `in_f`-wide weight row: one 34-byte block per 32 weights.
18257    pub fn q8_0_row_bytes(in_f: usize) -> usize {
18258        in_f / 32 * 34
18259    }
18260
18261    /// Encode a resident bf16 weight slab into its q8_0 mirror (MEMRA_STEP_TP_W8). Runs once
18262    /// per matrix at load; the block program is the one `quant_K_block` writes for the KV
18263    /// cache, so the two formats cannot drift apart.
18264    pub fn encode_q8_0_from_bf16(
18265        &self,
18266        w_bf16: &CudaSlice<u8>,
18267        out: &mut CudaSlice<u8>,
18268        in_f: usize,
18269        out_f: usize,
18270    ) -> Result<(), Box<dyn std::error::Error>> {
18271        if in_f % 32 != 0
18272            || w_bf16.len() < in_f * out_f * 2
18273            || out.len() < out_f * Self::q8_0_row_bytes(in_f)
18274        {
18275            return Err(format!(
18276                "encode_q8_0_from_bf16 geometry in={in_f} out={out_f} src={} dst={}",
18277                w_bf16.len(),
18278                out.len()
18279            )
18280            .into());
18281        }
18282        let f = self.func("encode_q8_0_rows_from_bf16");
18283        // Flat 1D grid of (row, 32-block) pairs, 4 pairs per block: rows on grid.y would cap
18284        // at 65535 and the LM head has 128896 rows.
18285        const PAIRS_PER_BLOCK: u32 = 4;
18286        let pairs = (out_f * (in_f / 32)) as u64;
18287        let cfg = LaunchConfig {
18288            grid_dim: ((pairs.div_ceil(PAIRS_PER_BLOCK as u64)) as u32, 1, 1),
18289            block_dim: (32, PAIRS_PER_BLOCK, 1),
18290            shared_mem_bytes: 0,
18291        };
18292        let (ini, outi) = (in_f as i32, out_f as i32);
18293        let __s_b = self.gpu.stream();
18294        let mut b = __s_b.launch_builder(&f);
18295        b.arg(w_bf16).arg(out).arg(&ini).arg(&outi);
18296        unsafe {
18297            b.launch(cfg)?;
18298        }
18299        Ok(())
18300    }
18301
18302    /// ROW-RANGE-VIEW twin of `encode_q8_0_from_bf16`. Identical kernel, identical launch
18303    /// geometry, identical per-row program: only the operand type differs, because the split
18304    /// decode paths hold their rows as a `CudaView` of the resident slab, not as an owned slab.
18305    pub fn encode_q8_0_from_bf16_view(
18306        &self,
18307        w_bf16: &cudarc::driver::CudaView<'_, u8>,
18308        out: &mut CudaSlice<u8>,
18309        in_f: usize,
18310        out_f: usize,
18311    ) -> Result<(), Box<dyn std::error::Error>> {
18312        if in_f % 32 != 0
18313            || w_bf16.len() < in_f * out_f * 2
18314            || out.len() < out_f * Self::q8_0_row_bytes(in_f)
18315        {
18316            return Err(format!(
18317                "encode_q8_0_from_bf16_view geometry in={in_f} out={out_f} src={} dst={}",
18318                w_bf16.len(),
18319                out.len()
18320            )
18321            .into());
18322        }
18323        let f = self.func("encode_q8_0_rows_from_bf16");
18324        const PAIRS_PER_BLOCK: u32 = 4;
18325        let pairs = (out_f * (in_f / 32)) as u64;
18326        let cfg = LaunchConfig {
18327            grid_dim: ((pairs.div_ceil(PAIRS_PER_BLOCK as u64)) as u32, 1, 1),
18328            block_dim: (32, PAIRS_PER_BLOCK, 1),
18329            shared_mem_bytes: 0,
18330        };
18331        let (ini, outi) = (in_f as i32, out_f as i32);
18332        let __s_b = self.gpu.stream();
18333        let mut b = __s_b.launch_builder(&f);
18334        b.arg(w_bf16).arg(out).arg(&ini).arg(&outi);
18335        unsafe {
18336            b.launch(cfg)?;
18337        }
18338        Ok(())
18339    }
18340
18341    /// Fused q8_0 QKV against a q8_1 activation (MEMRA_STEP_TP_W8): one launch over the
18342    /// stacked q/k/v rows, each row running the exact `qmatvec_q8_0_mmvq_rp` per-row program.
18343    /// Bit-identical to three per-matrix mmvq calls; it exists because those three launches
18344    /// plus the activation quantize measured SLOWER than the bf16 fused kernel.
18345    #[allow(clippy::too_many_arguments)]
18346    pub fn qmatvec_q8_0_qkv_rp_into(
18347        &self,
18348        wq: &CudaSlice<u8>,
18349        wk: &CudaSlice<u8>,
18350        wv: &CudaSlice<u8>,
18351        aq: &CudaSlice<i8>,
18352        ad: &CudaSlice<f32>,
18353        yq: &mut CudaSlice<f32>,
18354        yk: &mut CudaSlice<f32>,
18355        yv: &mut CudaSlice<f32>,
18356        in_f: usize,
18357        out_q: usize,
18358        out_kv: usize,
18359    ) -> Result<(), Box<dyn std::error::Error>> {
18360        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
18361        let rows = out_q + 2 * out_kv;
18362        let nblk = in_f / 32;
18363        if in_f % 32 != 0
18364            || aq.len() < in_f
18365            || ad.len() < nblk
18366            || yq.len() < out_q
18367            || yk.len() < out_kv
18368            || yv.len() < out_kv
18369            || wq.len() < out_q * nblk * 34
18370            || wk.len() < out_kv * nblk * 34
18371            || wv.len() < out_kv * nblk * 34
18372        {
18373            return Err(
18374                format!("q8_0 qkv rp geometry in={in_f} out_q={out_q} out_kv={out_kv}").into(),
18375            );
18376        }
18377        let f = self.func("qmatvec_q8_0_qkv_rp");
18378        let cfg = LaunchConfig {
18379            grid_dim: ((rows as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
18380            block_dim: (32, ROWS_PER_BLOCK, 1),
18381            shared_mem_bytes: 0,
18382        };
18383        let (ini, oq, okv) = (in_f as i32, out_q as i32, out_kv as i32);
18384        let __s_b = self.gpu.stream();
18385        let mut b = __s_b.launch_builder(&f);
18386        b.arg(wq)
18387            .arg(wk)
18388            .arg(wv)
18389            .arg(aq)
18390            .arg(ad)
18391            .arg(yq)
18392            .arg(yk)
18393            .arg(yv)
18394            .arg(&ini)
18395            .arg(&oq)
18396            .arg(&okv);
18397        unsafe {
18398            b.launch(cfg)?;
18399        }
18400        Ok(())
18401    }
18402
18403    /// Fused q8_0 O projection over the four HEAD_SPLIT blocks (MEMRA_STEP_TP_W8): one
18404    /// launch, one warp per output row, per-block reduce then add — the same shape
18405    /// `matvec_bf16_b4` uses, against a q8_1 activation.
18406    #[allow(clippy::too_many_arguments)]
18407    pub fn qmatvec_q8_0_b4_rp_into(
18408        &self,
18409        w: [&CudaSlice<u8>; 4],
18410        aq: &CudaSlice<i8>,
18411        ad: &CudaSlice<f32>,
18412        y: &mut CudaSlice<f32>,
18413        block_cols: usize,
18414        out_f: usize,
18415    ) -> Result<(), Box<dyn std::error::Error>> {
18416        const ROWS_PER_BLOCK: u32 = 4; // matches MEMRA_MMVQ_ROWS in qmatvec.cu
18417        let nblk = block_cols / 32;
18418        if block_cols % 32 != 0
18419            || aq.len() < 4 * block_cols
18420            || ad.len() < 4 * nblk
18421            || y.len() < out_f
18422            || w.iter().any(|p| p.len() < out_f * nblk * 34)
18423        {
18424            return Err(format!("q8_0 b4 rp geometry block_cols={block_cols} out={out_f}").into());
18425        }
18426        let f = self.func("qmatvec_q8_0_b4_rp");
18427        let cfg = LaunchConfig {
18428            grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
18429            block_dim: (32, ROWS_PER_BLOCK, 1),
18430            shared_mem_bytes: 0,
18431        };
18432        let (bc, of) = (block_cols as i32, out_f as i32);
18433        let __s_b = self.gpu.stream();
18434        let mut b = __s_b.launch_builder(&f);
18435        b.arg(w[0])
18436            .arg(w[1])
18437            .arg(w[2])
18438            .arg(w[3])
18439            .arg(aq)
18440            .arg(ad)
18441            .arg(y)
18442            .arg(&bc)
18443            .arg(&of);
18444        unsafe {
18445            b.launch(cfg)?;
18446        }
18447        Ok(())
18448    }
18449
18450    /// T-column twin of `matvec_bf16_via_q8_mirror`: one q8 launch over all t rows, sharing the
18451    /// same pointer-keyed mirror cache and a t-wide q8_1 activation.
18452    fn matvec_bf16_via_q8_mirror_t(
18453        &self,
18454        data: &CudaSlice<u8>,
18455        x: &CudaSlice<f32>,
18456        y: &mut CudaSlice<f32>,
18457        in_f: usize,
18458        out_f: usize,
18459        t: usize,
18460    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
18461        use cudarc::driver::DevicePtr;
18462        let key = {
18463            let s = self.gpu.stream();
18464            let (p, _g) = data.device_ptr(&s);
18465            (p as u64, in_f as u32, out_f as u32)
18466        };
18467        {
18468            let mut mirrors = self
18469                .w8_mirrors
18470                .lock()
18471                .map_err(|_| "w8 mirror map is poisoned")?;
18472            if !mirrors.contains_key(&key) {
18473                let mut interleaved = self.alloc_u8_uninit(out_f * Self::q8_0_row_bytes(in_f))?;
18474                self.encode_q8_0_from_bf16(data, &mut interleaved, in_f, out_f)?;
18475                let planar = self.build_q8_rp4_raw(&interleaved, in_f, out_f)?;
18476                mirrors.insert(key, planar);
18477            }
18478        }
18479        let nblk = in_f / 32;
18480        // The t-wide activation scratch is keyed by (in_f, t-cap) so a wider walk regrows it.
18481        let akey = in_f * 64 + t.min(32);
18482        {
18483            let mut act = self.w8_act.lock().map_err(|_| "w8 act map is poisoned")?;
18484            if !act.contains_key(&akey) {
18485                let aq = self.alloc_i8_uninit(32 * in_f)?;
18486                let ad = self.alloc_uninit::<f32>(32 * nblk)?;
18487                act.insert(akey, (aq, ad));
18488            }
18489            let (aq, ad) = act.get_mut(&akey).expect("just inserted");
18490            self.quantize_q8_1_into(x, t, in_f, aq, ad)?;
18491        }
18492        let mirrors = self
18493            .w8_mirrors
18494            .lock()
18495            .map_err(|_| "w8 mirror map is poisoned")?;
18496        let act = self.w8_act.lock().map_err(|_| "w8 act map is poisoned")?;
18497        let mirror = mirrors.get(&key).expect("built above");
18498        let (aq, ad) = act.get(&akey).expect("built above");
18499        const ROWS_PER_BLOCK: u32 = 4;
18500        let (ini, of) = (in_f as i32, out_f as i32);
18501        // MEMRA_Q8T_WONCE=1: the weight-once twin — one row grid, each weight int4 loaded once
18502        // and dotted against all t columns. The `_t` form re-streams the shared weights per
18503        // column through __ldcs (measured 1.43-1.67x a single-column call for 2 columns).
18504        if q8t_wonce_on() && t <= 32 {
18505            let f = self.func(if t <= 8 {
18506                "qmatvec_q8_0_rows_tw"
18507            } else {
18508                "qmatvec_q8_0_rows_tw32"
18509            });
18510            let cfg = LaunchConfig {
18511                grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
18512                block_dim: (32, ROWS_PER_BLOCK, 1),
18513                shared_mem_bytes: 0,
18514            };
18515            let ti = t as i32;
18516            let __s_b = self.gpu.stream();
18517            let mut b = __s_b.launch_builder(&f);
18518            b.arg(mirror)
18519                .arg(aq)
18520                .arg(ad)
18521                .arg(&mut *y)
18522                .arg(&ini)
18523                .arg(&of)
18524                .arg(&ti);
18525            unsafe {
18526                b.launch(cfg)?;
18527            }
18528            return Ok(Some(()));
18529        }
18530        let f = self.func("qmatvec_q8_0_rows_t");
18531        let cfg = LaunchConfig {
18532            grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), t as u32, 1),
18533            block_dim: (32, ROWS_PER_BLOCK, 1),
18534            shared_mem_bytes: 0,
18535        };
18536        let __s_b = self.gpu.stream();
18537        let mut b = __s_b.launch_builder(&f);
18538        b.arg(mirror)
18539            .arg(aq)
18540            .arg(ad)
18541            .arg(&mut *y)
18542            .arg(&ini)
18543            .arg(&of);
18544        unsafe {
18545            b.launch(cfg)?;
18546        }
18547        Ok(Some(()))
18548    }
18549
18550    /// Get-or-build this bf16 weight's q8_0 mirror and run the GEMV through it. Returns
18551    /// `None` when the shape has no mirror form, so the caller falls back to bf16.
18552    fn matvec_bf16_via_q8_mirror(
18553        &self,
18554        data: &CudaSlice<u8>,
18555        x: &CudaSlice<f32>,
18556        y: &mut CudaSlice<f32>,
18557        in_f: usize,
18558        out_f: usize,
18559    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
18560        use cudarc::driver::DevicePtr;
18561        let key = {
18562            let s = self.gpu.stream();
18563            let (p, _g) = data.device_ptr(&s);
18564            (p as u64, in_f as u32, out_f as u32)
18565        };
18566        {
18567            let mut mirrors = self
18568                .w8_mirrors
18569                .lock()
18570                .map_err(|_| "w8 mirror map is poisoned")?;
18571            if !mirrors.contains_key(&key) {
18572                let mut interleaved = self.alloc_u8_uninit(out_f * Self::q8_0_row_bytes(in_f))?;
18573                self.encode_q8_0_from_bf16(data, &mut interleaved, in_f, out_f)?;
18574                let planar = self.build_q8_rp4_raw(&interleaved, in_f, out_f)?;
18575                mirrors.insert(key, planar);
18576                // Which weights this half actually covers is not obvious from the call graph:
18577                // the head and the shared expert may reach the GPU through the rows fast path
18578                // or the fused dual-silu launcher instead of here. One line per mirror answers
18579                // that without a profiler (the hybrid half measured +0.1% and this is how we
18580                // find out whether it even fired).
18581                if std::env::var("MEMRA_W8_TRACE").as_deref() == Ok("1") {
18582                    eprintln!(
18583                        "[w8-mirror] built in_f={in_f} out_f={out_f} mirrors={}",
18584                        mirrors.len()
18585                    );
18586                }
18587            }
18588        }
18589        let nblk = in_f / 32;
18590        {
18591            let mut act = self.w8_act.lock().map_err(|_| "w8 act map is poisoned")?;
18592            if !act.contains_key(&in_f) {
18593                let aq = self.alloc_uninit::<i8>(in_f)?;
18594                let ad = self.alloc_uninit::<f32>(nblk)?;
18595                act.insert(in_f, (aq, ad));
18596            }
18597            let (aq, ad) = act.get_mut(&in_f).expect("just inserted");
18598            self.quantize_q8_1_into(x, 1, in_f, aq, ad)?;
18599        }
18600        let mirrors = self
18601            .w8_mirrors
18602            .lock()
18603            .map_err(|_| "w8 mirror map is poisoned")?;
18604        let act = self.w8_act.lock().map_err(|_| "w8 act map is poisoned")?;
18605        let mirror = mirrors.get(&key).expect("built above");
18606        let (aq, ad) = act.get(&in_f).expect("built above");
18607        self.qmatvec_mmvq_into(
18608            mirror,
18609            aq,
18610            ad,
18611            1,
18612            in_f,
18613            out_f,
18614            QT_Q8_0,
18615            Self::q8_0_row_bytes(in_f),
18616            1.0,
18617            true,
18618            y,
18619        )?;
18620        Ok(Some(()))
18621    }
18622
18623    /// T-column q8_0 QKV for the VERIFY walk (MEMRA_STEP_TP_W8). nsys put the bf16 twin
18624    /// `matvec_bf16_qkvg_tcol` at 12.3% of spec GPU time and `matvec_bf16_b4_tcol` at 24.8%:
18625    /// the W8 door had replaced only the decode kernels, so 37% of the verify still streamed
18626    /// bf16. Bit-identical to `t` separate `qmatvec_q8_0_qkv_rp` calls.
18627    #[allow(clippy::too_many_arguments)]
18628    pub fn qmatvec_q8_0_qkv_rp_t_into(
18629        &self,
18630        wq: &CudaSlice<u8>,
18631        wk: &CudaSlice<u8>,
18632        wv: &CudaSlice<u8>,
18633        aq: &CudaSlice<i8>,
18634        ad: &CudaSlice<f32>,
18635        yq: &mut CudaSlice<f32>,
18636        yk: &mut CudaSlice<f32>,
18637        yv: &mut CudaSlice<f32>,
18638        in_f: usize,
18639        out_q: usize,
18640        out_kv: usize,
18641        t: usize,
18642    ) -> Result<(), Box<dyn std::error::Error>> {
18643        const ROWS_PER_BLOCK: u32 = 4;
18644        let rows = out_q + 2 * out_kv;
18645        let nblk = in_f / 32;
18646        if in_f % 32 != 0
18647            || t == 0
18648            || aq.len() < t * in_f
18649            || ad.len() < t * nblk
18650            || yq.len() < t * out_q
18651            || yk.len() < t * out_kv
18652            || yv.len() < t * out_kv
18653        {
18654            return Err(format!("q8_0 qkv rp_t geometry in={in_f} t={t}").into());
18655        }
18656        let (ini, oq, okv) = (in_f as i32, out_q as i32, out_kv as i32);
18657        // MEMRA_Q8T_WONCE=1: weight-once twin — see qmatvec.cu's `_tw` block for why the `_t`
18658        // form re-streams the fully-shared QKV weights per column (__ldcs + column grid axis;
18659        // measured 1.67x a single-column call for 2 columns).
18660        if q8t_wonce_on() && t <= 32 {
18661            let f = self.func(if t <= 8 {
18662                "qmatvec_q8_0_qkv_rp_tw"
18663            } else {
18664                "qmatvec_q8_0_qkv_rp_tw32"
18665            });
18666            let cfg = LaunchConfig {
18667                grid_dim: ((rows as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
18668                block_dim: (32, ROWS_PER_BLOCK, 1),
18669                shared_mem_bytes: 0,
18670            };
18671            let ti = t as i32;
18672            let __s_b = self.gpu.stream();
18673            let mut b = __s_b.launch_builder(&f);
18674            b.arg(wq)
18675                .arg(wk)
18676                .arg(wv)
18677                .arg(aq)
18678                .arg(ad)
18679                .arg(yq)
18680                .arg(yk)
18681                .arg(yv)
18682                .arg(&ini)
18683                .arg(&oq)
18684                .arg(&okv)
18685                .arg(&ti);
18686            unsafe {
18687                b.launch(cfg)?;
18688            }
18689            return Ok(());
18690        }
18691        let f = self.func("qmatvec_q8_0_qkv_rp_t");
18692        let cfg = LaunchConfig {
18693            grid_dim: ((rows as u32).div_ceil(ROWS_PER_BLOCK), t as u32, 1),
18694            block_dim: (32, ROWS_PER_BLOCK, 1),
18695            shared_mem_bytes: 0,
18696        };
18697        let __s_b = self.gpu.stream();
18698        let mut b = __s_b.launch_builder(&f);
18699        b.arg(wq)
18700            .arg(wk)
18701            .arg(wv)
18702            .arg(aq)
18703            .arg(ad)
18704            .arg(yq)
18705            .arg(yk)
18706            .arg(yv)
18707            .arg(&ini)
18708            .arg(&oq)
18709            .arg(&okv);
18710        unsafe {
18711            b.launch(cfg)?;
18712        }
18713        Ok(())
18714    }
18715
18716    /// T-column q8_0 o_proj over the four HEAD_SPLIT blocks (MEMRA_STEP_TP_W8, verify walk).
18717    /// Bit-identical to `t` separate `qmatvec_q8_0_b4_rp` calls.
18718    #[allow(clippy::too_many_arguments)]
18719    pub fn qmatvec_q8_0_b4_rp_t_into(
18720        &self,
18721        w: [&CudaSlice<u8>; 4],
18722        aq: &CudaSlice<i8>,
18723        ad: &CudaSlice<f32>,
18724        y: &mut CudaSlice<f32>,
18725        block_cols: usize,
18726        out_f: usize,
18727        t: usize,
18728    ) -> Result<(), Box<dyn std::error::Error>> {
18729        const ROWS_PER_BLOCK: u32 = 4;
18730        let nblk = block_cols / 32;
18731        if block_cols % 32 != 0
18732            || t == 0
18733            || aq.len() < t * 4 * block_cols
18734            || ad.len() < t * 4 * nblk
18735            || y.len() < t * out_f
18736        {
18737            return Err(format!("q8_0 b4 rp_t geometry cols={block_cols} t={t}").into());
18738        }
18739        let (bc, of) = (block_cols as i32, out_f as i32);
18740        // MEMRA_Q8T_WONCE=1: weight-once twin (see qmatvec.cu; `_t` measured 1.43x for 2 columns
18741        // on fully-shared o_proj weights).
18742        if q8t_wonce_on() && t <= 32 {
18743            let f = self.func(if t <= 8 {
18744                "qmatvec_q8_0_b4_rp_tw"
18745            } else {
18746                "qmatvec_q8_0_b4_rp_tw32"
18747            });
18748            let cfg = LaunchConfig {
18749                grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
18750                block_dim: (32, ROWS_PER_BLOCK, 1),
18751                shared_mem_bytes: 0,
18752            };
18753            let ti = t as i32;
18754            let __s_b = self.gpu.stream();
18755            let mut b = __s_b.launch_builder(&f);
18756            b.arg(w[0])
18757                .arg(w[1])
18758                .arg(w[2])
18759                .arg(w[3])
18760                .arg(aq)
18761                .arg(ad)
18762                .arg(y)
18763                .arg(&bc)
18764                .arg(&of)
18765                .arg(&ti);
18766            unsafe {
18767                b.launch(cfg)?;
18768            }
18769            return Ok(());
18770        }
18771        let f = self.func("qmatvec_q8_0_b4_rp_t");
18772        let cfg = LaunchConfig {
18773            grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), t as u32, 1),
18774            block_dim: (32, ROWS_PER_BLOCK, 1),
18775            shared_mem_bytes: 0,
18776        };
18777        let __s_b = self.gpu.stream();
18778        let mut b = __s_b.launch_builder(&f);
18779        b.arg(w[0])
18780            .arg(w[1])
18781            .arg(w[2])
18782            .arg(w[3])
18783            .arg(aq)
18784            .arg(ad)
18785            .arg(y)
18786            .arg(&bc)
18787            .arg(&of);
18788        unsafe {
18789            b.launch(cfg)?;
18790        }
18791        Ok(())
18792    }
18793
18794    /// MEMRA_W8_VIEW: the q8_0 mirror for a bf16 GEMV whose weight is a ROW-RANGE VIEW.
18795    /// `MEMRA_W8_HYBRID` hangs off `matvec_bf16_into`, and the two split decode paths pinned in
18796    /// the step37 serving env send only their HI half there: HEAD_SPLIT runs
18797    /// `rank1.matvec_bf16_into(head_hi)` beside `e.matvec_bf16_view_into(head_lo)`, and
18798    /// SHEXP_OVERLAP does the same with the shared-expert down rows. The view launcher had no
18799    /// mirror, so the lo half kept streaming 2 B/w while its twin ran at 1.0625, and because the
18800    /// halves execute CONCURRENTLY on the two cards the critical path is the SLOW half.
18801    /// NUMERIC CLASS: identical to the rest of `MEMRA_STEP_TP_W8`, so it carries that argmax
18802    /// acceptance and that maxdiff class, not a new one. Default OFF until measured.
18803    fn matvec_bf16_view_via_q8_mirror(
18804        &self,
18805        data: &cudarc::driver::CudaView<'_, u8>,
18806        x: &CudaSlice<f32>,
18807        y: &mut CudaSlice<f32>,
18808        in_f: usize,
18809        out_f: usize,
18810    ) -> Result<Option<()>, Box<dyn std::error::Error>> {
18811        use cudarc::driver::DevicePtr;
18812        let key = {
18813            let s = self.gpu.stream();
18814            let (p, _g) = data.device_ptr(&s);
18815            (p as u64, in_f as u32, out_f as u32)
18816        };
18817        {
18818            let mut mirrors = self
18819                .w8_mirrors
18820                .lock()
18821                .map_err(|_| "w8 mirror map is poisoned")?;
18822            if !mirrors.contains_key(&key) {
18823                let mut interleaved = self.alloc_u8_uninit(out_f * Self::q8_0_row_bytes(in_f))?;
18824                self.encode_q8_0_from_bf16_view(data, &mut interleaved, in_f, out_f)?;
18825                let planar = self.build_q8_rp4_raw(&interleaved, in_f, out_f)?;
18826                mirrors.insert(key, planar);
18827                // Unconditional, once per distinct shape: a door with no announce cannot be read
18828                // in BOTH directions, and this lane was already burned once by a sweep that
18829                // inferred "never engages" from a log line that did not exist in the tree.
18830                eprintln!("[w8-view] mirror built in_f={in_f} out_f={out_f}");
18831            }
18832        }
18833        let nblk = in_f / 32;
18834        {
18835            let mut act = self.w8_act.lock().map_err(|_| "w8 act map is poisoned")?;
18836            if !act.contains_key(&in_f) {
18837                let aq = self.alloc_uninit::<i8>(in_f)?;
18838                let ad = self.alloc_uninit::<f32>(nblk)?;
18839                act.insert(in_f, (aq, ad));
18840            }
18841            let (aq, ad) = act.get_mut(&in_f).expect("just inserted");
18842            self.quantize_q8_1_into(x, 1, in_f, aq, ad)?;
18843        }
18844        let mirrors = self
18845            .w8_mirrors
18846            .lock()
18847            .map_err(|_| "w8 mirror map is poisoned")?;
18848        let act = self.w8_act.lock().map_err(|_| "w8 act map is poisoned")?;
18849        let mirror = mirrors.get(&key).expect("built above");
18850        let (aq, ad) = act.get(&in_f).expect("built above");
18851        self.qmatvec_mmvq_into(
18852            mirror,
18853            aq,
18854            ad,
18855            1,
18856            in_f,
18857            out_f,
18858            QT_Q8_0,
18859            Self::q8_0_row_bytes(in_f),
18860            1.0,
18861            true,
18862            y,
18863        )?;
18864        Ok(Some(()))
18865    }
18866
18867    pub fn matvec_bf16_into(
18868        &self,
18869        data: &CudaSlice<u8>,
18870        x: &CudaSlice<f32>,
18871        y: &mut CudaSlice<f32>,
18872        in_f: usize,
18873        out_f: usize,
18874    ) -> Result<(), Box<dyn std::error::Error>> {
18875        if data.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 || y.len() < out_f {
18876            return Err(format!(
18877                "matvec_bf16_into geometry bytes={} x={} y={} in={in_f} out={out_f}",
18878                data.len(),
18879                x.len(),
18880                y.len()
18881            )
18882            .into());
18883        }
18884        // MEMRA_STEP_TP_W8, hybrid half: route this GEMV through a q8_0 mirror of the same
18885        // weight. Covers exactly the bf16 GEMVs that are NOT in a TP resident bank — the LM
18886        // head (324.4 -> 163.7 us measured), the shared-expert down rows (13.0 -> 5.6 us) and
18887        // the dense-FFN layers. Same numeric class as the QKV/o_proj arms (int8 dp4a with
18888        // per-32 scales), so it rides the same argmax acceptance; the bf16 slab stays resident
18889        // for prefill. The mirror builds on first use and is keyed by the slab's pointer.
18890        if step_tp_w8_on() && w8_hybrid_on() && in_f % 32 == 0 && out_f >= 64 {
18891            if let Some(()) = self.matvec_bf16_via_q8_mirror(data, x, y, in_f, out_f)? {
18892                return Ok(());
18893            }
18894        }
18895        // MEMRA_DOWN_X4=1 (short-row shapes, in_f<=2048): four sequential rows per
18896        // block, exact f32acc per-row program — cures the 1-iteration latency
18897        // starvation (shexp down measured 420GB/s at in_f=1280).
18898        static X4: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18899        let x4 = *X4.get_or_init(|| std::env::var("MEMRA_DOWN_X4").as_deref() == Ok("1"))
18900            && in_f <= 2048;
18901        if x4 {
18902            let f = self.func("matvec_bf16_f32acc_x4");
18903            let cfg = LaunchConfig {
18904                grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
18905                block_dim: (mmv_block(), 1, 1),
18906                shared_mem_bytes: 0,
18907            };
18908            let (ini, outi) = (in_f as i32, out_f as i32);
18909            let __s_b = self.gpu.stream();
18910            let mut b = __s_b.launch_builder(&f);
18911            b.arg(data).arg(x).arg(y).arg(&ini).arg(&outi);
18912            unsafe {
18913                b.launch(cfg)?;
18914            }
18915            return Ok(());
18916        }
18917        let f = self.func("matvec_bf16_f32acc");
18918        let cfg = LaunchConfig {
18919            grid_dim: (out_f as u32, 1, 1),
18920            block_dim: (mmv_block(), 1, 1),
18921            shared_mem_bytes: 0,
18922        };
18923        let ini = in_f as i32;
18924        let __s_b = self.gpu.stream();
18925        let mut b = __s_b.launch_builder(&f);
18926        b.arg(data).arg(x).arg(y).arg(&ini);
18927        unsafe {
18928            b.launch(cfg)?;
18929        }
18930        Ok(())
18931    }
18932
18933    /// `matvec_bf16_into` over a WEIGHT VIEW (row-range slice of a bf16 tensor): the head-split
18934    /// door feeds each device its half of the lm-head rows. Same kernel, same per-row program.
18935    pub fn matvec_bf16_view_into(
18936        &self,
18937        data: &cudarc::driver::CudaView<'_, u8>,
18938        x: &CudaSlice<f32>,
18939        y: &mut CudaSlice<f32>,
18940        in_f: usize,
18941        out_f: usize,
18942    ) -> Result<(), Box<dyn std::error::Error>> {
18943        if data.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 || y.len() < out_f {
18944            return Err(format!(
18945                "matvec_bf16_view_into geometry bytes={} x={} y={} in={in_f} out={out_f}",
18946                data.len(),
18947                x.len(),
18948                y.len()
18949            )
18950            .into());
18951        }
18952        if w8_view_on() && step_tp_w8_on() && w8_hybrid_on() && in_f % 32 == 0 && out_f >= 64 {
18953            if let Some(()) = self.matvec_bf16_view_via_q8_mirror(data, x, y, in_f, out_f)? {
18954                return Ok(());
18955            }
18956        }
18957        let f = self.func("matvec_bf16_f32acc");
18958        let cfg = LaunchConfig {
18959            grid_dim: (out_f as u32, 1, 1),
18960            block_dim: (mmv_block(), 1, 1),
18961            shared_mem_bytes: 0,
18962        };
18963        let ini = in_f as i32;
18964        let __s_b = self.gpu.stream();
18965        let mut b = __s_b.launch_builder(&f);
18966        b.arg(data).arg(x).arg(y).arg(&ini);
18967        unsafe {
18968            b.launch(cfg)?;
18969        }
18970        Ok(())
18971    }
18972
18973    /// `matvec_bf16_into` with a RAW u64 output pointer (UVA — the dev1-shexp down row
18974    /// lands root-resident over P2P). Same kernel, same per-row program: bit-identical.
18975    pub fn matvec_bf16_raw_out(
18976        &self,
18977        w: &CudaSlice<u8>,
18978        x: &CudaSlice<f32>,
18979        y_raw: u64,
18980        in_f: usize,
18981        out_f: usize,
18982    ) -> Result<(), Box<dyn std::error::Error>> {
18983        if w.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 || y_raw == 0 {
18984            return Err("matvec_bf16_raw_out geometry".into());
18985        }
18986        let f = self.func("matvec_bf16_f32acc");
18987        let cfg = LaunchConfig {
18988            grid_dim: (out_f as u32, 1, 1),
18989            block_dim: (mmv_block(), 1, 1),
18990            shared_mem_bytes: 0,
18991        };
18992        let ini = in_f as i32;
18993        let __s_b = self.gpu.stream();
18994        let mut b = __s_b.launch_builder(&f);
18995        b.arg(w).arg(x).arg(&y_raw).arg(&ini);
18996        unsafe {
18997            b.launch(cfg)?;
18998        }
18999        Ok(())
19000    }
19001
19002    /// MOE TAIL FUSION M1: dst = (a + b) + sh*scale[0] in one launch (sh/scale as RAW
19003    /// UVA pointers so the caller passes persistent-static rows without holding locks).
19004    /// Exact per-element sequence of the split add + add_scaled_rows pair.
19005    pub fn add3_raw(
19006        &self,
19007        a: &CudaSlice<f32>,
19008        b: &CudaSlice<f32>,
19009        sh_raw: u64,
19010        scale_raw: u64,
19011        dst: &mut CudaSlice<f32>,
19012        n: usize,
19013    ) -> Result<(), Box<dyn std::error::Error>> {
19014        if a.len() < n || b.len() < n || dst.len() < n || sh_raw == 0 || scale_raw == 0 {
19015            return Err("add3_raw geometry".into());
19016        }
19017        let f = self.func("add3_f32");
19018        let cfg = LaunchConfig {
19019            grid_dim: ((n as u32).div_ceil(256), 1, 1),
19020            block_dim: (256, 1, 1),
19021            shared_mem_bytes: 0,
19022        };
19023        let ni = n as i32;
19024        let __s_b = self.gpu.stream();
19025        let mut bld = __s_b.launch_builder(&f);
19026        bld.arg(a)
19027            .arg(b)
19028            .arg(&sh_raw)
19029            .arg(&scale_raw)
19030            .arg(dst)
19031            .arg(&ni);
19032        unsafe {
19033            bld.launch(cfg)?;
19034        }
19035        Ok(())
19036    }
19037
19038    /// FUSION #2e: shexp down matvec + scaled accumulate (dst[r] += dot_r * scale[0]),
19039    /// one launch replacing matvec_bf16_into + the ownership copy + add_scaled_rows.
19040    pub fn matvec_bf16_down_addscale_into(
19041        &self,
19042        w: &CudaSlice<u8>,
19043        x: &CudaSlice<f32>,
19044        scale: &CudaSlice<f32>,
19045        dst: &mut CudaSlice<f32>,
19046        in_f: usize,
19047        out_f: usize,
19048    ) -> Result<(), Box<dyn std::error::Error>> {
19049        if w.len() != in_f * out_f * 2
19050            || x.len() < in_f
19051            || in_f % 8 != 0
19052            || dst.len() < out_f
19053            || scale.is_empty()
19054        {
19055            return Err("matvec_bf16_down_addscale geometry".into());
19056        }
19057        let f = self.func("matvec_bf16_down_addscale");
19058        let cfg = LaunchConfig {
19059            grid_dim: (out_f as u32, 1, 1),
19060            block_dim: (mmv_block(), 1, 1),
19061            shared_mem_bytes: 0,
19062        };
19063        let ini = in_f as i32;
19064        let __s_b = self.gpu.stream();
19065        let mut b = __s_b.launch_builder(&f);
19066        b.arg(w).arg(x).arg(scale).arg(dst).arg(&ini);
19067        unsafe {
19068            b.launch(cfg)?;
19069        }
19070        Ok(())
19071    }
19072
19073    /// FUSION #2b: shexp dual matvec + SwiGLU act, one launch (bit-identical to
19074    /// matvec_bf16_dual_into + ffn_act_lim at gs=us=1; limit=None takes plain silu).
19075    /// T-ROW twin of `matvec_bf16_dual_silu_into` (per-row program identical).
19076    #[allow(clippy::too_many_arguments)]
19077    pub fn matvec_bf16_dual_silu_rows_into(
19078        &self,
19079        wg: &CudaSlice<u8>,
19080        wu: &CudaSlice<u8>,
19081        x: &CudaSlice<f32>,
19082        act: &mut CudaSlice<f32>,
19083        in_f: usize,
19084        out_f: usize,
19085        limit: Option<f32>,
19086        t: usize,
19087    ) -> Result<(), Box<dyn std::error::Error>> {
19088        if x.len() < t * in_f || act.len() < t * out_f || t == 0 || t > 32 {
19089            return Err("matvec_bf16_dual_silu_rows geometry".into());
19090        }
19091        let f = self.func("matvec_bf16_dual_silu_rows");
19092        let cfg = LaunchConfig {
19093            grid_dim: (out_f as u32, t as u32, 1),
19094            block_dim: (mmv_block(), 1, 1),
19095            shared_mem_bytes: 0,
19096        };
19097        let (ini, outi) = (in_f as i32, out_f as i32);
19098        let lim = limit.unwrap_or(0.0);
19099        let __s_b = self.gpu.stream();
19100        let mut b = __s_b.launch_builder(&f);
19101        b.arg(wg)
19102            .arg(wu)
19103            .arg(x)
19104            .arg(&mut *act)
19105            .arg(&ini)
19106            .arg(&outi)
19107            .arg(&lim);
19108        unsafe {
19109            b.launch(cfg)?;
19110        }
19111        Ok(())
19112    }
19113
19114    /// T-ROW twin of the bf16 f32acc-x4 matvec (per-row program identical).
19115    pub fn matvec_bf16_rows_into(
19116        &self,
19117        w: &CudaSlice<u8>,
19118        x: &CudaSlice<f32>,
19119        y: &mut CudaSlice<f32>,
19120        in_f: usize,
19121        out_f: usize,
19122        t: usize,
19123    ) -> Result<(), Box<dyn std::error::Error>> {
19124        if x.len() < t * in_f || y.len() < t * out_f || t == 0 || t > 32 || in_f % 8 != 0 {
19125            return Err("matvec_bf16_rows geometry".into());
19126        }
19127        // MEMRA_STEP_TP_W8 + MEMRA_W8_HYBRID, t > 1: the VERIFY walk's shexp/dense rows land
19128        // here too (`matvec_bf16_f32acc_x4_rows` was 78 launches/round at 56.5 us in a spec
19129        // capture, ~162 ms of GPU over 37 rounds), and the t==1 gate below skipped them. The
19130        // t-column q8 kernel is bit-identical to t single-row calls.
19131        if t >= 2 && t <= 32 && step_tp_w8_on() && w8_hybrid_on() && in_f % 32 == 0 && out_f >= 64 {
19132            if let Some(()) = self.matvec_bf16_via_q8_mirror_t(w, x, y, in_f, out_f, t)? {
19133                return Ok(());
19134            }
19135        }
19136        // MEMRA_STEP_TP_W8: the LM head reaches the device HERE, not through
19137        // matvec_bf16_into — the W8 trace showed the hybrid half building mirrors only for
19138        // in_f=1280 out_f=4096 (the shared-expert down rows, which SHEXP_OVERLAP already
19139        // hides, hence its +0.1%). Route the t=1 decode row through the q8 mirror; wider t
19140        // (the verify walk) keeps bf16 so the prefill class is untouched.
19141        if t == 1 && step_tp_w8_on() && w8_hybrid_on() && in_f % 32 == 0 && out_f >= 64 {
19142            if let Some(()) = self.matvec_bf16_via_q8_mirror(w, x, y, in_f, out_f)? {
19143                return Ok(());
19144            }
19145        }
19146        let f = self.func("matvec_bf16_f32acc_x4_rows");
19147        let cfg = LaunchConfig {
19148            grid_dim: (out_f.div_ceil(4) as u32, t as u32, 1),
19149            block_dim: (mmv_block(), 1, 1),
19150            shared_mem_bytes: 0,
19151        };
19152        let (ini, outi) = (in_f as i32, out_f as i32);
19153        let __s_b = self.gpu.stream();
19154        let mut b = __s_b.launch_builder(&f);
19155        b.arg(w).arg(x).arg(&mut *y).arg(&ini).arg(&outi);
19156        unsafe {
19157            b.launch(cfg)?;
19158        }
19159        Ok(())
19160    }
19161
19162    pub fn matvec_bf16_dual_silu_into(
19163        &self,
19164        wg: &CudaSlice<u8>,
19165        wu: &CudaSlice<u8>,
19166        x: &CudaSlice<f32>,
19167        act: &mut CudaSlice<f32>,
19168        in_f: usize,
19169        out_f: usize,
19170        limit: Option<f32>,
19171    ) -> Result<(), Box<dyn std::error::Error>> {
19172        if wg.len() != in_f * out_f * 2
19173            || wu.len() != in_f * out_f * 2
19174            || x.len() < in_f
19175            || in_f % 8 != 0
19176            || act.len() < out_f
19177        {
19178            return Err("matvec_bf16_dual_silu geometry".into());
19179        }
19180        let f = self.func("matvec_bf16_dual_silu");
19181        let cfg = LaunchConfig {
19182            grid_dim: (out_f as u32, 1, 1),
19183            block_dim: (mmv_block(), 1, 1),
19184            shared_mem_bytes: 0,
19185        };
19186        let (ini, outi) = (in_f as i32, out_f as i32);
19187        let lim = limit.unwrap_or(0.0);
19188        let __s_b = self.gpu.stream();
19189        let mut b = __s_b.launch_builder(&f);
19190        b.arg(wg)
19191            .arg(wu)
19192            .arg(x)
19193            .arg(act)
19194            .arg(&ini)
19195            .arg(&outi)
19196            .arg(&lim);
19197        unsafe {
19198            b.launch(cfg)?;
19199        }
19200        Ok(())
19201    }
19202
19203    /// `matvec_bf16_dual_into` over WEIGHT VIEWS (row-range slices): the shexp row-split
19204    /// door feeds each device its half of the gate/up rows. Same kernel, same per-row program.
19205    #[allow(clippy::too_many_arguments)]
19206    pub fn matvec_bf16_dual_view_into(
19207        &self,
19208        wg: &cudarc::driver::CudaView<'_, u8>,
19209        wu: &cudarc::driver::CudaView<'_, u8>,
19210        x: &CudaSlice<f32>,
19211        yg: &mut CudaSlice<f32>,
19212        yu: &mut CudaSlice<f32>,
19213        in_f: usize,
19214        out_f: usize,
19215    ) -> Result<(), Box<dyn std::error::Error>> {
19216        if wg.len() != in_f * out_f * 2
19217            || wu.len() != in_f * out_f * 2
19218            || x.len() < in_f
19219            || in_f % 8 != 0
19220            || yg.len() < out_f
19221            || yu.len() < out_f
19222        {
19223            return Err(format!(
19224                "matvec_bf16_dual_view_into geometry wg={} wu={} x={} in={in_f} out={out_f}",
19225                wg.len(),
19226                wu.len(),
19227                x.len()
19228            )
19229            .into());
19230        }
19231        let f = self.func("matvec_bf16_dual");
19232        let cfg = LaunchConfig {
19233            grid_dim: ((2 * out_f) as u32, 1, 1),
19234            block_dim: (mmv_block(), 1, 1),
19235            shared_mem_bytes: 0,
19236        };
19237        let (ini, outi) = (in_f as i32, out_f as i32);
19238        let __s_b = self.gpu.stream();
19239        let mut b = __s_b.launch_builder(&f);
19240        b.arg(wg)
19241            .arg(wu)
19242            .arg(x)
19243            .arg(yg)
19244            .arg(yu)
19245            .arg(&ini)
19246            .arg(&outi);
19247        unsafe {
19248            b.launch(cfg)?;
19249        }
19250        Ok(())
19251    }
19252
19253    /// `matvec_bf16_dual` writing into caller-owned outputs (persistent-workspace form).
19254    #[allow(clippy::too_many_arguments)]
19255    pub fn matvec_bf16_dual_into(
19256        &self,
19257        wg: &CudaSlice<u8>,
19258        wu: &CudaSlice<u8>,
19259        x: &CudaSlice<f32>,
19260        yg: &mut CudaSlice<f32>,
19261        yu: &mut CudaSlice<f32>,
19262        in_f: usize,
19263        out_f: usize,
19264    ) -> Result<(), Box<dyn std::error::Error>> {
19265        if wg.len() != in_f * out_f * 2
19266            || wu.len() != in_f * out_f * 2
19267            || x.len() < in_f
19268            || in_f % 8 != 0
19269            || yg.len() < out_f
19270            || yu.len() < out_f
19271        {
19272            return Err(format!(
19273                "matvec_bf16_dual_into geometry wg={} wu={} x={} in={in_f} out={out_f}",
19274                wg.len(),
19275                wu.len(),
19276                x.len()
19277            )
19278            .into());
19279        }
19280        let f = self.func("matvec_bf16_dual");
19281        let cfg = LaunchConfig {
19282            grid_dim: ((2 * out_f) as u32, 1, 1),
19283            block_dim: (mmv_block(), 1, 1),
19284            shared_mem_bytes: 0,
19285        };
19286        let (ini, outi) = (in_f as i32, out_f as i32);
19287        let __s_b = self.gpu.stream();
19288        let mut b = __s_b.launch_builder(&f);
19289        b.arg(wg)
19290            .arg(wu)
19291            .arg(x)
19292            .arg(yg)
19293            .arg(yu)
19294            .arg(&ini)
19295            .arg(&outi);
19296        unsafe {
19297            b.launch(cfg)?;
19298        }
19299        Ok(())
19300    }
19301
19302    /// Dual bf16 matvec: gate/up (same shape) from one shared input in one launch. Per row
19303    /// bit-identical to two `matvec_bf16` launches. Returns (gate, up).
19304    pub(crate) fn matvec_bf16_dual(
19305        &self,
19306        wg: &CudaSlice<u8>,
19307        wu: &CudaSlice<u8>,
19308        x: &CudaSlice<f32>,
19309        in_f: usize,
19310        out_f: usize,
19311    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
19312        if wg.len() != in_f * out_f * 2
19313            || wu.len() != in_f * out_f * 2
19314            || x.len() < in_f
19315            || in_f % 8 != 0
19316        {
19317            return Err(format!(
19318                "matvec_bf16_dual geometry wg={} wu={} x={} in={in_f} out={out_f}",
19319                wg.len(),
19320                wu.len(),
19321                x.len()
19322            )
19323            .into());
19324        }
19325        let mut yg = self.alloc_uninit::<f32>(out_f)?;
19326        let mut yu = self.alloc_uninit::<f32>(out_f)?;
19327        let f = self.func("matvec_bf16_dual");
19328        let cfg = LaunchConfig {
19329            grid_dim: ((2 * out_f) as u32, 1, 1),
19330            block_dim: (mmv_block(), 1, 1),
19331            shared_mem_bytes: 0,
19332        };
19333        let (ini, outi) = (in_f as i32, out_f as i32);
19334        let __s_b = self.gpu.stream();
19335        let mut b = __s_b.launch_builder(&f);
19336        b.arg(wg)
19337            .arg(wu)
19338            .arg(x)
19339            .arg(&mut yg)
19340            .arg(&mut yu)
19341            .arg(&ini)
19342            .arg(&outi);
19343        unsafe {
19344            b.launch(cfg)?;
19345        }
19346        Ok((yg, yu))
19347    }
19348
19349    #[allow(clippy::too_many_arguments)]
19350    fn linear_bf16_chunked_inner(
19351        &self,
19352        x: &CudaSlice<f32>,
19353        data: &CudaSlice<u8>,
19354        m: usize,
19355        in_f: usize,
19356        out_f: usize,
19357        exact: bool,
19358        canonical_chunk_rows: Option<usize>,
19359    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19360        const CHUNK_BYTES: usize = 256 << 20;
19361        // canonical_chunk_rows callers are the step TP resident ranks whose cross-topology
19362        // equality program is defined in cuBLASLt chunk shapes — the door leaves them alone.
19363        if m == 1
19364            && !exact
19365            && canonical_chunk_rows.is_none()
19366            && in_f % 8 == 0
19367            && Self::bf16_mmv_on()
19368        {
19369            return self.matvec_bf16(data, x, in_f, out_f);
19370        }
19371        // MEMRA_PP_BF16: prefill on the RESIDENT bf16 bytes through cuBLASLt tensor cores.
19372        // Below this door the whole weight is dequanted to f32 and multiplied without tensor
19373        // cores — the step37 prime's 14x gap to vLLM. `exact` and canonical-chunk callers are
19374        // numerical programs with their own equality gates and are left alone.
19375        if m >= 16
19376            && !exact
19377            && canonical_chunk_rows.is_none()
19378            && data.len() == in_f * out_f * 2
19379            && crate::f16_ffi::pp_bf16_enabled()
19380        {
19381            // None = cuBLASLt declined this shape (it announced which one); fall through to the
19382            // f32 dequant GEMM below, which is always correct.
19383            if let Some(y) = self.bf16_tc_gemm(data, x, m, in_f, out_f)? {
19384                return Ok(y);
19385            }
19386        }
19387        let row_bytes = in_f
19388            .checked_mul(std::mem::size_of::<f32>())
19389            .ok_or("BF16 chunk row byte count overflow")?;
19390        if row_bytes == 0 || out_f == 0 {
19391            return Err("BF16 chunk dimensions must be nonzero".into());
19392        }
19393        let max_chunk_rows = (CHUNK_BYTES / row_bytes).max(1).min(out_f);
19394        let chunk_rows = match canonical_chunk_rows {
19395            Some(rows) if rows == 0 => {
19396                return Err("canonical BF16 chunk rows must be nonzero".into());
19397            }
19398            Some(rows) if rows > max_chunk_rows => {
19399                return Err(format!(
19400                    "canonical BF16 chunk rows {rows} exceed the {max_chunk_rows}-row scratch limit"
19401                )
19402                .into());
19403            }
19404            Some(rows) if out_f % rows != 0 => {
19405                return Err(format!(
19406                    "BF16 output width {out_f} is not divisible by canonical {rows}-row chunks"
19407                )
19408                .into());
19409            }
19410            Some(rows) => rows,
19411            None => max_chunk_rows,
19412        };
19413        if chunk_rows >= out_f {
19414            let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
19415            return if exact {
19416                self.linear_decode_exact(x, &wf32, m, in_f, out_f)
19417            } else {
19418                self.linear(x, &wf32, m, in_f, out_f)
19419            };
19420        }
19421        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
19422        let mut r0 = 0usize;
19423        while r0 < out_f {
19424            let rows = chunk_rows.min(out_f - r0);
19425            let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
19426            let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
19427            let yc = if exact {
19428                self.linear_decode_exact(x, &wf32, m, in_f, rows)?
19429            } else {
19430                self.linear(x, &wf32, m, in_f, rows)?
19431            };
19432            // scatter [m, rows] into y[m, out_f] at column offset r0 (m is tiny in decode/verify)
19433            for mi in 0..m {
19434                let src = yc.slice(mi * rows..(mi + 1) * rows);
19435                let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
19436                self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
19437            }
19438            r0 += rows;
19439        }
19440        Ok(y)
19441    }
19442
19443    /// Execute an already resident BF16 projection. This is the model-faithful substrate used by
19444    /// Step tensor-parallel correctness ranks; it preserves checkpoint bytes and the existing
19445    /// chunked BF16 numerical program instead of re-encoding the weight.
19446    pub fn linear_bf16_resident(
19447        &self,
19448        x: &CudaSlice<f32>,
19449        data: &CudaSlice<u8>,
19450        m: usize,
19451        in_f: usize,
19452        out_f: usize,
19453    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19454        if data.len() != in_f * out_f * 2 {
19455            return Err(format!("resident BF16 bytes {} != {out_f}x{in_f}x2", data.len()).into());
19456        }
19457        self.linear_bf16_chunked(x, data, m, in_f, out_f, false, None)
19458    }
19459
19460    /// Execute a resident BF16 projection as fixed-width output-row chunks.
19461    ///
19462    /// Tensor-parallel ranks use this to give TP1/TP2/TP4/TP8 the same cuBLASLt problem shape
19463    /// for every checkpoint row. Callers must derive `canonical_chunk_rows` from the registered
19464    /// model topology rather than the active rank count.
19465    pub fn linear_bf16_resident_canonical_rows(
19466        &self,
19467        x: &CudaSlice<f32>,
19468        data: &CudaSlice<u8>,
19469        m: usize,
19470        in_f: usize,
19471        out_f: usize,
19472        canonical_chunk_rows: usize,
19473    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19474        if data.len() != in_f * out_f * 2 {
19475            return Err(format!("resident BF16 bytes {} != {out_f}x{in_f}x2", data.len()).into());
19476        }
19477        self.linear_bf16_chunked(x, data, m, in_f, out_f, false, Some(canonical_chunk_rows))
19478    }
19479
19480    /// Execute a load-time F32 mirror with the same fixed output-row chunks as the BF16 path.
19481    ///
19482    /// Expanding the checkpoint bytes once changes residency, not arithmetic: every cuBLASLt
19483    /// call receives the same F32 values and problem shape as `linear_bf16_chunked`.
19484    pub fn linear_f32_resident_canonical_rows(
19485        &self,
19486        x: &CudaSlice<f32>,
19487        data: &CudaSlice<f32>,
19488        m: usize,
19489        in_f: usize,
19490        out_f: usize,
19491        canonical_chunk_rows: usize,
19492    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19493        self.linear_f32_resident_canonical_rows_inner(
19494            x,
19495            data,
19496            m,
19497            in_f,
19498            out_f,
19499            canonical_chunk_rows,
19500            false,
19501        )
19502    }
19503
19504    /// Execute fixed output-row chunks and assemble them with one strided placement per chunk.
19505    ///
19506    /// The projection shapes and values are identical to
19507    /// [`Self::linear_f32_resident_canonical_rows`]. Only the byte-preserving output layout step
19508    /// changes, replacing one device copy per token with one placement kernel per output chunk.
19509    pub fn linear_f32_resident_canonical_rows_strided(
19510        &self,
19511        x: &CudaSlice<f32>,
19512        data: &CudaSlice<f32>,
19513        m: usize,
19514        in_f: usize,
19515        out_f: usize,
19516        canonical_chunk_rows: usize,
19517    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19518        self.linear_f32_resident_canonical_rows_inner(
19519            x,
19520            data,
19521            m,
19522            in_f,
19523            out_f,
19524            canonical_chunk_rows,
19525            true,
19526        )
19527    }
19528
19529    fn linear_f32_resident_canonical_rows_inner(
19530        &self,
19531        x: &CudaSlice<f32>,
19532        data: &CudaSlice<f32>,
19533        m: usize,
19534        in_f: usize,
19535        out_f: usize,
19536        canonical_chunk_rows: usize,
19537        strided_output: bool,
19538    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19539        if data.len() != in_f * out_f {
19540            return Err(format!("resident F32 values {} != {out_f}x{in_f}", data.len()).into());
19541        }
19542        if canonical_chunk_rows == 0
19543            || canonical_chunk_rows > out_f
19544            || out_f % canonical_chunk_rows != 0
19545        {
19546            return Err(format!(
19547                "invalid canonical F32 chunk rows {canonical_chunk_rows} for output width {out_f}"
19548            )
19549            .into());
19550        }
19551        if canonical_chunk_rows == out_f {
19552            return self.linear(x, data, m, in_f, out_f);
19553        }
19554
19555        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
19556        let input = x.slice(0..x.len());
19557        for r0 in (0..out_f).step_by(canonical_chunk_rows) {
19558            let weights = data.slice(r0 * in_f..(r0 + canonical_chunk_rows) * in_f);
19559            if m == 1 {
19560                let mut destination = y.slice_mut(r0..r0 + canonical_chunk_rows);
19561                self.linear_device_into(
19562                    &input,
19563                    &weights,
19564                    &mut destination,
19565                    1,
19566                    in_f,
19567                    canonical_chunk_rows,
19568                )?;
19569                continue;
19570            }
19571            let chunk = self.linear_device(&input, &weights, m, in_f, canonical_chunk_rows)?;
19572            if strided_output {
19573                self.place_rows_strided(&chunk, &mut y, canonical_chunk_rows, m, out_f, r0)?;
19574            } else {
19575                for token in 0..m {
19576                    let source = chunk
19577                        .slice(token * canonical_chunk_rows..(token + 1) * canonical_chunk_rows);
19578                    let mut destination =
19579                        y.slice_mut(token * out_f + r0..token * out_f + r0 + canonical_chunk_rows);
19580                    self.gpu.stream().memcpy_dtod(&source, &mut destination)?;
19581                }
19582            }
19583        }
19584        Ok(y)
19585    }
19586
19587    /// One-token twin of `linear_f32_resident_canonical_rows` writing into a caller-owned
19588    /// output. Same cuBLASLt calls, values, and chunk order as the allocating variant at
19589    /// `m == 1`; only the output residency changes (persistent workspace instead of a fresh
19590    /// allocation per call). This is the projection substrate of the v2 Step TP decode driver.
19591    pub fn linear_f32_resident_canonical_rows_t1_into(
19592        &self,
19593        x: &CudaSlice<f32>,
19594        data: &CudaSlice<f32>,
19595        y: &mut CudaSlice<f32>,
19596        in_f: usize,
19597        out_f: usize,
19598        canonical_chunk_rows: usize,
19599    ) -> Result<(), Box<dyn std::error::Error>> {
19600        if data.len() != in_f * out_f {
19601            return Err(format!("resident F32 values {} != {out_f}x{in_f}", data.len()).into());
19602        }
19603        if y.len() != out_f || x.len() != in_f {
19604            return Err(format!(
19605                "resident F32 t1 shapes x={} y={} != in {in_f} out {out_f}",
19606                x.len(),
19607                y.len()
19608            )
19609            .into());
19610        }
19611        if canonical_chunk_rows == 0
19612            || canonical_chunk_rows > out_f
19613            || out_f % canonical_chunk_rows != 0
19614        {
19615            return Err(format!(
19616                "invalid canonical F32 chunk rows {canonical_chunk_rows} for output width {out_f}"
19617            )
19618            .into());
19619        }
19620        let input = x.slice(0..x.len());
19621        for r0 in (0..out_f).step_by(canonical_chunk_rows) {
19622            let weights = data.slice(r0 * in_f..(r0 + canonical_chunk_rows) * in_f);
19623            let mut destination = y.slice_mut(r0..r0 + canonical_chunk_rows);
19624            self.linear_device_into(
19625                &input,
19626                &weights,
19627                &mut destination,
19628                1,
19629                in_f,
19630                canonical_chunk_rows,
19631            )?;
19632        }
19633        Ok(())
19634    }
19635
19636    /// One-token view-to-view linear into a caller-owned destination — the `linear` twin
19637    /// without the allocation, for workspace-resident operands.
19638    pub fn linear_t1_into(
19639        &self,
19640        x: &cudarc::driver::CudaView<'_, f32>,
19641        w: &cudarc::driver::CudaView<'_, f32>,
19642        y: &mut cudarc::driver::CudaViewMut<'_, f32>,
19643        in_f: usize,
19644        out_f: usize,
19645    ) -> Result<(), Box<dyn std::error::Error>> {
19646        self.linear_device_into(x, w, y, 1, in_f, out_f)
19647    }
19648
19649    /// On-device linear: y[m,out] = x[m,in] @ W[out,in]^T, weights row-major [out,in] (ggml).
19650    /// cuBLASLt col-major mapping (see memra_runtime::Gpu::linear_f32 for the derivation).
19651    /// DECODE-EXACT float linear: per-column m=1 cuBLASLt calls. cuBLASLt's reduction split is
19652    /// n-dependent (lt_ndep probe: m=1 vs m=2 col0 differs every bit), so spec-verify batches
19653    /// must not batch float matmuls the T=1 decode chain runs at m=1. Used by the small-t MoE
19654    /// router/shexp sites and matmul_decode_exact's Float arm.
19655    pub fn linear_decode_exact(
19656        &self,
19657        x: &CudaSlice<f32>,
19658        w: &CudaSlice<f32>,
19659        m_tokens: usize,
19660        in_f: usize,
19661        out_f: usize,
19662    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19663        if m_tokens == 1 {
19664            return self.linear(x, w, 1, in_f, out_f);
19665        }
19666        let xv = self.view(x, m_tokens * in_f);
19667        let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
19668        for t in 0..m_tokens {
19669            let row = xv.slice(t * in_f..(t + 1) * in_f);
19670            let mut xr = self.alloc_uninit::<f32>(in_f)?;
19671            self.copy_view_into(&mut xr, 0, &row, in_f)?;
19672            let yr = self.linear(&xr, w, 1, in_f, out_f)?;
19673            self.copy_into(&mut y, t * out_f, &yr, out_f)?;
19674        }
19675        Ok(y)
19676    }
19677
19678    pub fn linear(
19679        &self,
19680        x: &CudaSlice<f32>,
19681        w: &CudaSlice<f32>,
19682        m_tokens: usize,
19683        in_f: usize,
19684        out_f: usize,
19685    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
19686        self.linear_device(x, w, m_tokens, in_f, out_f)
19687    }
19688
19689    fn linear_device<I>(
19690        &self,
19691        x: &I,
19692        w: &I,
19693        m_tokens: usize,
19694        in_f: usize,
19695        out_f: usize,
19696    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>>
19697    where
19698        I: cudarc::driver::DevicePtr<f32>,
19699    {
19700        let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; // cuBLASLt beta=0: C fully written
19701        self.linear_device_into(x, w, &mut c, m_tokens, in_f, out_f)?;
19702        Ok(c)
19703    }
19704
19705    fn linear_device_into<I, O>(
19706        &self,
19707        x: &I,
19708        w: &I,
19709        c: &mut O,
19710        m_tokens: usize,
19711        in_f: usize,
19712        out_f: usize,
19713    ) -> Result<(), Box<dyn std::error::Error>>
19714    where
19715        I: cudarc::driver::DevicePtr<f32>,
19716        O: cudarc::driver::DevicePtrMut<f32>,
19717    {
19718        use cudarc::cublaslt::{Matmul, MatmulConfig};
19719        let cfg = MatmulConfig {
19720            transa: true,
19721            transb: false,
19722            transc: false,
19723            m: out_f as u64,
19724            n: m_tokens as u64,
19725            k: in_f as u64,
19726            alpha: 1.0,
19727            lda: in_f as i64,
19728            ldb: in_f as i64,
19729            beta: 0.0,
19730            ldc: out_f as i64,
19731            stride_a: None,
19732            stride_b: None,
19733            stride_c: None,
19734            stride_bias: None,
19735            batch_size: None,
19736        };
19737        let blas = self.gpu.blas();
19738        unsafe {
19739            blas.matmul(cfg, w, x, c, None, None)?;
19740        }
19741        Ok(())
19742    }
19743
19744    /// Naive SDPA. Q:[head_dim,n_head,T], K/V:[head_dim,n_head_kv,T_kv] -> O:[head_dim,n_head,T].
19745    ///
19746    /// LONG-CTX DISPATCH (lane/hermes-perf-fixes, 2026-08-23): the smem kernel's `T_kv*4`
19747    /// dynamic shared memory exceeds the 48KB launch bound past T_kv=12288 — the plain
19748    /// full-attn sibling of the DFlash2 B2 crash the windowed layers fixed with
19749    /// `sdpa_naive_w_lo`. Past the bound this transparently takes the byte-identical
19750    /// gmem-scores twin (`sdpa_naive_gmem`, kernel_check-pinned) instead of returning the
19751    /// launch error mid-request.
19752    pub fn sdpa_naive(
19753        &self,
19754        q: &CudaSlice<f32>,
19755        k: &CudaSlice<f32>,
19756        v: &CudaSlice<f32>,
19757        o: &mut CudaSlice<f32>,
19758        head_dim: usize,
19759        n_head: usize,
19760        n_head_kv: usize,
19761        t: usize,
19762        t_kv: usize,
19763        scale: f32,
19764        causal: bool,
19765    ) -> Result<(), Box<dyn std::error::Error>> {
19766        if t_kv * 4 > SDPA_NAIVE_SMEM_MAX {
19767            return self.sdpa_naive_gmem(
19768                q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
19769            );
19770        }
19771        let f = self.func("sdpa_naive_f32");
19772        let cfg = LaunchConfig {
19773            grid_dim: (n_head as u32, t as u32, 1),
19774            block_dim: (128, 1, 1),
19775            shared_mem_bytes: (t_kv * 4) as u32,
19776        };
19777        let (hd, nh, nhkv, ti, tkvi, cz) = (
19778            head_dim as i32,
19779            n_head as i32,
19780            n_head_kv as i32,
19781            t as i32,
19782            t_kv as i32,
19783            causal as i32,
19784        );
19785        let __s_b = self.gpu.stream();
19786        let mut b = __s_b.launch_builder(&f);
19787        b.arg(q)
19788            .arg(k)
19789            .arg(v)
19790            .arg(o)
19791            .arg(&hd)
19792            .arg(&nh)
19793            .arg(&nhkv)
19794            .arg(&ti)
19795            .arg(&tkvi)
19796            .arg(&scale)
19797            .arg(&cz);
19798        unsafe {
19799            b.launch(cfg)?;
19800        }
19801        Ok(())
19802    }
19803
19804    /// Global-memory-scores twin of [`Self::sdpa_naive`] (lane/hermes-perf-fixes, 2026-08-23).
19805    /// Same kernel body with the per-(head, query) scores row in a device workspace instead
19806    /// of dynamic shared memory: identical loop structure and reduction order, so the output
19807    /// is BYTE-IDENTICAL to the smem kernel wherever both launch (kernel_check
19808    /// `sdpa_naive_gmem` pins bit-identity plus the >12k arm where the smem kernel MUST
19809    /// fail). O(n_head * T * T_kv * 4) workspace — fine for the tall-KV block shapes that
19810    /// hit the bound (dspark/dflash full-attn: T <= block size), guarded so a square
19811    /// T==T_kv caller cannot silently allocate tens of GB.
19812    #[allow(clippy::too_many_arguments)]
19813    pub fn sdpa_naive_gmem(
19814        &self,
19815        q: &CudaSlice<f32>,
19816        k: &CudaSlice<f32>,
19817        v: &CudaSlice<f32>,
19818        o: &mut CudaSlice<f32>,
19819        head_dim: usize,
19820        n_head: usize,
19821        n_head_kv: usize,
19822        t: usize,
19823        t_kv: usize,
19824        scale: f32,
19825        causal: bool,
19826    ) -> Result<(), Box<dyn std::error::Error>> {
19827        let ws_len = n_head
19828            .checked_mul(t)
19829            .and_then(|x| x.checked_mul(t_kv))
19830            .ok_or("sdpa_naive_gmem: scores workspace size overflow")?;
19831        let ws_bytes = ws_len
19832            .checked_mul(std::mem::size_of::<f32>())
19833            .ok_or("sdpa_naive_gmem: scores workspace byte count overflow")?;
19834        if ws_bytes > SDPA_NAIVE_GMEM_WS_MAX {
19835            return Err(format!(
19836                "sdpa_naive_gmem: scores workspace {ws_bytes} bytes (heads {n_head} x T {t} x \
19837                 T_kv {t_kv}) exceeds the {SDPA_NAIVE_GMEM_WS_MAX}-byte guard — this shape \
19838                 needs a tiled/flash kernel, not the naive oracle"
19839            )
19840            .into());
19841        }
19842        let mut scores = self.uninit(ws_len)?;
19843        let f = self.func("sdpa_naive_gmem_f32");
19844        let cfg = LaunchConfig {
19845            grid_dim: (n_head as u32, t as u32, 1),
19846            block_dim: (128, 1, 1),
19847            shared_mem_bytes: 0,
19848        };
19849        let (hd, nh, nhkv, ti, tkvi, cz) = (
19850            head_dim as i32,
19851            n_head as i32,
19852            n_head_kv as i32,
19853            t as i32,
19854            t_kv as i32,
19855            causal as i32,
19856        );
19857        let __s_b = self.gpu.stream();
19858        let mut b = __s_b.launch_builder(&f);
19859        b.arg(q)
19860            .arg(k)
19861            .arg(v)
19862            .arg(o)
19863            .arg(&mut scores)
19864            .arg(&hd)
19865            .arg(&nh)
19866            .arg(&nhkv)
19867            .arg(&ti)
19868            .arg(&tkvi)
19869            .arg(&scale)
19870            .arg(&cz);
19871        unsafe {
19872            b.launch(cfg)?;
19873        }
19874        Ok(())
19875    }
19876
19877    /// Island twin (lane/gemma-vision): causal + sliding-window attention with
19878    /// bidirectional image islands. `span_id` labels each absolute kv position
19879    /// (-1 text, >=0 island id); same-island keys are visible unconditionally,
19880    /// reproducing the reference's non-causal image batch. window 0 = no window.
19881    #[allow(clippy::too_many_arguments)]
19882    pub fn sdpa_naive_island(
19883        &self,
19884        q: &CudaSlice<f32>,
19885        k: &CudaSlice<f32>,
19886        v: &CudaSlice<f32>,
19887        o: &mut CudaSlice<f32>,
19888        span_id: &CudaSlice<i32>,
19889        head_dim: usize,
19890        n_head: usize,
19891        n_head_kv: usize,
19892        t: usize,
19893        t_kv: usize,
19894        scale: f32,
19895        window: usize,
19896    ) -> Result<(), Box<dyn std::error::Error>> {
19897        let f = self.func("sdpa_naive_island_f32");
19898        let cfg = LaunchConfig {
19899            grid_dim: (n_head as u32, t as u32, 1),
19900            block_dim: (128, 1, 1),
19901            shared_mem_bytes: (t_kv * 4) as u32,
19902        };
19903        let (hd, nh, nhkv, ti, tkvi, wi) = (
19904            head_dim as i32,
19905            n_head as i32,
19906            n_head_kv as i32,
19907            t as i32,
19908            t_kv as i32,
19909            window as i32,
19910        );
19911        let __s_b = self.gpu.stream();
19912        let mut b = __s_b.launch_builder(&f);
19913        b.arg(q)
19914            .arg(k)
19915            .arg(v)
19916            .arg(o)
19917            .arg(span_id)
19918            .arg(&hd)
19919            .arg(&nh)
19920            .arg(&nhkv)
19921            .arg(&ti)
19922            .arg(&tkvi)
19923            .arg(&scale)
19924            .arg(&wi);
19925        unsafe {
19926            b.launch(cfg)?;
19927        }
19928        Ok(())
19929    }
19930
19931    /// Windowed sdpa_naive twin (gemma4 R6): masks keys older than q_pos-(window-1).
19932    #[allow(clippy::too_many_arguments)]
19933    pub fn sdpa_naive_w(
19934        &self,
19935        q: &CudaSlice<f32>,
19936        k: &CudaSlice<f32>,
19937        v: &CudaSlice<f32>,
19938        o: &mut CudaSlice<f32>,
19939        head_dim: usize,
19940        n_head: usize,
19941        n_head_kv: usize,
19942        t: usize,
19943        t_kv: usize,
19944        scale: f32,
19945        causal: bool,
19946        window: usize,
19947    ) -> Result<(), Box<dyn std::error::Error>> {
19948        let f = self.func("sdpa_naive_w_f32");
19949        let cfg = LaunchConfig {
19950            grid_dim: (n_head as u32, t as u32, 1),
19951            block_dim: (128, 1, 1),
19952            shared_mem_bytes: (t_kv * 4) as u32,
19953        };
19954        let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19955            head_dim as i32,
19956            n_head as i32,
19957            n_head_kv as i32,
19958            t as i32,
19959            t_kv as i32,
19960            causal as i32,
19961            window as i32,
19962        );
19963        let __s_b = self.gpu.stream();
19964        let mut b = __s_b.launch_builder(&f);
19965        b.arg(q)
19966            .arg(k)
19967            .arg(v)
19968            .arg(o)
19969            .arg(&hd)
19970            .arg(&nh)
19971            .arg(&nhkv)
19972            .arg(&ti)
19973            .arg(&tkvi)
19974            .arg(&scale)
19975            .arg(&cz)
19976            .arg(&wi);
19977        unsafe {
19978            b.launch(cfg)?;
19979        }
19980        Ok(())
19981    }
19982
19983    /// Lo-clipped windowed sdpa_naive twin (lane/dflash2-longctx, DFLASH2-EVAL §10.6(c)).
19984    /// Same mask law as `sdpa_naive_w`, but keys below every query's window floor are never
19985    /// read: kv_lo = max(0, (t_kv - t) + 1 - window) — the oldest key visible to the OLDEST
19986    /// query row (q_pos = t_kv - t). Dynamic shared memory shrinks from t_kv*4 bytes (which
19987    /// blows the 48KB launch bound at ~12k rows — the B2 ctx crash) to (t_kv - kv_lo)*4 =
19988    /// (window - 1 + t)*4, and the key scan drops from O(t_kv) to O(window + t). Output is
19989    /// byte-identical to `sdpa_naive_w` (masked keys contribute exact zeros to same-order
19990    /// reductions; kernel_check `sdpa_naive_w_lo` pins it). window == 0 (no window) keeps
19991    /// kv_lo = 0 and is then shape-identical to the legacy kernel, including its bound.
19992    #[allow(clippy::too_many_arguments)]
19993    pub fn sdpa_naive_w_lo(
19994        &self,
19995        q: &CudaSlice<f32>,
19996        k: &CudaSlice<f32>,
19997        v: &CudaSlice<f32>,
19998        o: &mut CudaSlice<f32>,
19999        head_dim: usize,
20000        n_head: usize,
20001        n_head_kv: usize,
20002        t: usize,
20003        t_kv: usize,
20004        scale: f32,
20005        causal: bool,
20006        window: usize,
20007    ) -> Result<(), Box<dyn std::error::Error>> {
20008        let kv_lo = if window > 0 {
20009            (t_kv - t + 1).saturating_sub(window)
20010        } else {
20011            0
20012        };
20013        let smem = (t_kv - kv_lo) * 4;
20014        if smem > 48 * 1024 {
20015            return Err(format!(
20016                "sdpa_naive_w_lo: window {window} + T {t} rows need {smem} bytes of dynamic \
20017                 shared memory (> 48KB launch bound) — this kernel clips the OLD side only; \
20018                 a window this wide needs the multi-pass long-ctx kernel"
20019            )
20020            .into());
20021        }
20022        let f = self.func("sdpa_naive_w_lo_f32");
20023        let cfg = LaunchConfig {
20024            grid_dim: (n_head as u32, t as u32, 1),
20025            block_dim: (128, 1, 1),
20026            shared_mem_bytes: smem as u32,
20027        };
20028        let (hd, nh, nhkv, ti, tkvi, cz, wi, lo) = (
20029            head_dim as i32,
20030            n_head as i32,
20031            n_head_kv as i32,
20032            t as i32,
20033            t_kv as i32,
20034            causal as i32,
20035            window as i32,
20036            kv_lo as i32,
20037        );
20038        let __s_b = self.gpu.stream();
20039        let mut b = __s_b.launch_builder(&f);
20040        b.arg(q)
20041            .arg(k)
20042            .arg(v)
20043            .arg(o)
20044            .arg(&hd)
20045            .arg(&nh)
20046            .arg(&nhkv)
20047            .arg(&ti)
20048            .arg(&tkvi)
20049            .arg(&scale)
20050            .arg(&cz)
20051            .arg(&wi)
20052            .arg(&lo);
20053        unsafe {
20054            b.launch(cfg)?;
20055        }
20056        Ok(())
20057    }
20058
20059    /// SDPA where K/V are CudaViews into a resident KV cache (decode hot path, no host round-trip).
20060    pub fn sdpa_naive_view(
20061        &self,
20062        q: &CudaSlice<f32>,
20063        k: &cudarc::driver::CudaView<f32>,
20064        v: &cudarc::driver::CudaView<f32>,
20065        o: &mut CudaSlice<f32>,
20066        head_dim: usize,
20067        n_head: usize,
20068        n_head_kv: usize,
20069        t: usize,
20070        t_kv: usize,
20071        scale: f32,
20072        causal: bool,
20073    ) -> Result<(), Box<dyn std::error::Error>> {
20074        let f = self.func("sdpa_naive_f32");
20075        let cfg = LaunchConfig {
20076            grid_dim: (n_head as u32, t as u32, 1),
20077            block_dim: (128, 1, 1),
20078            shared_mem_bytes: (t_kv * 4) as u32,
20079        };
20080        let (hd, nh, nhkv, ti, tkvi, cz) = (
20081            head_dim as i32,
20082            n_head as i32,
20083            n_head_kv as i32,
20084            t as i32,
20085            t_kv as i32,
20086            causal as i32,
20087        );
20088        let __s_b = self.gpu.stream();
20089        let mut b = __s_b.launch_builder(&f);
20090        b.arg(q)
20091            .arg(k)
20092            .arg(v)
20093            .arg(o)
20094            .arg(&hd)
20095            .arg(&nh)
20096            .arg(&nhkv)
20097            .arg(&ti)
20098            .arg(&tkvi)
20099            .arg(&scale)
20100            .arg(&cz);
20101        unsafe {
20102            b.launch(cfg)?;
20103        }
20104        Ok(())
20105    }
20106
20107    /// Correctness fallback for quantized resident K/V views. Dequantizes K and V once into f32
20108    /// workspaces, then calls `sdpa_naive`. This is an explicit API: the optimized prefill view
20109    /// dispatch remains unchanged, so callers can use it as a reference or compatibility path.
20110    /// Dequant a quantized KV view into caller-owned f32 buffers (one grid-stride launch).
20111    /// `g` picks the kf8vf8-module stamp for e4m3 caches (same flag contract as fa_decode/
20112    /// fa_prefill_view). Used by the E4B shared-KV prefill arms (2026-07-31) to feed the
20113    /// f32 fa_prefill_w / fa_prefill_hd512 twins from the target layer's quantized rows.
20114    #[allow(clippy::too_many_arguments)]
20115    pub fn fa_dequant_kv_view_f32(
20116        &self,
20117        k: &cudarc::driver::CudaView<u8>,
20118        v: &cudarc::driver::CudaView<u8>,
20119        kf: &mut CudaSlice<f32>,
20120        vf: &mut CudaSlice<f32>,
20121        kv_dim_k: usize,
20122        kv_dim_v: usize,
20123        t_kv: usize,
20124        k_tok_bytes: usize,
20125        v_tok_bytes: usize,
20126        g: bool,
20127    ) -> Result<(), Box<dyn std::error::Error>> {
20128        let f = if g {
20129            self.func_g("fa_dequant_kv_ws_f32")
20130        } else {
20131            self.func("fa_dequant_kv_ws_f32")
20132        };
20133        let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
20134        let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
20135        let cfg = LaunchConfig {
20136            grid_dim: (nblk.max(1), 1, 1),
20137            block_dim: (256, 1, 1),
20138            shared_mem_bytes: 0,
20139        };
20140        let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
20141        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
20142        let __s_b = self.gpu.stream();
20143        let mut b = __s_b.launch_builder(&f);
20144        b.arg(k)
20145            .arg(v)
20146            .arg(&mut *kf)
20147            .arg(&mut *vf)
20148            .arg(&kdk)
20149            .arg(&kdv)
20150            .arg(&tkvi)
20151            .arg(&ktb)
20152            .arg(&vtb);
20153        unsafe {
20154            b.launch(cfg)?;
20155        }
20156        Ok(())
20157    }
20158
20159    #[allow(clippy::too_many_arguments)]
20160    pub fn sdpa_naive_quantized_view(
20161        &self,
20162        q: &CudaSlice<f32>,
20163        k: &cudarc::driver::CudaView<u8>,
20164        v: &cudarc::driver::CudaView<u8>,
20165        o: &mut CudaSlice<f32>,
20166        head_dim: usize,
20167        n_head: usize,
20168        n_head_kv: usize,
20169        t: usize,
20170        t_kv: usize,
20171        scale: f32,
20172        causal: bool,
20173        k_tok_bytes: usize,
20174        v_tok_bytes: usize,
20175    ) -> Result<(), Box<dyn std::error::Error>> {
20176        let kv_dim = n_head_kv * head_dim;
20177        let mut kf = self.uninit(t_kv * kv_dim)?;
20178        let mut vf = self.uninit(t_kv * kv_dim)?;
20179        let f = self.func("fa_dequant_kv_ws_f32");
20180        let total = (2 * t_kv * kv_dim) as u64;
20181        let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
20182        let cfg = LaunchConfig {
20183            grid_dim: (nblk.max(1), 1, 1),
20184            block_dim: (256, 1, 1),
20185            shared_mem_bytes: 0,
20186        };
20187        let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
20188        let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
20189        let __s_b = self.gpu.stream();
20190        let mut b = __s_b.launch_builder(&f);
20191        b.arg(k)
20192            .arg(v)
20193            .arg(&mut kf)
20194            .arg(&mut vf)
20195            .arg(&kv_dim_i)
20196            .arg(&kv_dim_i)
20197            .arg(&t_kv_i)
20198            .arg(&k_tok_bytes_i)
20199            .arg(&v_tok_bytes_i);
20200        unsafe { b.launch(cfg)? };
20201        self.sdpa_naive(
20202            q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
20203        )
20204    }
20205
20206    /// WINDOWED twin of `sdpa_naive_quantized_view` (step35 SWA prefill): dequant the KV byte
20207    /// view into f32 workspaces with the SAME `fa_dequant_kv_ws_f32` launch, then run
20208    /// `sdpa_naive_w` instead of `sdpa_naive`. `window == 0` is the unwindowed form (the kernel
20209    /// treats a non-positive window as "no window mask"), so this is a strict superset of the
20210    /// unwindowed function above and produces bit-identical output at window == 0.
20211    ///
20212    /// Why this exists: EVERY windowed FlashAttention stamp in flash_attn.cu is head_dim-256
20213    /// only (`fa_prefill_w_f32` == `fa_prefill_f32_body<256>`, and the quantized-view windowed
20214    /// twins likewise), while step35 is head_dim 128. Its SWA layers therefore have no windowed
20215    /// FA path and take this f32 floor in v0 — same cache bytes, same numeric class as the
20216    /// unwindowed quantized-view fallback, so the chunk-invariance contract holds on both.
20217    #[allow(clippy::too_many_arguments)]
20218    pub fn sdpa_naive_w_quantized_view(
20219        &self,
20220        q: &CudaSlice<f32>,
20221        k: &cudarc::driver::CudaView<u8>,
20222        v: &cudarc::driver::CudaView<u8>,
20223        o: &mut CudaSlice<f32>,
20224        head_dim: usize,
20225        n_head: usize,
20226        n_head_kv: usize,
20227        t: usize,
20228        t_kv: usize,
20229        scale: f32,
20230        causal: bool,
20231        window: usize,
20232        k_tok_bytes: usize,
20233        v_tok_bytes: usize,
20234    ) -> Result<(), Box<dyn std::error::Error>> {
20235        let kv_dim = n_head_kv * head_dim;
20236        let mut kf = self.uninit(t_kv * kv_dim)?;
20237        let mut vf = self.uninit(t_kv * kv_dim)?;
20238        let f = self.func("fa_dequant_kv_ws_f32");
20239        let total = (2 * t_kv * kv_dim) as u64;
20240        let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
20241        let cfg = LaunchConfig {
20242            grid_dim: (nblk.max(1), 1, 1),
20243            block_dim: (256, 1, 1),
20244            shared_mem_bytes: 0,
20245        };
20246        let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
20247        let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
20248        let __s_b = self.gpu.stream();
20249        let mut b = __s_b.launch_builder(&f);
20250        b.arg(k)
20251            .arg(v)
20252            .arg(&mut kf)
20253            .arg(&mut vf)
20254            .arg(&kv_dim_i)
20255            .arg(&kv_dim_i)
20256            .arg(&t_kv_i)
20257            .arg(&k_tok_bytes_i)
20258            .arg(&v_tok_bytes_i);
20259        unsafe { b.launch(cfg)? };
20260        self.sdpa_naive_w(
20261            q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
20262        )
20263    }
20264
20265    /// Hand-written FlashAttention prefill (sm_120, FA-2 online softmax on validated mma.sync,
20266    /// head_dim 256 or 128 (template-stamped twins), GQA, causal). Replaces sdpa_naive for T>1.
20267    /// Q/K/V/O [head_dim, n_head(_kv), T].
20268    pub fn fa_prefill(
20269        &self,
20270        q: &CudaSlice<f32>,
20271        k: &CudaSlice<f32>,
20272        v: &CudaSlice<f32>,
20273        o: &mut CudaSlice<f32>,
20274        head_dim: usize,
20275        n_head: usize,
20276        n_head_kv: usize,
20277        t: usize,
20278        t_kv: usize,
20279        scale: f32,
20280        causal: bool,
20281    ) -> Result<(), Box<dyn std::error::Error>> {
20282        if portable_mma_gated() {
20283            return self.sdpa_naive(
20284                q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
20285            );
20286        }
20287        // FA3 v10 arm (task #20, OPT-IN MEMRA_FA3=1 — harness-proven 883us vs the shipped
20288        // kernel's 993us at T=2048): TMA-swizzled wgmma FA, fresh causal hd256 only.
20289        // NEW NUMERIC CONFIG (GDN-mma precedent): online softmax / bf16-P class — the
20290        // run-gen argmax + greedy-stream batteries arbitrate; not bit-paired.
20291        // PROMOTED default-ON hopper (2026-07-27): 3-seed 2048-prime -> 128-decode
20292        // streams MATCH vs mma, full battery green, lane interleaved 5/5 (+2.4%).
20293        // MEMRA_FA3=0 reverts; kernel-check pins the mma config regardless.
20294        let fa3_on = head_dim == 256
20295            && causal
20296            && t == t_kv
20297            && match std::env::var("MEMRA_FA3").as_deref() {
20298                Ok("0") => false,
20299                // The force arm consults the arch now: the bf16 stage below calls
20300                // f32_to_bf16_into -> func("f32_to_bf16_bulk"), which cu/hybrid.cu:1623 omits on
20301                // a portable build. Refuse at the switch, not at the lookup.
20302                Ok("1") => {
20303                    refuse_portable_force("MEMRA_FA3=1", "the sm_90a fa3/bf16 kernels");
20304                    true
20305                }
20306                _ => cfg!(memra_hopper_mma),
20307            };
20308        if fa3_on {
20309            let n = t * n_head * head_dim;
20310            let nkv = t * n_head_kv * head_dim;
20311            let mut q16 = self.alloc_u8_uninit(n * 2)?;
20312            let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
20313            let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
20314            self.f32_to_bf16_into(q, &mut q16, n)?;
20315            self.f32_to_bf16_into(k, &mut k16, nkv)?;
20316            self.f32_to_bf16_into(v, &mut v16, nkv)?;
20317            let rc = {
20318                use cudarc::driver::{DevicePtr, DevicePtrMut};
20319                let stream = self.gpu.stream();
20320                let (qp, _g1) = q16.device_ptr(&stream);
20321                let (kp, _g2) = k16.device_ptr(&stream);
20322                let (vp, _g3) = v16.device_ptr(&stream);
20323                let (op, _g4) = o.device_ptr_mut(&stream);
20324                unsafe {
20325                    memra_fa3_prefill(
20326                        qp as *const core::ffi::c_void,
20327                        kp as *const core::ffi::c_void,
20328                        vp as *const core::ffi::c_void,
20329                        op as *mut f32,
20330                        t as i32,
20331                        n_head as i32,
20332                        n_head_kv as i32,
20333                        head_dim as i32,
20334                        scale,
20335                        stream.cu_stream() as *mut core::ffi::c_void,
20336                    )
20337                }
20338            };
20339            if rc != 0 {
20340                return Err(format!("memra_fa3_prefill rc={rc}").into());
20341            }
20342            return Ok(());
20343        }
20344        // FLOOR PORT (P2+P0a+P0b+P1): 4 warps/CTA, BLOCK_Q=64 query rows, BK=32 KV tile,
20345        // Q-in-reg + register-O, grid.y=n_head_kv (4 Q-heads share staged K/V).
20346        // P1 plain arm (MEMRA_FA_P1=1 opt-in until the qwen battery): the engine-study body
20347        // (FA2 schedule + boundary split + swizzle) on the non-windowed lane. bf16 pre-convert.
20348        static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20349        let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
20350        if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
20351            const BLOCK_Q: usize = 64;
20352            const BKX: usize = 32;
20353            let f = self.func("fa_prefill_bf16_p1");
20354            let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
20355                + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
20356            use cudarc::driver::sys::CUfunction_attribute_enum as A;
20357            f.set_attribute(
20358                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20359                shmem as i32,
20360            )?;
20361            let cfg = LaunchConfig {
20362                grid_dim: (
20363                    (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20364                    n_head as u32,
20365                    1,
20366                ),
20367                block_dim: (32, 4, 1),
20368                shared_mem_bytes: shmem,
20369            };
20370            let (hd, nh, nhkv, ti, tkvi, cz) = (
20371                head_dim as i32,
20372                n_head as i32,
20373                n_head_kv as i32,
20374                t as i32,
20375                t_kv as i32,
20376                causal as i32,
20377            );
20378            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
20379            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20380            let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
20381            let __s_b = self.gpu.stream();
20382            let mut b = __s_b.launch_builder(&f);
20383            b.arg(&qb)
20384                .arg(&kb)
20385                .arg(&vb)
20386                .arg(o)
20387                .arg(&hd)
20388                .arg(&nh)
20389                .arg(&nhkv)
20390                .arg(&ti)
20391                .arg(&tkvi)
20392                .arg(&scale)
20393                .arg(&cz);
20394            unsafe {
20395                b.launch(cfg)?;
20396            }
20397            return Ok(());
20398        }
20399        // Edge 5a (DEFAULT): fa_prefill_f32_pp — register-resident softmax (no sSw smem
20400        // round-trip), the FA3 softmax-GEMM overlap variant. ncu (pp512): short_scoreboard
20401        // 4.32->3.47, wait 1.99->1.45, per-call ~577us->~440us (1.31x) at flat 12.1% warps /
20402        // 255 regs / 2 CTAs (occupancy preserved). Bit-safe: 9B+27B argmax MATCH, rel 2.55e-3
20403        // vs floor 3.03e-3. MEMRA_FA_FLOOR reverts to the serialized-softmax floor kernel.
20404        const BK: usize = 32;
20405        // W2 lane (MEMRA_FA_PP_W2=1, ncu 2026-07-26): 2-warp/32-row CTA tile doubles grid.x —
20406        // bit-identical per-row math, pure coverage trade for the 6.25%-occupancy starvation.
20407        let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
20408        let (block_q, warps, w2_sfx): (usize, u32, &str) =
20409            if w2 { (32, 2, "_w2") } else { (64, 4, "") };
20410        // hd128 twins (2026-07-07): the prefill kernels are template-stamped at 256 (original
20411        // names, dispatch unchanged) and 128 (`_hd128`, the MiniMax-M3 class). Callers gate
20412        // other head_dims to sdpa_naive before reaching here.
20413        let hd_sfx = fa_hd_suffix(head_dim)?;
20414        let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
20415        // BF16-KV staging lane (2026-07-26, default ON): the kernel converts K/V to bf16
20416        // during staging anyway — pre-converting to bf16 mirrors is BIT-IDENTICAL (same
20417        // __float2bfloat16 values into the same mma) and turns the 67%-of-stalls scalar
20418        // staging into int4 vector copies. MEMRA_FA_BF16KV=0 reverts.
20419        let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
20420        let (kb16, vb16) = if bf16kv {
20421            let n = t_kv * n_head_kv * head_dim;
20422            let mut kb = self.alloc_u8_uninit(n * 2)?;
20423            let mut vb = self.alloc_u8_uninit(n * 2)?;
20424            let fcv = self.func("f32_to_bf16_bulk");
20425            let ni = n as i64;
20426            let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20427            let __s_b = self.gpu.stream();
20428            let mut b = __s_b.launch_builder(&fcv);
20429            b.arg(k).arg(&mut kb).arg(&ni);
20430            unsafe {
20431                b.launch(cfgc)?;
20432            }
20433            let __s_b = self.gpu.stream();
20434            let mut b = __s_b.launch_builder(&fcv);
20435            b.arg(v).arg(&mut vb).arg(&ni);
20436            unsafe {
20437                b.launch(cfgc)?;
20438            }
20439            (Some(kb), Some(vb))
20440        } else {
20441            (None, None)
20442        };
20443        let f = self.func(&if bf16kv {
20444            format!("fa_prefill_bf16kv_pp{hd_sfx}")
20445        } else {
20446            format!(
20447                "fa_prefill_f32{}{}{hd_sfx}",
20448                if floor { "" } else { "_pp" },
20449                if floor { "" } else { w2_sfx }
20450            )
20451        });
20452        // persistent smem: bf16*(KV_STAGES*(sK + sV) + sP) + f32*(sS + sM + sL);
20453        // the bf16kv ring doubles the K/V stages (KV_STAGES=2).
20454        let kv_stages = if bf16kv { 2 } else { 1 };
20455        let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
20456            + 4 * (block_q * BK + 2 * block_q)) as u32;
20457        use cudarc::driver::sys::CUfunction_attribute_enum as A;
20458        f.set_attribute(
20459            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20460            shmem as i32,
20461        )?;
20462        let cfg = LaunchConfig {
20463            grid_dim: (
20464                (t as u32 + block_q as u32 - 1) / block_q as u32,
20465                n_head as u32,
20466                1,
20467            ),
20468            block_dim: (32, warps, 1),
20469            shared_mem_bytes: shmem,
20470        };
20471        let (hd, nh, nhkv, ti, tkvi, cz) = (
20472            head_dim as i32,
20473            n_head as i32,
20474            n_head_kv as i32,
20475            t as i32,
20476            t_kv as i32,
20477            causal as i32,
20478        );
20479        let __s_b = self.gpu.stream();
20480        let mut b = __s_b.launch_builder(&f);
20481        b.arg(q);
20482        match (&kb16, &vb16) {
20483            (Some(kb), Some(vb)) => {
20484                b.arg(kb).arg(vb);
20485            }
20486            _ => {
20487                b.arg(k).arg(v);
20488            }
20489        }
20490        b.arg(o)
20491            .arg(&hd)
20492            .arg(&nh)
20493            .arg(&nhkv)
20494            .arg(&ti)
20495            .arg(&tkvi)
20496            .arg(&scale)
20497            .arg(&cz);
20498        unsafe {
20499            b.launch(cfg)?;
20500        }
20501        Ok(())
20502    }
20503
20504    /// Windowed FA prefill (gemma4 SWA layers past the sliding window, hd256): fa_prefill's
20505    /// exact dispatch (pp default, MEMRA_FA_FLOOR seam) with the sliding-window mask + tile
20506    /// skip in-kernel. Replaces the O(T*T_kv) scalar sdpa_naive_w on the prime path.
20507    #[allow(clippy::too_many_arguments)]
20508    pub fn fa_prefill_w(
20509        &self,
20510        q: &CudaSlice<f32>,
20511        k: &CudaSlice<f32>,
20512        v: &CudaSlice<f32>,
20513        o: &mut CudaSlice<f32>,
20514        head_dim: usize,
20515        n_head: usize,
20516        n_head_kv: usize,
20517        t: usize,
20518        t_kv: usize,
20519        scale: f32,
20520        causal: bool,
20521        window: usize,
20522    ) -> Result<(), Box<dyn std::error::Error>> {
20523        // sm_90a rides the mma twins (portable_mma_gated, 2026-07-31 — the raw
20524        // portable_cuda gate was stale-conservative on Hopper; fa_prefill already flipped).
20525        if portable_mma_gated() {
20526            return self.sdpa_naive_w(
20527                q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
20528            );
20529        }
20530        // Default: bf16-prestaged twin (same treatment as hd512 — Q/K/V pre-converted once,
20531        // int4 stage copies; bit-identical, kernel_check-gated). MEMRA_FAW_STAGE=f32 reverts;
20532        // MEMRA_FA_FLOOR keeps the f32 floor stamp untouched.
20533        static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20534        let faw_f32 =
20535            *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
20536        let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
20537        self.fa_prefill_w_arm(
20538            q,
20539            k,
20540            v,
20541            o,
20542            head_dim,
20543            n_head,
20544            n_head_kv,
20545            t,
20546            t_kv,
20547            scale,
20548            causal,
20549            window,
20550            floor || faw_f32,
20551            floor,
20552        )
20553    }
20554
20555    /// Windowed FA prefill with PRE-CONVERTED bf16 operands (producer-emitted; 31B glue lane).
20556    /// Launches the P1 stamp directly — callers guarantee qb/kb/vb hold the exact bf16 of q/k/v.
20557    #[allow(clippy::too_many_arguments)]
20558    pub fn fa_prefill_w_pre(
20559        &self,
20560        qb: &CudaSlice<u8>,
20561        kb: &CudaSlice<u8>,
20562        vb: &CudaSlice<u8>,
20563        o: &mut CudaSlice<f32>,
20564        head_dim: usize,
20565        n_head: usize,
20566        n_head_kv: usize,
20567        t: usize,
20568        t_kv: usize,
20569        scale: f32,
20570        causal: bool,
20571        window: usize,
20572        v_f16: bool,
20573    ) -> Result<(), Box<dyn std::error::Error>> {
20574        const BLOCK_Q: usize = 64;
20575        const BK: usize = 32;
20576        debug_assert_eq!(head_dim, 256);
20577        let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
20578        debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
20579        if hp {
20580            const BLOCK_QH: usize = 32;
20581            // V bytes must be f16 for the h2 stamp; producer normally emits f16 (v_f16),
20582            // else re-encode through the pooled scratch (stream-ordered reuse).
20583            let mut vguard = self.fa_vf16_scratch.lock().unwrap();
20584            let vh: &CudaSlice<u8> = if v_f16 {
20585                vb
20586            } else {
20587                let n = t_kv * n_head_kv * head_dim;
20588                if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
20589                    *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
20590                }
20591                self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
20592                vguard.as_ref().unwrap()
20593            };
20594            let f = self.func("fa_prefill_w_bf16_p1h2");
20595            let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
20596            use cudarc::driver::sys::CUfunction_attribute_enum as A;
20597            f.set_attribute(
20598                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20599                shmem as i32,
20600            )?;
20601            let cfg = LaunchConfig {
20602                grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
20603                block_dim: (32, 4, 1),
20604                shared_mem_bytes: shmem,
20605            };
20606            let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
20607                head_dim as i32,
20608                n_head as i32,
20609                n_head_kv as i32,
20610                t as i32,
20611                t_kv as i32,
20612                causal as i32,
20613                window as i32,
20614            );
20615            let __s_b = self.gpu.stream();
20616            let mut b = __s_b.launch_builder(&f);
20617            b.arg(qb)
20618                .arg(kb)
20619                .arg(vh)
20620                .arg(o)
20621                .arg(&hd)
20622                .arg(&nh)
20623                .arg(&nhkv)
20624                .arg(&ti)
20625                .arg(&tkvi)
20626                .arg(&scale)
20627                .arg(&cz)
20628                .arg(&wi);
20629            unsafe {
20630                b.launch(cfg)?;
20631            }
20632            return Ok(());
20633        }
20634        let f = self.func("fa_prefill_w_bf16_p1");
20635        let shmem =
20636            (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
20637        use cudarc::driver::sys::CUfunction_attribute_enum as A;
20638        f.set_attribute(
20639            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20640            shmem as i32,
20641        )?;
20642        let cfg = LaunchConfig {
20643            grid_dim: (
20644                (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20645                n_head as u32,
20646                1,
20647            ),
20648            block_dim: (32, 4, 1),
20649            shared_mem_bytes: shmem,
20650        };
20651        let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
20652            head_dim as i32,
20653            n_head as i32,
20654            n_head_kv as i32,
20655            t as i32,
20656            t_kv as i32,
20657            causal as i32,
20658            window as i32,
20659        );
20660        let __s_b = self.gpu.stream();
20661        let mut b = __s_b.launch_builder(&f);
20662        b.arg(qb)
20663            .arg(kb)
20664            .arg(vb)
20665            .arg(o)
20666            .arg(&hd)
20667            .arg(&nh)
20668            .arg(&nhkv)
20669            .arg(&ti)
20670            .arg(&tkvi)
20671            .arg(&scale)
20672            .arg(&cz)
20673            .arg(&wi);
20674        unsafe {
20675            b.launch(cfg)?;
20676        }
20677        Ok(())
20678    }
20679
20680    /// Windowed FA prefill with the stage arm FORCED — the kernel_check bit-identity entry.
20681    #[allow(clippy::too_many_arguments)]
20682    pub fn fa_prefill_w_arm(
20683        &self,
20684        q: &CudaSlice<f32>,
20685        k: &CudaSlice<f32>,
20686        v: &CudaSlice<f32>,
20687        o: &mut CudaSlice<f32>,
20688        head_dim: usize,
20689        n_head: usize,
20690        n_head_kv: usize,
20691        t: usize,
20692        t_kv: usize,
20693        scale: f32,
20694        causal: bool,
20695        window: usize,
20696        f32_stage: bool,
20697        floor: bool,
20698    ) -> Result<(), Box<dyn std::error::Error>> {
20699        const BLOCK_Q: usize = 64;
20700        const BK: usize = 32;
20701        debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
20702        // P1 (2026-07-22 engine study): per-head Br=64 stamp with the FA2 schedule (V-copy
20703        // over GEMM0, next-K over softmax+GEMM1) + boundary/interior mask split. FP order
20704        // preserved -> bit-identical (gated). MEMRA_FAW_P1=0 reverts to the g4/o2 arms.
20705        static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20706        let p1 = !floor
20707            && !f32_stage
20708            && *P1_ON.get_or_init(|| {
20709                std::env::var("MEMRA_FAW_P1")
20710                    .map(|v| v != "0")
20711                    .unwrap_or(true)
20712            });
20713        let hp =
20714            p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
20715        if hp {
20716            const BLOCK_QH: usize = 32;
20717            let f = self.func("fa_prefill_w_bf16_p1h2");
20718            let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
20719            use cudarc::driver::sys::CUfunction_attribute_enum as A;
20720            f.set_attribute(
20721                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20722                shmem as i32,
20723            )?;
20724            let cfg = LaunchConfig {
20725                grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
20726                block_dim: (32, 4, 1),
20727                shared_mem_bytes: shmem,
20728            };
20729            let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
20730                head_dim as i32,
20731                n_head as i32,
20732                n_head_kv as i32,
20733                t as i32,
20734                t_kv as i32,
20735                causal as i32,
20736                window as i32,
20737            );
20738            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
20739            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20740            let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
20741            let __s_b = self.gpu.stream();
20742            let mut b = __s_b.launch_builder(&f);
20743            b.arg(&qb)
20744                .arg(&kb)
20745                .arg(&vh)
20746                .arg(o)
20747                .arg(&hd)
20748                .arg(&nh)
20749                .arg(&nhkv)
20750                .arg(&ti)
20751                .arg(&tkvi)
20752                .arg(&scale)
20753                .arg(&cz)
20754                .arg(&wi);
20755            unsafe {
20756                b.launch(cfg)?;
20757            }
20758            return Ok(());
20759        }
20760        if p1 {
20761            let f = self.func("fa_prefill_w_bf16_p1");
20762            let shmem =
20763                (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
20764            use cudarc::driver::sys::CUfunction_attribute_enum as A;
20765            f.set_attribute(
20766                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20767                shmem as i32,
20768            )?;
20769            let cfg = LaunchConfig {
20770                grid_dim: (
20771                    (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20772                    n_head as u32,
20773                    1,
20774                ),
20775                block_dim: (32, 4, 1),
20776                shared_mem_bytes: shmem,
20777            };
20778            let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
20779                head_dim as i32,
20780                n_head as i32,
20781                n_head_kv as i32,
20782                t as i32,
20783                t_kv as i32,
20784                causal as i32,
20785                window as i32,
20786            );
20787            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
20788            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20789            let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
20790            let __s_b = self.gpu.stream();
20791            let mut b = __s_b.launch_builder(&f);
20792            b.arg(&qb)
20793                .arg(&kb)
20794                .arg(&vb)
20795                .arg(o)
20796                .arg(&hd)
20797                .arg(&nh)
20798                .arg(&nhkv)
20799                .arg(&ti)
20800                .arg(&tkvi)
20801                .arg(&scale)
20802                .arg(&cz)
20803                .arg(&wi);
20804            unsafe {
20805                b.launch(cfg)?;
20806            }
20807            return Ok(());
20808        }
20809        // MQA head-grouping (MEMRA_FAW_G4=0 reverts): 4 heads/CTA share the staged K/V —
20810        // per-(head,row) FP chain identical to the per-head stamp -> bit-identical (gated).
20811        static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20812        let g4 = !floor
20813            && !f32_stage
20814            && n_head_kv == 1
20815            && n_head % 4 == 0
20816            && *G4_ON.get_or_init(|| {
20817                std::env::var("MEMRA_FAW_G4")
20818                    .map(|v| v != "0")
20819                    .unwrap_or(true)
20820            });
20821        if g4 {
20822            const SP_M: usize = 16;
20823            // Occupancy-2 twin (MEMRA_FAW_O2=0 reverts): one shared K/V buffer inside the dead
20824            // Q-stage region -> ~36.5KB smem, 2 CTA/SM (the llama hd256 mechanism). Bit-identical.
20825            static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20826            let o2 = *O2_ON.get_or_init(|| {
20827                std::env::var("MEMRA_FAW_O2")
20828                    .map(|v| v != "0")
20829                    .unwrap_or(true)
20830            });
20831            let f = self.func(if o2 {
20832                "fa_prefill_w_bf16_g4o2"
20833            } else {
20834                "fa_prefill_w_bf16_g4"
20835            });
20836            let shmem = if o2 {
20837                (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
20838            } else {
20839                (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
20840                    as u32
20841            };
20842            use cudarc::driver::sys::CUfunction_attribute_enum as A;
20843            f.set_attribute(
20844                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20845                shmem as i32,
20846            )?;
20847            let cfg = LaunchConfig {
20848                grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
20849                block_dim: (32, 4, 1),
20850                shared_mem_bytes: shmem,
20851            };
20852            let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
20853                head_dim as i32,
20854                n_head as i32,
20855                n_head_kv as i32,
20856                t as i32,
20857                t_kv as i32,
20858                causal as i32,
20859                window as i32,
20860            );
20861            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
20862            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20863            let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
20864            let __s_b = self.gpu.stream();
20865            let mut b = __s_b.launch_builder(&f);
20866            b.arg(&qb)
20867                .arg(&kb)
20868                .arg(&vb)
20869                .arg(o)
20870                .arg(&hd)
20871                .arg(&nh)
20872                .arg(&nhkv)
20873                .arg(&ti)
20874                .arg(&tkvi)
20875                .arg(&scale)
20876                .arg(&cz)
20877                .arg(&wi);
20878            unsafe {
20879                b.launch(cfg)?;
20880            }
20881            return Ok(());
20882        }
20883        let f = self.func(if floor {
20884            "fa_prefill_w_f32"
20885        } else if f32_stage {
20886            "fa_prefill_w_f32_pp"
20887        } else {
20888            "fa_prefill_w_bf16_pp"
20889        });
20890        let shmem =
20891            (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
20892        use cudarc::driver::sys::CUfunction_attribute_enum as A;
20893        f.set_attribute(
20894            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20895            shmem as i32,
20896        )?;
20897        let cfg = LaunchConfig {
20898            grid_dim: (
20899                (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20900                n_head as u32,
20901                1,
20902            ),
20903            block_dim: (32, 4, 1),
20904            shared_mem_bytes: shmem,
20905        };
20906        let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
20907            head_dim as i32,
20908            n_head as i32,
20909            n_head_kv as i32,
20910            t as i32,
20911            t_kv as i32,
20912            causal as i32,
20913            window as i32,
20914        );
20915        if f32_stage {
20916            let __s_b = self.gpu.stream();
20917            let mut b = __s_b.launch_builder(&f);
20918            b.arg(q)
20919                .arg(k)
20920                .arg(v)
20921                .arg(o)
20922                .arg(&hd)
20923                .arg(&nh)
20924                .arg(&nhkv)
20925                .arg(&ti)
20926                .arg(&tkvi)
20927                .arg(&scale)
20928                .arg(&cz)
20929                .arg(&wi);
20930            unsafe {
20931                b.launch(cfg)?;
20932            }
20933        } else {
20934            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
20935            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20936            let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
20937            let __s_b = self.gpu.stream();
20938            let mut b = __s_b.launch_builder(&f);
20939            b.arg(&qb)
20940                .arg(&kb)
20941                .arg(&vb)
20942                .arg(o)
20943                .arg(&hd)
20944                .arg(&nh)
20945                .arg(&nhkv)
20946                .arg(&ti)
20947                .arg(&tkvi)
20948                .arg(&scale)
20949                .arg(&cz)
20950                .arg(&wi);
20951            unsafe {
20952                b.launch(cfg)?;
20953            }
20954        }
20955        Ok(())
20956    }
20957
20958    /// hd512 FA prefill (gemma4 GLOBAL layers): BLOCK_Q=32 x 2 warps, Q staged in smem,
20959    /// grid.z = 2 O-halves (each CTA computes the full 512-dim scores, accumulates half the
20960    /// V dims). Replaces the scalar sdpa_naive on the prime path's globals.
20961    #[allow(clippy::too_many_arguments)]
20962    pub fn fa_prefill_hd512(
20963        &self,
20964        q: &CudaSlice<f32>,
20965        k: &CudaSlice<f32>,
20966        v: &CudaSlice<f32>,
20967        o: &mut CudaSlice<f32>,
20968        head_dim: usize,
20969        n_head: usize,
20970        n_head_kv: usize,
20971        t: usize,
20972        t_kv: usize,
20973        scale: f32,
20974        causal: bool,
20975    ) -> Result<(), Box<dyn std::error::Error>> {
20976        // sm_90a rides the mma twins (portable_mma_gated, 2026-07-31 — same flip as _w).
20977        if portable_mma_gated() {
20978            return self.sdpa_naive(
20979                q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
20980            );
20981        }
20982        // Default: pre-convert Q/K/V to bf16 once and stage int4 (8 bf16/copy) — at 1 CTA/SM the
20983        // synchronous stage serializes with compute and MQA re-stages the same K/V per head CTA;
20984        // pre-converting halves staged bytes and cuts stage instructions 8x. BIT-IDENTICAL to the
20985        // f32-staged kernel (the converter applies the same __float2bfloat16 the stage applied;
20986        // kernel_check gates the identity). MEMRA_FA512_STAGE=f32 = rollback to the f32 kernel.
20987        static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20988        let f32_stage =
20989            *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
20990        // Single-pass arm (MEMRA_FA512_SP=0 reverts to the z=2 bf16 kernel): GEMM0 split-K across
20991        // the 2 warps instead of recomputed per O-half CTA — the 2026-07-22 kernel-diff excess.
20992        // Own numeric config (partial-sum order) — battery-gated.
20993        static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20994        let sp = !f32_stage
20995            && *SP_ON.get_or_init(|| {
20996                std::env::var("MEMRA_FA512_SP")
20997                    .map(|v| v != "0")
20998                    .unwrap_or(true)
20999            });
21000        self.fa_prefill_hd512_arm(
21001            q,
21002            k,
21003            v,
21004            o,
21005            head_dim,
21006            n_head,
21007            n_head_kv,
21008            t,
21009            t_kv,
21010            scale,
21011            causal,
21012            f32_stage,
21013            sp,
21014            sp && fa_f16pv_on(),
21015        )
21016    }
21017
21018    /// hd512 single-pass FA with PRE-CONVERTED bf16 operands (producer-emitted).
21019    #[allow(clippy::too_many_arguments)]
21020    pub fn fa_prefill_hd512_pre(
21021        &self,
21022        qb: &CudaSlice<u8>,
21023        kb: &CudaSlice<u8>,
21024        vb: &CudaSlice<u8>,
21025        o: &mut CudaSlice<f32>,
21026        head_dim: usize,
21027        n_head: usize,
21028        n_head_kv: usize,
21029        t: usize,
21030        t_kv: usize,
21031        scale: f32,
21032        causal: bool,
21033        v_f16: bool,
21034    ) -> Result<(), Box<dyn std::error::Error>> {
21035        debug_assert_eq!(head_dim, 512);
21036        const SP_M: usize = 16;
21037        const BKS: usize = 32;
21038        // f16-P/V door (MEMRA_FA_F16PV=1): P and the P@V accumulation in f16 (llama's fa=1 VKQ
21039        // class); KQ/softmax/rescale-band/final-normalize stay f32. Own numeric config —
21040        // battery-gated. V bytes must be f16 for the sp16 kernel (stage/ldmatrix are typeless).
21041        let f16pv = fa_f16pv_on();
21042        let nw = if f16pv { fa512_wide_warps() } else { 2 };
21043        let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
21044        debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
21045        let mut vguard = self.fa_vf16_scratch.lock().unwrap();
21046        let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
21047            // Fallback re-encode (producer emitted bf16); the emit lane normally hands f16.
21048            let n = t_kv * n_head_kv * head_dim;
21049            let need = n * 2;
21050            if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
21051                *vguard = Some(self.alloc_uninit::<u8>(need)?);
21052            }
21053            let dst = vguard.as_mut().unwrap();
21054            self.bf16_to_f16_into(vb, n, dst)?;
21055            vguard.as_ref().unwrap()
21056        } else {
21057            vb
21058        };
21059        let f = self.func(if hp {
21060            "fa_prefill_bf16_hd512_sp16h2"
21061        } else {
21062            match (f16pv, nw) {
21063                (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
21064                (true, _) => "fa_prefill_bf16_hd512_sp16",
21065                _ => "fa_prefill_bf16_hd512_sp",
21066            }
21067        });
21068        let (nwarp, npart) = if hp {
21069            (4usize, 4usize)
21070        } else if nw > 2 {
21071            (nw, nw)
21072        } else {
21073            (2, 1)
21074        };
21075        // h2 drops sQ (Q register-resident) and doubles sP/sS/sL for the head pair.
21076        let shmem = if hp {
21077            (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
21078                as u32
21079        } else {
21080            (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
21081                + 4 * (npart * SP_M * BKS + SP_M)) as u32
21082        };
21083        use cudarc::driver::sys::CUfunction_attribute_enum as A;
21084        f.set_attribute(
21085            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21086            shmem as i32,
21087        )?;
21088        let grid_y = if hp {
21089            (n_head / 2) as u32
21090        } else {
21091            n_head as u32
21092        };
21093        let cfg = LaunchConfig {
21094            grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
21095            block_dim: (32, nwarp as u32, 1),
21096            shared_mem_bytes: shmem,
21097        };
21098        let (hd, nh, nhkv, ti, tkvi, cz) = (
21099            head_dim as i32,
21100            n_head as i32,
21101            n_head_kv as i32,
21102            t as i32,
21103            t_kv as i32,
21104            causal as i32,
21105        );
21106        let __s_b = self.gpu.stream();
21107        let mut b = __s_b.launch_builder(&f);
21108        b.arg(qb)
21109            .arg(kb)
21110            .arg(vref)
21111            .arg(o)
21112            .arg(&hd)
21113            .arg(&nh)
21114            .arg(&nhkv)
21115            .arg(&ti)
21116            .arg(&tkvi)
21117            .arg(&scale)
21118            .arg(&cz);
21119        unsafe {
21120            b.launch(cfg)?;
21121        }
21122        Ok(())
21123    }
21124
21125    /// hd512 FA prefill with the stage/sp arms FORCED — the kernel_check gate entry
21126    /// (`fa_prefill_hd512` picks the arms from MEMRA_FA512_STAGE / MEMRA_FA512_SP).
21127    #[allow(clippy::too_many_arguments)]
21128    pub fn fa_prefill_hd512_arm(
21129        &self,
21130        q: &CudaSlice<f32>,
21131        k: &CudaSlice<f32>,
21132        v: &CudaSlice<f32>,
21133        o: &mut CudaSlice<f32>,
21134        head_dim: usize,
21135        n_head: usize,
21136        n_head_kv: usize,
21137        t: usize,
21138        t_kv: usize,
21139        scale: f32,
21140        causal: bool,
21141        f32_stage: bool,
21142        sp: bool,
21143        f16pv: bool,
21144    ) -> Result<(), Box<dyn std::error::Error>> {
21145        debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
21146        if sp && !f32_stage {
21147            // Single-pass: 16 q-rows/CTA, 2 warps, grid (ceil(T/16), n_head, 1).
21148            // smem: sQ[16][512] + sK[32][512] + sV[32][512] + sP[16][32] (bf16) + sS[16][32]+sL f32.
21149            // f16pv: sp16 kernel — f16 P + f16 P@V accum, V operand encoded f16.
21150            const SP_M: usize = 16;
21151            const BKS: usize = 32;
21152            let nw = if f16pv { fa512_wide_warps() } else { 2 };
21153            let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
21154            let f = self.func(if hp {
21155                "fa_prefill_bf16_hd512_sp16h2"
21156            } else {
21157                match (f16pv, nw) {
21158                    (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
21159                    (true, _) => "fa_prefill_bf16_hd512_sp16",
21160                    _ => "fa_prefill_bf16_hd512_sp",
21161                }
21162            });
21163            let (nwarp, npart) = if hp {
21164                (4usize, 4usize)
21165            } else if nw > 2 {
21166                (nw, nw)
21167            } else {
21168                (2, 1)
21169            };
21170            let shmem = if hp {
21171                (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
21172                    + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
21173            } else {
21174                (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
21175                    + 4 * (npart * SP_M * BKS + SP_M)) as u32
21176            };
21177            use cudarc::driver::sys::CUfunction_attribute_enum as A;
21178            f.set_attribute(
21179                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21180                shmem as i32,
21181            )?;
21182            let grid_y = if hp {
21183                (n_head / 2) as u32
21184            } else {
21185                n_head as u32
21186            };
21187            let cfg = LaunchConfig {
21188                grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
21189                block_dim: (32, nwarp as u32, 1),
21190                shared_mem_bytes: shmem,
21191            };
21192            let (hd, nh, nhkv, ti, tkvi, cz) = (
21193                head_dim as i32,
21194                n_head as i32,
21195                n_head_kv as i32,
21196                t as i32,
21197                t_kv as i32,
21198                causal as i32,
21199            );
21200            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
21201            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
21202            let vb = if f16pv {
21203                self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
21204            } else {
21205                self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
21206            };
21207            let __s_b = self.gpu.stream();
21208            let mut b = __s_b.launch_builder(&f);
21209            b.arg(&qb)
21210                .arg(&kb)
21211                .arg(&vb)
21212                .arg(o)
21213                .arg(&hd)
21214                .arg(&nh)
21215                .arg(&nhkv)
21216                .arg(&ti)
21217                .arg(&tkvi)
21218                .arg(&scale)
21219                .arg(&cz);
21220            unsafe {
21221                b.launch(cfg)?;
21222            }
21223            return Ok(());
21224        }
21225        const BLOCK_Q: usize = 32;
21226        const BK: usize = 32;
21227        const HALF: usize = 256;
21228        let f = self.func(if f32_stage {
21229            "fa_prefill_f32_hd512"
21230        } else {
21231            "fa_prefill_bf16_hd512"
21232        });
21233        // sQ[32][512] + sK[BK][512] + sV[BK][256] + sP[32][BK] (bf16) + sL[32] f32
21234        let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
21235            + 4 * BLOCK_Q) as u32;
21236        use cudarc::driver::sys::CUfunction_attribute_enum as A;
21237        f.set_attribute(
21238            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21239            shmem as i32,
21240        )?;
21241        let cfg = LaunchConfig {
21242            grid_dim: (
21243                (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
21244                n_head as u32,
21245                2,
21246            ),
21247            block_dim: (32, 2, 1),
21248            shared_mem_bytes: shmem,
21249        };
21250        let (hd, nh, nhkv, ti, tkvi, cz) = (
21251            head_dim as i32,
21252            n_head as i32,
21253            n_head_kv as i32,
21254            t as i32,
21255            t_kv as i32,
21256            causal as i32,
21257        );
21258        if f32_stage {
21259            let __s_b = self.gpu.stream();
21260            let mut b = __s_b.launch_builder(&f);
21261            b.arg(q)
21262                .arg(k)
21263                .arg(v)
21264                .arg(o)
21265                .arg(&hd)
21266                .arg(&nh)
21267                .arg(&nhkv)
21268                .arg(&ti)
21269                .arg(&tkvi)
21270                .arg(&scale)
21271                .arg(&cz);
21272            unsafe {
21273                b.launch(cfg)?;
21274            }
21275        } else {
21276            let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
21277            let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
21278            let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
21279            let __s_b = self.gpu.stream();
21280            let mut b = __s_b.launch_builder(&f);
21281            b.arg(&qb)
21282                .arg(&kb)
21283                .arg(&vb)
21284                .arg(o)
21285                .arg(&hd)
21286                .arg(&nh)
21287                .arg(&nhkv)
21288                .arg(&ti)
21289                .arg(&tkvi)
21290                .arg(&scale)
21291                .arg(&cz);
21292            unsafe {
21293                b.launch(cfg)?;
21294            }
21295        }
21296        Ok(())
21297    }
21298
21299    /// rope_neox2 with bf16 EMIT (31B glue lane): identical rope math/stores plus the post-rope
21300    /// values written as bf16 — the FA q/k operands come from this launch (bit-identical to the
21301    /// separate f32_to_bf16 the FA entries would run).
21302    #[allow(clippy::too_many_arguments)]
21303    pub fn rope_neox2_bf16e(
21304        &self,
21305        q: &mut CudaSlice<f32>,
21306        k: &mut CudaSlice<f32>,
21307        qb: &mut CudaSlice<u8>,
21308        kb: &mut CudaSlice<u8>,
21309        pos: &CudaSlice<i32>,
21310        head_dim: usize,
21311        n_dims: usize,
21312        nh_q: usize,
21313        nh_k: usize,
21314        n_tokens: usize,
21315        base: f32,
21316        freq_scale: f32,
21317        ff: Option<&CudaSlice<f32>>,
21318    ) -> Result<(), Box<dyn std::error::Error>> {
21319        let f = self.func("rope_neox2_bf16e_f32");
21320        let rows = ((nh_q + nh_k) * n_tokens) as u32;
21321        let cfg = LaunchConfig {
21322            grid_dim: (rows, 1, 1),
21323            block_dim: ((head_dim / 2) as u32, 1, 1),
21324            shared_mem_bytes: 0,
21325        };
21326        let theta_scale = base.powf(-2.0 / n_dims as f32);
21327        let (hd, nd, nhq, nhk, nt) = (
21328            head_dim as i32,
21329            n_dims as i32,
21330            nh_q as i32,
21331            nh_k as i32,
21332            n_tokens as i32,
21333        );
21334        let __s_b = self.gpu.stream();
21335        let mut b = __s_b.launch_builder(&f);
21336        match ff {
21337            Some(t) => {
21338                b.arg(&mut *q)
21339                    .arg(&mut *k)
21340                    .arg(&mut *qb)
21341                    .arg(&mut *kb)
21342                    .arg(pos)
21343                    .arg(&hd)
21344                    .arg(&nd)
21345                    .arg(&nhq)
21346                    .arg(&nhk)
21347                    .arg(&nt)
21348                    .arg(&theta_scale)
21349                    .arg(&freq_scale)
21350                    .arg(t);
21351                unsafe {
21352                    b.launch(cfg)?;
21353                }
21354            }
21355            None => {
21356                let null: u64 = 0;
21357                b.arg(&mut *q)
21358                    .arg(&mut *k)
21359                    .arg(&mut *qb)
21360                    .arg(&mut *kb)
21361                    .arg(pos)
21362                    .arg(&hd)
21363                    .arg(&nd)
21364                    .arg(&nhq)
21365                    .arg(&nhk)
21366                    .arg(&nt)
21367                    .arg(&theta_scale)
21368                    .arg(&freq_scale)
21369                    .arg(&null);
21370                unsafe {
21371                    b.launch(cfg)?;
21372                }
21373            }
21374        }
21375        Ok(())
21376    }
21377
21378    /// Flat f32 -> bf16 conversion into a fresh scratch buffer (2 bytes/elem). `n % 4 == 0`
21379    /// (float4 in, 4x bf16 out). Feeds the bf16-staged hd512 FA prefill.
21380    pub fn f32_to_bf16(
21381        &self,
21382        x: &CudaSlice<f32>,
21383        n: usize,
21384    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21385        assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
21386        let mut y = self.alloc_uninit::<u8>(n * 2)?;
21387        let f = self.func("f32_to_bf16_flat");
21388        let n_i = n as i64;
21389        let cfg = LaunchConfig {
21390            grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
21391            block_dim: (256, 1, 1),
21392            shared_mem_bytes: 0,
21393        };
21394        let __s_b = self.gpu.stream();
21395        let mut b = __s_b.launch_builder(&f);
21396        b.arg(x).arg(&mut y).arg(&n_i);
21397        unsafe {
21398            b.launch(cfg)?;
21399        }
21400        Ok(y)
21401    }
21402
21403    pub fn f32_to_f16(
21404        &self,
21405        x: &CudaSlice<f32>,
21406        n: usize,
21407    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21408        assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
21409        let mut y = self.alloc_uninit::<u8>(n * 2)?;
21410        let f = self.func("f32_to_f16_flat");
21411        let n_i = n as i64;
21412        let cfg = LaunchConfig {
21413            grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
21414            block_dim: (256, 1, 1),
21415            shared_mem_bytes: 0,
21416        };
21417        let __s_b = self.gpu.stream();
21418        let mut b = __s_b.launch_builder(&f);
21419        b.arg(x).arg(&mut y).arg(&n_i);
21420        unsafe {
21421            b.launch(cfg)?;
21422        }
21423        Ok(y)
21424    }
21425
21426    /// bf16 bytes -> f16 bytes, n elements (the f16-P/V door's V re-encode on the emit lane).
21427    pub fn bf16_to_f16(
21428        &self,
21429        xb: &CudaSlice<u8>,
21430        n: usize,
21431    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21432        let mut y = self.alloc_uninit::<u8>(n * 2)?;
21433        self.bf16_to_f16_into(xb, n, &mut y)?;
21434        Ok(y)
21435    }
21436
21437    /// Same conversion into a caller-owned (pooled) buffer; `y.len() >= n*2`.
21438    pub fn bf16_to_f16_into(
21439        &self,
21440        xb: &CudaSlice<u8>,
21441        n: usize,
21442        y: &mut CudaSlice<u8>,
21443    ) -> Result<(), Box<dyn std::error::Error>> {
21444        assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
21445        assert!(y.len() >= n * 2);
21446        let f = self.func("bf16_to_f16_flat");
21447        let n2 = (n / 2) as i64;
21448        let cfg = LaunchConfig {
21449            grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
21450            block_dim: (256, 1, 1),
21451            shared_mem_bytes: 0,
21452        };
21453        let __s_b = self.gpu.stream();
21454        let mut b = __s_b.launch_builder(&f);
21455        b.arg(xb).arg(y).arg(&n2);
21456        unsafe {
21457            b.launch(cfg)?;
21458        }
21459        Ok(())
21460    }
21461
21462    /// task #18 (attn side): varlen FA — bf16 K/V mirrors (2 launches) + ONE
21463    /// fa_prefill_bf16kv launch for every fresh sequence. Same per-block math as the
21464    /// per-seq path (bit-gateable). Caller guarantees: fresh causal (T_kv == T),
21465    /// head_dim in {256, 128}, bf16kv lane on.
21466    #[allow(clippy::too_many_arguments)]
21467    pub fn fa_prefill_vl8(
21468        &self,
21469        seqs: &[FaSeqVl],
21470        head_dim: usize,
21471        n_head: usize,
21472        n_head_kv: usize,
21473        scale: f32,
21474    ) -> Result<(), Box<dyn std::error::Error>> {
21475        const BK: usize = 32;
21476        let b = seqs.len();
21477        assert!(b >= 1 && b <= 8);
21478        let mut packed = [FaSeqVl::default(); 8];
21479        packed[..b].copy_from_slice(seqs);
21480        let v = FaVl8(packed);
21481        let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21482        let ept = (n_head_kv * head_dim) as i32;
21483        {
21484            let f = self.func("fa_mirror_vl");
21485            let max_n = (max_t as i64) * ept as i64;
21486            let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
21487            for which in 0..2i32 {
21488                let cfg = LaunchConfig {
21489                    grid_dim: (blocks, 1, b as u32),
21490                    block_dim: (256, 1, 1),
21491                    shared_mem_bytes: 0,
21492                };
21493                let __s_lb = self.gpu.stream();
21494                let mut lb = __s_lb.launch_builder(&f);
21495                lb.arg(&v).arg(&ept).arg(&which);
21496                unsafe {
21497                    lb.launch(cfg)?;
21498                }
21499            }
21500        }
21501        let hd_sfx = fa_hd_suffix(head_dim)?;
21502        let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
21503        let block_q = 64usize;
21504        let kv_stages = 2usize;
21505        let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
21506            + 4 * (block_q * BK + 2 * block_q)) as u32;
21507        use cudarc::driver::sys::CUfunction_attribute_enum as A;
21508        f.set_attribute(
21509            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21510            shmem as i32,
21511        )?;
21512        let cfg = LaunchConfig {
21513            grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
21514            block_dim: (32, 4, 1),
21515            shared_mem_bytes: shmem,
21516        };
21517        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
21518        let __s_lb = self.gpu.stream();
21519        let mut lb = __s_lb.launch_builder(&f);
21520        lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
21521        unsafe {
21522            lb.launch(cfg)?;
21523        }
21524        Ok(())
21525    }
21526
21527    /// task #18 (attn pre-FA): varlen split + QK-norm + RoPE + KV-append — FOUR launches
21528    /// for every fresh sequence (was 6 x B, plus the q/k/v split copies which the view
21529    /// inputs remove entirely). Fresh-only (append at t0=0, RoPE pos = token index).
21530    #[allow(clippy::too_many_arguments)]
21531    pub fn attn_pre_vl8(
21532        &self,
21533        seqs: &[AttnPreVl],
21534        wq: &CudaSlice<f32>,
21535        wk: &CudaSlice<f32>,
21536        head_dim: usize,
21537        rope_dims: usize,
21538        n_head: usize,
21539        n_head_kv: usize,
21540        eps: f32,
21541        freq_base: f32,
21542        freq_scale: f32,
21543        kv_dim_k: usize,
21544        kv_dim_v: usize,
21545        k_tok_bytes: usize,
21546        v_tok_bytes: usize,
21547    ) -> Result<(), Box<dyn std::error::Error>> {
21548        let b = seqs.len();
21549        assert!(b >= 1 && b <= 8);
21550        let mut packed = [AttnPreVl::default(); 8];
21551        packed[..b].copy_from_slice(seqs);
21552        let v = AttnPreVl8(packed);
21553        let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21554        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
21555        {
21556            let f = self.func("q_gate_split_vl");
21557            let n = max_t * (n_head * head_dim) as u32;
21558            let cfg = LaunchConfig {
21559                grid_dim: (n.div_ceil(256), 1, b as u32),
21560                block_dim: (256, 1, 1),
21561                shared_mem_bytes: 0,
21562            };
21563            let __s_lb = self.gpu.stream();
21564            let mut lb = __s_lb.launch_builder(&f);
21565            lb.arg(&v).arg(&hd).arg(&nh);
21566            unsafe {
21567                lb.launch(cfg)?;
21568            }
21569        }
21570        {
21571            let f = self.func("attn_rms_vl");
21572            let cfg = LaunchConfig {
21573                grid_dim: (max_t * n_head as u32, 2, b as u32),
21574                block_dim: (rms_block(), 1, 1),
21575                shared_mem_bytes: 0,
21576            };
21577            let __s_lb = self.gpu.stream();
21578            let mut lb = __s_lb.launch_builder(&f);
21579            lb.arg(&v)
21580                .arg(wq)
21581                .arg(wk)
21582                .arg(&hd)
21583                .arg(&nh)
21584                .arg(&nhkv)
21585                .arg(&eps);
21586            unsafe {
21587                lb.launch(cfg)?;
21588            }
21589        }
21590        {
21591            let f = self.func("attn_rope_vl");
21592            let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
21593            let nd = rope_dims as i32;
21594            let cfg = LaunchConfig {
21595                grid_dim: (max_t * n_head as u32, 2, b as u32),
21596                block_dim: ((head_dim / 2) as u32, 1, 1),
21597                shared_mem_bytes: 0,
21598            };
21599            let __s_lb = self.gpu.stream();
21600            let mut lb = __s_lb.launch_builder(&f);
21601            lb.arg(&v)
21602                .arg(&hd)
21603                .arg(&nd)
21604                .arg(&nh)
21605                .arg(&nhkv)
21606                .arg(&theta_scale)
21607                .arg(&freq_scale);
21608            unsafe {
21609                lb.launch(cfg)?;
21610            }
21611        }
21612        {
21613            let f = self.func("append_kv_vl");
21614            let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
21615            let cfg = LaunchConfig {
21616                grid_dim: (nblk, max_t, b as u32),
21617                block_dim: (32, 1, 1),
21618                shared_mem_bytes: 0,
21619            };
21620            let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
21621            let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21622            let __s_lb = self.gpu.stream();
21623            let mut lb = __s_lb.launch_builder(&f);
21624            lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
21625            unsafe {
21626                lb.launch(cfg)?;
21627            }
21628        }
21629        Ok(())
21630    }
21631
21632    /// FA prefill where K/V are QUANTIZED CudaViews into the resident byte KV cache (the T=K verify
21633    /// path, MTP-PLAN §D.3). Uses `fa_prefill_q` (inline-dequant during stage-to-smem). The view's
21634    /// base+offset pointer is honored; the kernel reads [0..t_kv*tok_bytes). Q is the T fresh query
21635    /// rows; t = T, t_kv = cache len. k_tok_bytes/v_tok_bytes are the per-token byte strides.
21636    pub fn fa_prefill_view(
21637        &self,
21638        q: &CudaSlice<f32>,
21639        k: &cudarc::driver::CudaView<u8>,
21640        v: &cudarc::driver::CudaView<u8>,
21641        o: &mut CudaSlice<f32>,
21642        head_dim: usize,
21643        n_head: usize,
21644        n_head_kv: usize,
21645        t: usize,
21646        t_kv: usize,
21647        scale: f32,
21648        causal: bool,
21649        k_tok_bytes: usize,
21650        v_tok_bytes: usize,
21651        g: bool,
21652    ) -> Result<(), Box<dyn std::error::Error>> {
21653        if portable_mma_gated() {
21654            return self.sdpa_naive_quantized_view(
21655                q,
21656                k,
21657                v,
21658                o,
21659                head_dim,
21660                n_head,
21661                n_head_kv,
21662                t,
21663                t_kv,
21664                scale,
21665                causal,
21666                k_tok_bytes,
21667                v_tok_bytes,
21668            );
21669        }
21670        const BLOCK_Q: usize = 64;
21671        const BK: usize = 32;
21672        // g = e4m3 cache: the kernel parses via DQ_K_ELEM/DQ_V_ELEM (format macros) — the
21673        // kf8vf8-module stamp reads fp8 with the identical MMA/softmax/PV body.
21674        let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
21675        let f = if g {
21676            self.func_g(&name)
21677        } else {
21678            self.func(&name)
21679        };
21680        let shmem =
21681            (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
21682        use cudarc::driver::sys::CUfunction_attribute_enum as A;
21683        f.set_attribute(
21684            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21685            shmem as i32,
21686        )?;
21687        let cfg = LaunchConfig {
21688            grid_dim: (
21689                (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
21690                n_head as u32,
21691                1,
21692            ),
21693            block_dim: (32, 4, 1),
21694            shared_mem_bytes: shmem,
21695        };
21696        let (hd, nh, nhkv, ti, tkvi, cz) = (
21697            head_dim as i32,
21698            n_head as i32,
21699            n_head_kv as i32,
21700            t as i32,
21701            t_kv as i32,
21702            causal as i32,
21703        );
21704        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21705        let __s_b = self.gpu.stream();
21706        let mut b = __s_b.launch_builder(&f);
21707        b.arg(q)
21708            .arg(k)
21709            .arg(v)
21710            .arg(o)
21711            .arg(&hd)
21712            .arg(&nh)
21713            .arg(&nhkv)
21714            .arg(&ti)
21715            .arg(&tkvi)
21716            .arg(&scale)
21717            .arg(&cz)
21718            .arg(&ktb)
21719            .arg(&vtb);
21720        unsafe {
21721            b.launch(cfg)?;
21722        }
21723        Ok(())
21724    }
21725
21726    /// ARC B (2026-07-05): dequant-once chunk-prime FA. Same contract as `fa_prefill_view`, but
21727    /// instead of every (q-block, head) CTA re-dequanting the whole quantized KV stream inline
21728    /// (T/64 x n_head redundant at chunk prime — 30.5% of the 32k prime wall), dequant the full
21729    /// [t_kv, kv_dim] K and V ONCE into a resident bf16 workspace (fa_dequant_kv_ws_bf16), then
21730    /// run `fa_prefill_qw` (the bf16-workspace twin) over it. EXACT: the workspace holds the same
21731    /// __float2bfloat16(dq_*_elem(...)) values fa_prefill_q stages to smem, and the twin's MMA/
21732    /// softmax/PV code is byte-identical -> bit-identical O (kernel_check pins bitdiff=0).
21733    /// The workspace allocation is REUSED across layers/chunks (grown to the largest shape);
21734    /// contents are rewritten per call. MEMRA_PRIME_DEQW=0 falls back to fa_prefill_view (callers gate).
21735    #[allow(clippy::too_many_arguments)]
21736    pub fn fa_prefill_view_ws(
21737        &self,
21738        q: &CudaSlice<f32>,
21739        k: &cudarc::driver::CudaView<u8>,
21740        v: &cudarc::driver::CudaView<u8>,
21741        o: &mut CudaSlice<f32>,
21742        head_dim: usize,
21743        n_head: usize,
21744        n_head_kv: usize,
21745        t: usize,
21746        t_kv: usize,
21747        scale: f32,
21748        causal: bool,
21749        k_tok_bytes: usize,
21750        v_tok_bytes: usize,
21751        g: bool,
21752    ) -> Result<(), Box<dyn std::error::Error>> {
21753        if portable_mma_gated() {
21754            return self.sdpa_naive_quantized_view(
21755                q,
21756                k,
21757                v,
21758                o,
21759                head_dim,
21760                n_head,
21761                n_head_kv,
21762                t,
21763                t_kv,
21764                scale,
21765                causal,
21766                k_tok_bytes,
21767                v_tok_bytes,
21768            );
21769        }
21770        const BLOCK_Q: usize = 64;
21771        const BK: usize = 32;
21772        let kv_dim_k = n_head_kv * head_dim;
21773        let kv_dim_v = n_head_kv * head_dim;
21774        let k_ws_bytes = t_kv * kv_dim_k * 2; // bf16
21775        let v_ws_bytes = t_kv * kv_dim_v * 2;
21776        // Lock held across BOTH launches: enqueue-only (µs), all compute serializes on gpu.stream.
21777        let mut guard = self.prime_deqw_ws.lock().unwrap();
21778        let need_grow = match guard.as_ref() {
21779            Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
21780            None => true,
21781        };
21782        if need_grow {
21783            let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
21784            let (ck, cv) = guard
21785                .as_ref()
21786                .map(|(a, b)| (a.len(), b.len()))
21787                .unwrap_or((0, 0));
21788            *guard = Some((
21789                self.alloc_u8(grow(ck, k_ws_bytes))?,
21790                self.alloc_u8(grow(cv, v_ws_bytes))?,
21791            ));
21792        }
21793        let (kw, vw) = guard.as_mut().unwrap();
21794        // pass 1: dequant K+V once into the bf16 workspace (grid-stride, 1 thread/elem)
21795        {
21796            // only THIS pass parses KV bytes — pass 2 reads the bf16 workspace (format-free).
21797            let f = if g {
21798                self.func_g("fa_dequant_kv_ws_bf16")
21799            } else {
21800                self.func("fa_dequant_kv_ws_bf16")
21801            };
21802            let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
21803            let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
21804            let cfg = LaunchConfig {
21805                grid_dim: (nblk.max(1), 1, 1),
21806                block_dim: (256, 1, 1),
21807                shared_mem_bytes: 0,
21808            };
21809            let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
21810            let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21811            let __s_b = self.gpu.stream();
21812            let mut b = __s_b.launch_builder(&f);
21813            b.arg(k)
21814                .arg(v)
21815                .arg(&mut *kw)
21816                .arg(&mut *vw)
21817                .arg(&kdk)
21818                .arg(&kdv)
21819                .arg(&tkvi)
21820                .arg(&ktb)
21821                .arg(&vtb);
21822            unsafe {
21823                b.launch(cfg)?;
21824            }
21825        }
21826        // pass 2: the bf16-workspace prefill twin (same tile sizes/loop structure as fa_prefill_q).
21827        // DEFAULT: cp.async double-buffered staging twin (fa_prefill_qw_db, +32KB smem for the
21828        // second K/V tile pair, 1 CTA/SM): overlaps tile n+1's L2->smem copy with tile n's MMA.
21829        // Bit-identical output (staging is a pure byte copy; kernel_check pins bitdiff=0 under
21830        // both twins). A/B (27B rtx6000, N=3): 32k prime 17.10->16.51s, 16k 9.09->8.65s — the copy
21831        // latency hides behind the MMA pipe and beats the 2-CTA/SM occupancy of the sync twin.
21832        // MEMRA_PRIME_DEQW_DB=0 falls back to the single-buffer twin.
21833        let db = std::env::var("MEMRA_PRIME_DEQW_DB")
21834            .map(|v| v != "0")
21835            .unwrap_or(true);
21836        {
21837            let hd_sfx = fa_hd_suffix(head_dim)?;
21838            let f = self.func(&format!(
21839                "fa_prefill_qw{}{hd_sfx}",
21840                if db { "_db" } else { "" }
21841            ));
21842            let shmem = if db {
21843                // 4x KV tile buffers (bf16) + sP (bf16) + sL (f32)
21844                (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
21845            } else {
21846                (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
21847            };
21848            use cudarc::driver::sys::CUfunction_attribute_enum as A;
21849            f.set_attribute(
21850                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21851                shmem as i32,
21852            )?;
21853            let cfg = LaunchConfig {
21854                grid_dim: (
21855                    (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
21856                    n_head as u32,
21857                    1,
21858                ),
21859                block_dim: (32, 4, 1),
21860                shared_mem_bytes: shmem,
21861            };
21862            let (hd, nh, nhkv, ti, tkvi, cz) = (
21863                head_dim as i32,
21864                n_head as i32,
21865                n_head_kv as i32,
21866                t as i32,
21867                t_kv as i32,
21868                causal as i32,
21869            );
21870            let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
21871            let __s_b = self.gpu.stream();
21872            let mut b = __s_b.launch_builder(&f);
21873            b.arg(q)
21874                .arg(&*kw)
21875                .arg(&*vw)
21876                .arg(o)
21877                .arg(&hd)
21878                .arg(&nh)
21879                .arg(&nhkv)
21880                .arg(&ti)
21881                .arg(&tkvi)
21882                .arg(&scale)
21883                .arg(&cz)
21884                .arg(&kdk)
21885                .arg(&kdv);
21886            unsafe {
21887                b.launch(cfg)?;
21888            }
21889        }
21890        Ok(())
21891    }
21892
21893    /// WINDOWED `fa_prefill_view_ws` twin at head_dim 128 (lane/pp-prefill 2026-08-07):
21894    /// step35's SWA prefill (win=512, 33 of 45 layers) previously had NO windowed FA prefill
21895    /// stamp — every windowed twin was hd256-only — and took `sdpa_naive_w_quantized_view`,
21896    /// the f32 floor, at 565 ms/layer on a pp4096 where the hd128 FA family does the harder
21897    /// causal-4096 in 3.3 ms (41% of the whole prime; research/pp-prefill-20260807 anatomy).
21898    /// Same two-pass shape as the unwindowed function: dequant K/V ONCE into the resident
21899    /// bf16 workspace, then the windowed qw kernel (`fa_prefill_qw_db_w_hd128`, cp.async
21900    /// double-buffered; MEMRA_PRIME_DEQW_DB=0 selects the single-buffer twin). The window
21901    /// mask is `fa_prefill_f32_body`'s exact predicate; `window == 0` is bit-identical to
21902    /// `fa_prefill_view_ws` by construction (default-arg body). NEW NUMERIC CLASS vs the
21903    /// f32 floor on SWA rows (bf16 MMA online-softmax vs f32 serial softmax) — adoption is
21904    /// gated by the full battery, and the class must change UNIFORMLY for a whole request
21905    /// (kernel selection keys on seq_end, never per chunk — the chunkfix law).
21906    /// hd128-only deliberately: the only windowed-prefill consumer at another head_dim is
21907    /// gemma4 (hd256), which already has `fa_prefill_w_f32`.
21908    #[allow(clippy::too_many_arguments)]
21909    pub fn fa_prefill_view_ws_w_hd128(
21910        &self,
21911        q: &CudaSlice<f32>,
21912        k: &cudarc::driver::CudaView<u8>,
21913        v: &cudarc::driver::CudaView<u8>,
21914        o: &mut CudaSlice<f32>,
21915        head_dim: usize,
21916        n_head: usize,
21917        n_head_kv: usize,
21918        t: usize,
21919        t_kv: usize,
21920        scale: f32,
21921        causal: bool,
21922        window: usize,
21923        k_tok_bytes: usize,
21924        v_tok_bytes: usize,
21925    ) -> Result<(), Box<dyn std::error::Error>> {
21926        assert_eq!(
21927            head_dim, 128,
21928            "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
21929        );
21930        if portable_mma_gated() {
21931            return self.sdpa_naive_w_quantized_view(
21932                q,
21933                k,
21934                v,
21935                o,
21936                head_dim,
21937                n_head,
21938                n_head_kv,
21939                t,
21940                t_kv,
21941                scale,
21942                causal,
21943                window,
21944                k_tok_bytes,
21945                v_tok_bytes,
21946            );
21947        }
21948        const BLOCK_Q: usize = 64;
21949        const BK: usize = 32;
21950        let kv_dim_k = n_head_kv * head_dim;
21951        let kv_dim_v = n_head_kv * head_dim;
21952        let k_ws_bytes = t_kv * kv_dim_k * 2; // bf16
21953        let v_ws_bytes = t_kv * kv_dim_v * 2;
21954        let mut guard = self.prime_deqw_ws.lock().unwrap();
21955        let need_grow = match guard.as_ref() {
21956            Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
21957            None => true,
21958        };
21959        if need_grow {
21960            let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
21961            let (ck, cv) = guard
21962                .as_ref()
21963                .map(|(a, b)| (a.len(), b.len()))
21964                .unwrap_or((0, 0));
21965            *guard = Some((
21966                self.alloc_u8(grow(ck, k_ws_bytes))?,
21967                self.alloc_u8(grow(cv, v_ws_bytes))?,
21968            ));
21969        }
21970        let (kw, vw) = guard.as_mut().unwrap();
21971        // pass 1: dequant K+V once into the bf16 workspace (identical to fa_prefill_view_ws —
21972        // the workspace bytes are the SAME __float2bfloat16(dq(...)) values either way).
21973        {
21974            let f = self.func("fa_dequant_kv_ws_bf16");
21975            let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
21976            let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
21977            let cfg = LaunchConfig {
21978                grid_dim: (nblk.max(1), 1, 1),
21979                block_dim: (256, 1, 1),
21980                shared_mem_bytes: 0,
21981            };
21982            let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
21983            let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21984            let __s_b = self.gpu.stream();
21985            let mut b = __s_b.launch_builder(&f);
21986            b.arg(k)
21987                .arg(v)
21988                .arg(&mut *kw)
21989                .arg(&mut *vw)
21990                .arg(&kdk)
21991                .arg(&kdv)
21992                .arg(&tkvi)
21993                .arg(&ktb)
21994                .arg(&vtb);
21995            unsafe {
21996                b.launch(cfg)?;
21997            }
21998        }
21999        // pass 2: the WINDOWED qw twin (db default, same as the unwindowed wrapper).
22000        let db = std::env::var("MEMRA_PRIME_DEQW_DB")
22001            .map(|v| v != "0")
22002            .unwrap_or(true);
22003        {
22004            let f = self.func(if db {
22005                "fa_prefill_qw_db_w_hd128"
22006            } else {
22007                "fa_prefill_qw_w_hd128"
22008            });
22009            let shmem = if db {
22010                (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
22011            } else {
22012                (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
22013            };
22014            use cudarc::driver::sys::CUfunction_attribute_enum as A;
22015            f.set_attribute(
22016                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22017                shmem as i32,
22018            )?;
22019            let cfg = LaunchConfig {
22020                grid_dim: (
22021                    (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
22022                    n_head as u32,
22023                    1,
22024                ),
22025                block_dim: (32, 4, 1),
22026                shared_mem_bytes: shmem,
22027            };
22028            let (hd, nh, nhkv, ti, tkvi, cz) = (
22029                head_dim as i32,
22030                n_head as i32,
22031                n_head_kv as i32,
22032                t as i32,
22033                t_kv as i32,
22034                causal as i32,
22035            );
22036            let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
22037            let __s_b = self.gpu.stream();
22038            let mut b = __s_b.launch_builder(&f);
22039            b.arg(q)
22040                .arg(&*kw)
22041                .arg(&*vw)
22042                .arg(o)
22043                .arg(&hd)
22044                .arg(&nh)
22045                .arg(&nhkv)
22046                .arg(&ti)
22047                .arg(&tkvi)
22048                .arg(&scale)
22049                .arg(&cz)
22050                .arg(&kdk)
22051                .arg(&kdv)
22052                .arg(&wnd);
22053            unsafe {
22054                b.launch(cfg)?;
22055            }
22056        }
22057        Ok(())
22058    }
22059
22060    /// FA decode (T=1 split-K) over the resident QUANTIZED KV cache (q8_0 K / q5_1 V) as u8 views.
22061    /// Replaces sdpa_naive_view for decode; inline-dequants per element. k_tok_bytes/v_tok_bytes are
22062    /// the per-token byte strides (differ: q8_0=34*nblk, q5_1=24*nblk per token).
22063    pub fn fa_decode(
22064        &self,
22065        q: &CudaSlice<f32>,
22066        k: &cudarc::driver::CudaView<u8>,
22067        v: &cudarc::driver::CudaView<u8>,
22068        o: &mut CudaSlice<f32>,
22069        head_dim: usize,
22070        n_head: usize,
22071        n_head_kv: usize,
22072        t_kv: usize,
22073        scale: f32,
22074        k_tok_bytes: usize,
22075        v_tok_bytes: usize,
22076    ) -> Result<(), Box<dyn std::error::Error>> {
22077        self.fa_decode_kvmod(
22078            q,
22079            k,
22080            v,
22081            o,
22082            head_dim,
22083            n_head,
22084            n_head_kv,
22085            t_kv,
22086            scale,
22087            k_tok_bytes,
22088            v_tok_bytes,
22089            false,
22090        )
22091    }
22092
22093    /// `fa_decode` with an explicit fp8-module flag (`g`): gemma windowed layers under
22094    /// MEMRA_GEMMA_WKV read an e4m3 cache — every kernel must come from the kf8vf8 module
22095    /// and the v4 lane (q8_0-hardcoded staging) is excluded.
22096    #[allow(clippy::too_many_arguments)]
22097    /// UNIFIED scalar decode launch (fa_decode_f32, nullable-ctr): ONE symbol for host-len
22098    /// (kvmod eager) and device-len (graph/stream) callers — the textually-identical f32_dc
22099    /// twin compiled apart and its ULP drift flipped 31B verify argmaxes (2026-07-12).
22100    #[allow(clippy::too_many_arguments)]
22101    #[allow(clippy::too_many_arguments)]
22102    fn fa_decode_scalar_unified(
22103        &self,
22104        q: &cudarc::driver::CudaView<f32>,
22105        k: &cudarc::driver::CudaView<u8>,
22106        v: &cudarc::driver::CudaView<u8>,
22107        o: &mut cudarc::driver::CudaViewMut<f32>,
22108        head_dim: usize,
22109        n_head: usize,
22110        n_head_kv: usize,
22111        t_kv_host: usize,
22112        t_kv_dev: Option<&CudaSlice<i32>>,
22113        scale: f32,
22114        n_splits: usize,
22115        split_keys: usize,
22116        k_tok_bytes: usize,
22117        v_tok_bytes: usize,
22118        g: bool,
22119        part_o: &mut CudaSlice<f32>,
22120        part_m: &mut CudaSlice<f32>,
22121        part_l: &mut CudaSlice<f32>,
22122        q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
22123    ) -> Result<(), Box<dyn std::error::Error>> {
22124        let f = if g {
22125            self.func_g("fa_decode_f32")
22126        } else {
22127            self.fa_func("fa_decode_f32", head_dim)
22128        };
22129        let cfg = LaunchConfig {
22130            grid_dim: (n_head as u32, n_splits as u32, 1),
22131            block_dim: (head_dim as u32, 1, 1),
22132            shared_mem_bytes: (4 * (head_dim + 32)) as u32,
22133        };
22134        let (hd, nh, nhkv, nsp) = (
22135            head_dim as i32,
22136            n_head as i32,
22137            n_head_kv as i32,
22138            n_splits as i32,
22139        );
22140        let (ktb, vtb, tkvi, ski) = (
22141            k_tok_bytes as i64,
22142            v_tok_bytes as i64,
22143            t_kv_host as i32,
22144            split_keys as i32,
22145        );
22146        let __s_b = self.gpu.stream();
22147        let mut b = __s_b.launch_builder(&f);
22148        match t_kv_dev {
22149            Some(d) => {
22150                b.arg(q)
22151                    .arg(k)
22152                    .arg(v)
22153                    .arg(&mut *part_o)
22154                    .arg(&mut *part_m)
22155                    .arg(&mut *part_l)
22156                    .arg(&hd)
22157                    .arg(&nh)
22158                    .arg(&nhkv)
22159                    .arg(&tkvi)
22160                    .arg(d)
22161                    .arg(&scale)
22162                    .arg(&nsp)
22163                    .arg(&ski)
22164                    .arg(&ktb)
22165                    .arg(&vtb);
22166                unsafe {
22167                    b.launch(cfg)?;
22168                }
22169            }
22170            None => {
22171                let null: u64 = 0;
22172                b.arg(q)
22173                    .arg(k)
22174                    .arg(v)
22175                    .arg(&mut *part_o)
22176                    .arg(&mut *part_m)
22177                    .arg(&mut *part_l)
22178                    .arg(&hd)
22179                    .arg(&nh)
22180                    .arg(&nhkv)
22181                    .arg(&tkvi)
22182                    .arg(&null)
22183                    .arg(&scale)
22184                    .arg(&nsp)
22185                    .arg(&ski)
22186                    .arg(&ktb)
22187                    .arg(&vtb);
22188                unsafe {
22189                    b.launch(cfg)?;
22190                }
22191            }
22192        }
22193        let cfg2 = LaunchConfig {
22194            grid_dim: (n_head as u32, 1, 1),
22195            block_dim: (head_dim as u32, 1, 1),
22196            shared_mem_bytes: 0,
22197        };
22198        if let Some((oq, od)) = q8_out {
22199            // wave-5b: q8-emitting combine — the wo matmul_pre consumes the pair directly.
22200            let fc = if g {
22201                self.func_g("fa_decode_combine_q8_1")
22202            } else {
22203                self.fa_func("fa_decode_combine_q8_1", head_dim)
22204            };
22205            let __s_b2 = self.gpu.stream();
22206            let mut b2 = __s_b2.launch_builder(&fc);
22207            b2.arg(&*part_o)
22208                .arg(&*part_m)
22209                .arg(&*part_l)
22210                .arg(oq)
22211                .arg(od)
22212                .arg(&hd)
22213                .arg(&nh)
22214                .arg(&nsp);
22215            unsafe {
22216                b2.launch(cfg2)?;
22217            }
22218            return Ok(());
22219        }
22220        let fc = if g {
22221            self.func_g("fa_decode_combine_f32")
22222        } else {
22223            self.fa_func("fa_decode_combine_f32", head_dim)
22224        };
22225        let __s_b2 = self.gpu.stream();
22226        let mut b2 = __s_b2.launch_builder(&fc);
22227        b2.arg(&*part_o)
22228            .arg(&*part_m)
22229            .arg(&*part_l)
22230            .arg(o)
22231            .arg(&hd)
22232            .arg(&nh)
22233            .arg(&nsp);
22234        unsafe {
22235            b2.launch(cfg2)?;
22236        }
22237        Ok(())
22238    }
22239
22240    pub fn fa_decode_kvmod(
22241        &self,
22242        q: &CudaSlice<f32>,
22243        k: &cudarc::driver::CudaView<u8>,
22244        v: &cudarc::driver::CudaView<u8>,
22245        o: &mut CudaSlice<f32>,
22246        head_dim: usize,
22247        n_head: usize,
22248        n_head_kv: usize,
22249        t_kv: usize,
22250        scale: f32,
22251        k_tok_bytes: usize,
22252        v_tok_bytes: usize,
22253        g: bool,
22254    ) -> Result<(), Box<dyn std::error::Error>> {
22255        let q_view = q.as_view();
22256        let mut o_view = o.as_view_mut();
22257        self.fa_decode_kvmod_view(
22258            &q_view,
22259            k,
22260            v,
22261            &mut o_view,
22262            head_dim,
22263            n_head,
22264            n_head_kv,
22265            t_kv,
22266            scale,
22267            k_tok_bytes,
22268            v_tok_bytes,
22269            g,
22270        )
22271    }
22272
22273    /// Row-view entry into `fa_decode_kvmod`. The kernel sees the selected Q/output rows as its
22274    /// base pointers, so the launch geometry and arithmetic are identical to the owned-slice entry.
22275    /// Batched fallback callers use this to avoid materializing rows around an otherwise unchanged
22276    /// per-session KV view and FA launch.
22277    #[allow(clippy::too_many_arguments)]
22278    pub fn fa_decode_kvmod_view(
22279        &self,
22280        q: &cudarc::driver::CudaView<f32>,
22281        k: &cudarc::driver::CudaView<u8>,
22282        v: &cudarc::driver::CudaView<u8>,
22283        o: &mut cudarc::driver::CudaViewMut<f32>,
22284        head_dim: usize,
22285        n_head: usize,
22286        n_head_kv: usize,
22287        t_kv: usize,
22288        scale: f32,
22289        k_tok_bytes: usize,
22290        v_tok_bytes: usize,
22291        g: bool,
22292    ) -> Result<(), Box<dyn std::error::Error>> {
22293        // PERF-4: the warp-per-token vec path replaces the scalar element-per-thread fa_decode_f32 —
22294        // warp-per-token fa_decode_vec_q (grid=(n_head_kv,n_splits), block=(32,gqa_ratio)).
22295        // The block dequants each KV tile ONCE into smem (bf16) and broadcasts to all gqa Q-head
22296        // warps -> each KV byte leaves HBM/L2 ~1x/group (vs 4x). ARGS identical; func/grid/block/
22297        // smem/n_splits differ. fa_decode_f32 stays the bit-reference fallback. Combine is shared.
22298        //
22299        // SPLIT-K: the scalar path has grid.x=n_head (32) blocks; the vec path only has
22300        // grid.x=n_head_kv (8). To avoid starving the GPU at mid ctx, the vec path splits MORE
22301        // aggressively (64 keys/split vs 256) so grid.y rises and 8*n_splits fills the SMs.
22302        // At VERY short ctx (t_kv<96) even 1 split can't fill the GPU from 8 KV heads, so the
22303        // broadcast can't beat the scalar path's 4x-more-blocks latency hiding — fall back to
22304        // scalar there (measured crossover: vec 0.68x at t_kv=64, 1.23x at t_kv=96, 2.2x at 256).
22305        // DEFAULT-ON (2026-06-28): clean clock-locked sweep proved vec beats scalar at every
22306        // t_kv>=96 and the gain WIDENS with ctx (graph decode: +9.5% @128, +11.6% @512, +11.8%
22307        // @2048) — the KV-byte-broadcast (4x fewer HBM reads/group) compounds as attention grows.
22308        // MEMRA_NO_FA_VEC forces the scalar bit-reference. Below FA_VEC_MIN_TKV the scalar path's
22309        // 4x-more-blocks (grid.x=n_head=32 vs n_head_kv=8) hides latency better, so keep scalar there.
22310        // g + no-v4: the g-module REGISTER twin mis-decodes the gemma windowed shape
22311        // (root-cause open, jsonl) — only reachable by forcing v4 off (MEMRA_FA_V4_MAX);
22312        // fall to the exact scalar there instead of the broken register arm.
22313        let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
22314        // hd256 under g: v4-or-scalar. hd128/other under g ride the REGISTER g-lane (the
22315        // dq_K_lane/dq_V_lane macros are format-aware; v3 is format-gated off, v2/smem
22316        // arms are excluded under g). Mirrored in kvmod / fa_decode_dc / fa_geom_eager.
22317        if g && head_dim == 256 && !fa_v4_at(t_kv) {
22318            fa_vec = false;
22319        }
22320        let sp = fa_split_keys(t_kv, n_head_kv);
22321        let n_splits = if fa_vec {
22322            ((t_kv + sp - 1) / sp).max(1)
22323        } else {
22324            ((t_kv + 255) / 256).max(1)
22325        };
22326        let o_len = n_head * n_splits * head_dim;
22327        let ml_len = n_head * n_splits;
22328        let mut part_guard = self.fa_part_pool.lock().unwrap();
22329        if part_guard
22330            .as_ref()
22331            .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
22332            .unwrap_or(true)
22333        {
22334            // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
22335            // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
22336            // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
22337            // later live allocations land at those addresses, and the next graph REPLAY writes
22338            // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
22339            // output corruption began the burst after the trunk's t_kv growth first realloc'd
22340            // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
22341            // the baked addresses alive (single-stream: eager writes the new buffers, replays
22342            // touch only the old — never concurrently). Doubling growth bounds retired VRAM
22343            // (total retired < final size).
22344            let old = part_guard.take();
22345            let (co, cm) = old
22346                .as_ref()
22347                .map(|pp| (pp.0.len(), pp.1.len()))
22348                .unwrap_or((0, 0));
22349            if let Some(old) = old {
22350                self.fa_part_retired.lock().unwrap().push(old);
22351            }
22352            if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
22353                eprintln!(
22354                    "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
22355                    co, o_len, cm, ml_len
22356                );
22357            }
22358            *part_guard =
22359                Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
22360        }
22361        let pg = part_guard.as_mut().unwrap();
22362        self.gpu
22363            .stream()
22364            .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
22365        self.gpu
22366            .stream()
22367            .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
22368        self.gpu
22369            .stream()
22370            .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
22371        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
22372        let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
22373        let (hd, nh, nhkv, tkvi, nsp) = (
22374            head_dim as i32,
22375            n_head as i32,
22376            n_head_kv as i32,
22377            t_kv as i32,
22378            n_splits as i32,
22379        );
22380        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22381        // The vec kernel holds head_dim/32 register accumulators (FA_DEC_MAX_DPL=8 -> head_dim<=256).
22382        // All shipped models use head_dim=256; fall back to scalar for anything wider rather than
22383        // silently truncating the accumulator.
22384        let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
22385        // hd-512 vec crossover (MEMRA_FA512_MIN, default 512): the DPL16 twin wins at depth
22386        // (82.5 -> vec at 1736) but the scalar's more-blocks latency hiding wins at tiny t_kv
22387        // (the same scalar-floor physics as hd256's old 96 floor; short-ctx plain regressed
22388        // 178.4 -> 173.7 when 512 rode vec unconditionally).
22389        let fa512_min = fa512_min_tkv();
22390        // FA-DEEP pick (bit-identical twins, see fa_deep_at): default module only — the
22391        // g-module keeps the v4 pick (its class is not the depth-decay class).
22392        let deep = fa_vec
22393            && head_dim == 256
22394            && fa_v4_at(t_kv)
22395            && !g
22396            && fa_deep_at(t_kv)
22397            && !matches!(fa_v4_mode(), "noB3" | "stage");
22398        let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
22399            // gemma4 globals (hd 512): the DPL16 register twin (fa_decode_vec_q body with a
22400            // 16-slot accumulator ceiling). Scalar fallback measured 82.5us/layer at 1736 ctx.
22401            let gqa = (n_head / n_head_kv).max(1) as u32;
22402            let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
22403            (
22404                fv,
22405                LaunchConfig {
22406                    grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22407                    block_dim: (32, gqa, 1),
22408                    shared_mem_bytes: 0,
22409                },
22410            )
22411        } else if fa_vec && head_dim <= 256 {
22412            let gqa = (n_head / n_head_kv).max(1) as u32;
22413            // DEEP-CTX smem twin (2026-07-05): the register-dequant path's GQA reuse rides L2,
22414            // which holds to ~8k ctx but dies at 40k (layer KV ~37MB) — the 4 GQA warps then
22415            // re-read every KV byte from DRAM (4x traffic). Above MEMRA_FA_SMEM_TKV (default
22416            // 1024 — the 2026-07-05 crossover re-sweep on real prompts: p3 spec 73.8->79.2 at
22417            // 2048, flat down to 512, p2 +5%, p1/9B unchanged; the ARC-A probe's synthetic
22418            // 2.1x smem-at-all-depths pointed here; 0=never) dispatch the smem-broadcast twin:
22419            // dequant each tile ONCE per block.
22420            // Bit-identical per (token,split): same bf16 round-trip, same accumulation order,
22421            // same partial layout -> same combine. Short/mid ctx keeps the register path (it won
22422            // there by 12x — latency, not bandwidth, rules small KV).
22423            static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
22424            let smem_tkv = *SMEM_TKV.get_or_init(|| {
22425                std::env::var("MEMRA_FA_SMEM_TKV")
22426                    .ok()
22427                    .and_then(|v| v.parse().ok())
22428                    .unwrap_or_else(|| {
22429                        FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
22430                    })
22431            });
22432            if fa_v4_at(t_kv) && head_dim == 256 {
22433                // FA v4 lane (2026-07-10): key-per-lane score phase, zero shuffles per key.
22434                // NEW NUMERIC CONFIG (chunk-serial per-key dot) — battery-arbitrated.
22435                // g (fp8-windowed): the v4 staging is format-aware (2026-07-12) — kf8vf8 module.
22436                let v4name = match fa_v4_mode() {
22437                    "noB3" => "fa_decode_vec_q_v4_noB3", // phase probe (WRONG OUTPUT)
22438                    "stage" => "fa_decode_vec_q_v4_stage", // phase probe (WRONG OUTPUT)
22439                    _ if deep => "fa_decode_vec_q_v4_deep",
22440                    _ => "fa_decode_vec_q_v4",
22441                };
22442                let fv = if g {
22443                    self.func_g(v4name)
22444                } else {
22445                    self.func(v4name)
22446                };
22447                // fa_v4_smem (deep: fa_v4_deep_smem, +640B row pads) + sV (g: raw e4m3 sV
22448                // tile = 1B/elem — half the smem, 3->5 blocks/SM)
22449                let shmem = (if deep { 12160 } else { 11520 }
22450                    + 32 * head_dim * if g { 1 } else { 2 }) as u32;
22451                use cudarc::driver::sys::CUfunction_attribute_enum as A;
22452                fv.set_attribute(
22453                    A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22454                    shmem as i32,
22455                )?;
22456                (
22457                    fv,
22458                    LaunchConfig {
22459                        grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22460                        block_dim: (32, gqa, 1),
22461                        shared_mem_bytes: shmem,
22462                    },
22463                )
22464            } else if fa_v3_active(head_dim) {
22465                // FA v3 lane: dp4a-K hybrid (register-quantized Q, raw q8_0 K, staged-V kept).
22466                // smem = sV only (half of v2's).
22467                let fv = if g {
22468                    self.func_g("fa_decode_vec_q_v3")
22469                } else {
22470                    self.func("fa_decode_vec_q_v3")
22471                };
22472                let shmem = (32 * head_dim * 2) as u32; // sV bf16 [FA_DEC_TILE=32][hd]
22473                (
22474                    fv,
22475                    LaunchConfig {
22476                        grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22477                        block_dim: (32, gqa, 1),
22478                        shared_mem_bytes: shmem,
22479                    },
22480                )
22481            } else if fa_v2_on() {
22482                // FAVENDOR lane: llama fattn-vec tile-batched softmax + wide-load staging on
22483                // OUR smem KV broadcast. Replaces BOTH per-key twins when on; same grid/block/
22484                // partials; same 32KB sK+sV tile as the smem twin.
22485                let fv = if g {
22486                    self.func_g("fa_decode_vec_q_v2")
22487                } else {
22488                    self.func("fa_decode_vec_q_v2")
22489                };
22490                let shmem = (2 * 32 * head_dim * 2) as u32; // sK+sV bf16 [FA_DEC_TILE=32][hd]
22491                (
22492                    fv,
22493                    LaunchConfig {
22494                        grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22495                        block_dim: (32, gqa, 1),
22496                        shared_mem_bytes: shmem,
22497                    },
22498                )
22499            } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
22500            {
22501                // (fp8 exclusions: the smem twin's V-stage is q5_1-hardcoded — neither the wkv
22502                // windowed layers (g) nor the gkv globals (hd512) may be forced onto it via
22503                // MEMRA_FA_SMEM_TKV; they fall through to the format-clean register/scalar arms.)
22504                let fv = if g {
22505                    self.func_g("fa_decode_vec_q_smem")
22506                } else {
22507                    self.func("fa_decode_vec_q_smem")
22508                };
22509                let shmem = (2 * 32 * head_dim * 2) as u32; // sK+sV bf16 [FA_DEC_TILE=32][hd]
22510                use cudarc::driver::sys::CUfunction_attribute_enum as A;
22511                fv.set_attribute(
22512                    A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22513                    shmem as i32,
22514                )?;
22515                (
22516                    fv,
22517                    LaunchConfig {
22518                        grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22519                        block_dim: (32, gqa, 1),
22520                        shared_mem_bytes: shmem,
22521                    },
22522                )
22523            } else {
22524                // REGISTER-DEQUANT kernel (2026-07-03): per-warp direct q8_0/q5_1 register
22525                // dequant, zero dynamic shared memory.
22526                let fv = if g {
22527                    self.func_g("fa_decode_vec_q")
22528                } else {
22529                    self.func("fa_decode_vec_q")
22530                };
22531                (
22532                    fv,
22533                    LaunchConfig {
22534                        grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22535                        block_dim: (32, gqa, 1),
22536                        shared_mem_bytes: 0,
22537                    },
22538                )
22539            }
22540        } else {
22541            // UNIFIED scalar (nullable-ctr symbol shared with graph/stream callers). The
22542            // split ladder value rides along so ns_eff reproduces THIS n_splits in-kernel.
22543            return self.fa_decode_scalar_unified(
22544                q,
22545                k,
22546                v,
22547                o,
22548                head_dim,
22549                n_head,
22550                n_head_kv,
22551                t_kv,
22552                None,
22553                scale,
22554                n_splits,
22555                if fa_vec { sp } else { 256 },
22556                k_tok_bytes,
22557                v_tok_bytes,
22558                g,
22559                part_o,
22560                part_m,
22561                part_l,
22562                None,
22563            );
22564        };
22565        let __s_b = self.gpu.stream();
22566        let mut b = __s_b.launch_builder(&f);
22567        b.arg(q)
22568            .arg(k)
22569            .arg(v)
22570            .arg(&mut *part_o)
22571            .arg(&mut *part_m)
22572            .arg(&mut *part_l)
22573            .arg(&hd)
22574            .arg(&nh)
22575            .arg(&nhkv)
22576            .arg(&tkvi)
22577            .arg(&scale)
22578            .arg(&nsp)
22579            .arg(&ktb)
22580            .arg(&vtb);
22581        unsafe {
22582            b.launch(cfg)?;
22583        }
22584        // (combine re-tile refuted in the fa-deep lane — flat/worse both shapes; the v4
22585        // combine stays for all arms. Receipts research/fa-decode-deep-20260802/.)
22586        let (fc, cfg2) = (
22587            if g {
22588                self.func_g("fa_decode_combine_f32")
22589            } else {
22590                self.fa_func("fa_decode_combine_f32", head_dim)
22591            },
22592            LaunchConfig {
22593                grid_dim: (n_head as u32, 1, 1),
22594                block_dim: (head_dim as u32, 1, 1),
22595                shared_mem_bytes: 0,
22596            },
22597        );
22598        let __s_b2 = self.gpu.stream();
22599        let mut b2 = __s_b2.launch_builder(&fc);
22600        b2.arg(&*part_o)
22601            .arg(&*part_m)
22602            .arg(&*part_l)
22603            .arg(o)
22604            .arg(&hd)
22605            .arg(&nh)
22606            .arg(&nsp);
22607        unsafe {
22608            b2.launch(cfg2)?;
22609        }
22610        Ok(())
22611    }
22612
22613    /// BATCHED-TICK increment 2: ONE fa_decode launch covering ALL B sequences of the
22614    /// batched decode step (blockIdx.z = sequence). Per-seq K/V cache bases ride a device
22615    /// pointer table (`kv_ptrs`, [2B] interleaved k0,v0,...); per-seq key bounds ride the
22616    /// tick's position table (`pos_seq`, T_kv = pos+1). v4-lane only: the CALLER
22617    /// (decode_batch) gates every row through `fa_seqs_eligible` AND one `fa_split_keys`
22618    /// rung (`split_keys`), so each sequence's split partition, key walk and combine order
22619    /// reproduce its per-seq eager v4 program exactly (kernel-check pins seqs-vs-loop bit
22620    /// identity; decode-batch-gate strict pins the whole tick vs decode_step_h).
22621    /// q is the stacked [B, n_head, head_dim] tick buffer read in place (no per-seq q
22622    /// copies); o is written [B, n_head, head_dim] in place (no per-seq a copies).
22623    #[allow(clippy::too_many_arguments)]
22624    pub fn fa_decode_batch_seqs_v4(
22625        &self,
22626        q: &CudaSlice<f32>,
22627        kv_ptrs: &cudarc::driver::CudaView<u64>,
22628        pos_seq: &CudaSlice<i32>,
22629        o: &mut CudaSlice<f32>,
22630        head_dim: usize,
22631        n_head: usize,
22632        n_head_kv: usize,
22633        b_n: usize,
22634        t_kv_max: usize,
22635        scale: f32,
22636        split_keys: usize,
22637        k_tok_bytes: usize,
22638        v_tok_bytes: usize,
22639    ) -> Result<(), Box<dyn std::error::Error>> {
22640        debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
22641        let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
22642        let o_len = b_n * n_head * n_splits_max * head_dim;
22643        let ml_len = b_n * n_head * n_splits_max;
22644        let mut part_guard = self.fa_part_pool.lock().unwrap();
22645        if part_guard
22646            .as_ref()
22647            .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
22648            .unwrap_or(true)
22649        {
22650            // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
22651            // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
22652            // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
22653            // later live allocations land at those addresses, and the next graph REPLAY writes
22654            // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
22655            // output corruption began the burst after the trunk's t_kv growth first realloc'd
22656            // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
22657            // the baked addresses alive (single-stream: eager writes the new buffers, replays
22658            // touch only the old — never concurrently). Doubling growth bounds retired VRAM
22659            // (total retired < final size).
22660            let old = part_guard.take();
22661            let (co, cm) = old
22662                .as_ref()
22663                .map(|pp| (pp.0.len(), pp.1.len()))
22664                .unwrap_or((0, 0));
22665            if let Some(old) = old {
22666                self.fa_part_retired.lock().unwrap().push(old);
22667            }
22668            if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
22669                eprintln!(
22670                    "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
22671                    co, o_len, cm, ml_len
22672                );
22673            }
22674            *part_guard =
22675                Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
22676        }
22677        let pg = part_guard.as_mut().unwrap();
22678        self.gpu
22679            .stream()
22680            .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
22681        self.gpu
22682            .stream()
22683            .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
22684        self.gpu
22685            .stream()
22686            .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
22687        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
22688        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
22689        let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
22690        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22691        let gqa = (n_head / n_head_kv).max(1) as u32;
22692        let f = self.func("fa_decode_vec_q_seqs_v4");
22693        // fa_v4_smem (11520B) + sV bf16 tile — the v4 eager arm's sizing on the default module.
22694        let shmem = (11520 + 32 * head_dim * 2) as u32;
22695        use cudarc::driver::sys::CUfunction_attribute_enum as A;
22696        f.set_attribute(
22697            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22698            shmem as i32,
22699        )?;
22700        let cfg = LaunchConfig {
22701            grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
22702            block_dim: (32, gqa, 1),
22703            shared_mem_bytes: shmem,
22704        };
22705        {
22706            let __s_b = self.gpu.stream();
22707            let mut b = __s_b.launch_builder(&f);
22708            b.arg(q)
22709                .arg(kv_ptrs)
22710                .arg(pos_seq)
22711                .arg(&mut *part_o)
22712                .arg(&mut *part_m)
22713                .arg(&mut *part_l)
22714                .arg(&hd)
22715                .arg(&nh)
22716                .arg(&nhkv)
22717                .arg(&scale)
22718                .arg(&nspm)
22719                .arg(&spk)
22720                .arg(&ktb)
22721                .arg(&vtb);
22722            unsafe {
22723                b.launch(cfg)?;
22724            }
22725        }
22726        let fc = self.func("fa_decode_combine_seqs");
22727        let cfg2 = LaunchConfig {
22728            grid_dim: (n_head as u32, b_n as u32, 1),
22729            block_dim: (head_dim as u32, 1, 1),
22730            shared_mem_bytes: 0,
22731        };
22732        let __s_b2 = self.gpu.stream();
22733        let mut b2 = __s_b2.launch_builder(&fc);
22734        b2.arg(&*part_o)
22735            .arg(&*part_m)
22736            .arg(&*part_l)
22737            .arg(o)
22738            .arg(&hd)
22739            .arg(&nh)
22740            .arg(pos_seq)
22741            .arg(&nspm)
22742            .arg(&spk);
22743        unsafe {
22744            b2.launch(cfg2)?;
22745        }
22746        Ok(())
22747    }
22748
22749    /// BATCHED-TICK increment 2: z-batched decode KV append — one launch appends this
22750    /// step's B rows, each into ITS OWN sequence cache at slot pos_seq[z], through the same
22751    /// [2B] interleaved pointer table the seqs FA reads. Each (block, z) warp executes the
22752    /// per-token appender's exact warp program on row z of the stacked [B, kv_dim] k/v —
22753    /// written cache bytes are BIT-IDENTICAL to the B per-seq calls it replaces
22754    /// (kernel-check pins the bytes). Default flash module only (callers exclude fp8-KV).
22755    #[allow(clippy::too_many_arguments)]
22756    pub fn append_kv_quantized_seqs(
22757        &self,
22758        k_rows: &CudaSlice<f32>,
22759        v_rows: &CudaSlice<f32>,
22760        kv_ptrs: &cudarc::driver::CudaView<u64>,
22761        pos_seq: &CudaSlice<i32>,
22762        b_n: usize,
22763        kv_dim_k: usize,
22764        kv_dim_v: usize,
22765        k_tok_bytes: usize,
22766        v_tok_bytes: usize,
22767    ) -> Result<(), Box<dyn std::error::Error>> {
22768        let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
22769        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
22770        let cfg = LaunchConfig {
22771            grid_dim: (nblk, b_n as u32, 1),
22772            block_dim: (32, 1, 1),
22773            shared_mem_bytes: 0,
22774        };
22775        let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
22776        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22777        let __s_b = self.gpu.stream();
22778        let mut b = __s_b.launch_builder(&f);
22779        b.arg(k_rows)
22780            .arg(v_rows)
22781            .arg(kv_ptrs)
22782            .arg(pos_seq)
22783            .arg(&kdk)
22784            .arg(&kdv)
22785            .arg(&ktb)
22786            .arg(&vtb);
22787        unsafe {
22788            b.launch(cfg)?;
22789        }
22790        Ok(())
22791    }
22792
22793    /// True iff the MULTI-ROW verify FA (`fa_decode_rows`) is usable for a verify batch whose
22794    /// FIRST row attends `base_len + 1` keys: every row must take the SAME kernel eager decode
22795    /// would (the vec path) — mirrors fa_decode's gate exactly (MEMRA_NO_FA_VEC + FA_VEC_MIN_TKV +
22796    /// head_dim), evaluated at the MINIMUM row bound so no row could have picked scalar.
22797    /// MEMRA_FA_ROWS_OFF=1 is the A/B + fallback seam (per-row loop).
22798    pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
22799        std::env::var("MEMRA_NO_FA_VEC").is_err()
22800            && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
22801            && base_len + 1 >= fa_vec_min_tkv()
22802            && head_dim <= 256
22803            && head_dim % 32 == 0
22804    }
22805
22806    /// MULTI-ROW verify FA: run fa_decode_vec_q's EXACT per-row program for T causal query rows
22807    /// (row r attends keys [0..base_len+r+1)) in ONE kernel launch with grid.z = row, plus ONE
22808    /// row-batched combine. Replaces the T separate (fa_decode + combine) launches of the spec
22809    /// verify — same per-row split partition (n_splits_r = ceil(t_kv_r/split_keys), the
22810    /// fa_split_keys formula), same key-walk order, same reduce shapes => bit-identical outputs
22811    /// per row (kernel-check pins rows-vs-loop byte identity; run-spec is the end gate).
22812    /// Caller must have checked `fa_rows_eligible(base_len, head_dim)`.
22813    /// q is the verify's token-major [T, n_head, head_dim] stack; o is written [T, n_head, head_dim].
22814    #[allow(clippy::too_many_arguments)]
22815    pub fn fa_decode_rows(
22816        &self,
22817        q: &CudaSlice<f32>,
22818        k: &cudarc::driver::CudaView<u8>,
22819        v: &cudarc::driver::CudaView<u8>,
22820        o: &mut CudaSlice<f32>,
22821        head_dim: usize,
22822        n_head: usize,
22823        n_head_kv: usize,
22824        base_len: usize,
22825        t: usize,
22826        scale: f32,
22827        k_tok_bytes: usize,
22828        v_tok_bytes: usize,
22829        // hd512 dpl16 twin is DEVICE-LEN (graph arc): base_dev/plus feed the
22830        // kernel; host base_len keeps sizing the splits/partials. hd256 twins
22831        // keep the host arg. None is a bug for hd512 (asserted below).
22832        base_dev: Option<(&CudaSlice<i32>, i32)>,
22833        // K and V planes hold the same values (gemma globals, wv:=wk): pick
22834        // the _kv twin — V plane never read, value rides the q8_0 key dq.
22835        kv_shared: bool,
22836        // this layer's cache is e4m3 (gemma windowed under wkv): resolve the
22837        // hd256 rows kernel from the kf8vf8 module. PER-CALL — a global env
22838        // check here hijacked qwen/kernel-check hd256 rows (8 FAILs, 230ebbe).
22839        g: bool,
22840        // t=1 decode arm only: emit (int8, per-32 scales) from the dc combine
22841        // (hd512 path) — the standalone quantize launch folds away.
22842        mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
22843    ) -> Result<(), Box<dyn std::error::Error>> {
22844        debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
22845        let t_kv_max = base_len + t; // LAST row's key bound
22846        let mut sp = fa_split_keys(t_kv_max, n_head_kv); // env/default — same value every row
22847        // hd512 split override (MEMRA_FA_SP512, 2026-07-11): gemma globals have n_head_kv=2 so
22848        // the grid is (2 x n_splits) — at depth ~29 splits = 58 blocks on 82 SMs (half idle,
22849        // rows_dpl16 8x off its byte floor). EVERY gemma hd512 caller shares THIS wrapper
22850        // (parity law), so the partition is freely tunable — verify and decode move together.
22851        if head_dim == 512 {
22852            static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
22853            // default 16 (2026-07-11 depth sweep, N=2: plain 155.4->156.5, depth spec
22854            // 236.9->250.4; 12/24/32 all worse). hd512 exists only on gemma globals.
22855            let v = *SP512.get_or_init(|| {
22856                std::env::var("MEMRA_FA_SP512")
22857                    .ok()
22858                    .and_then(|x| x.parse().ok())
22859                    .unwrap_or(0)
22860            });
22861            sp = if v >= 8 {
22862                v
22863            } else {
22864                FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
22865            };
22866        }
22867        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
22868        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22869        let gqa = (n_head / n_head_kv).max(1) as u32;
22870        // LADDER-RUNG STRADDLE FIX (issue #10, 2026-07-13, rtx6000-proven): one sp for every row
22871        // diverges from eager decode when a split-ladder rung falls INSIDE the batch — row r's
22872        // eager twin used fa_split_keys(t_kv_r), the batch used fa_split_keys(t_kv_max), and
22873        // the different partition changes the combine's FP order (greedy tie flips at depth;
22874        // MEMRA_FA_SPLIT=64 pin -> PASS on the exact rtx6000 failing config). Fix: group
22875        // consecutive rows by their OWN ladder value and launch once per group — each row then
22876        // executes the exact per-row program eager ran. Rungs land once per doubling, so this
22877        // is 1 launch in the common case and 2 on a crossing round. hd512 keeps one group (its
22878        // sp override is t_kv-independent by construction).
22879        let mut groups: Vec<(usize, usize, usize)> = Vec::new(); // (row0, t_g, sp_g)
22880        if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
22881            groups.push((0, t, sp));
22882        } else {
22883            let mut r0 = 0usize;
22884            while r0 < t {
22885                let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
22886                let mut r1 = r0 + 1;
22887                while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
22888                    r1 += 1;
22889                }
22890                groups.push((r0, r1 - r0, sp_g));
22891                r0 = r1;
22892            }
22893        }
22894        // Deep-ctx smem twin for the VERIFY rows (2026-07-05): same threshold + rationale as
22895        // fa_decode's dispatch — at 40k the register path's GQA L2-reuse premise is dead and the
22896        // verify multiplies the 4x DRAM re-read by T rows. Bit-identical per (row,token,split).
22897        static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
22898        let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
22899            std::env::var("MEMRA_FA_SMEM_TKV")
22900                .ok()
22901                .and_then(|v| v.parse().ok())
22902                .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
22903        });
22904        let v4 = fa_v4_at(base_len + t) && head_dim == 256;
22905        let v3 = fa_v3_active(head_dim);
22906        let smem_rows =
22907            head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
22908        // kv_shared twin RETIRED (2026-07-11 depth run-gen gate): the wv:=wk premise fails
22909        // POST-cache — cached K is k-normed+roped, cached V is not; the twin fed roped keys
22910        // in as values. Verify/decode/stream gates were blind (both sides shared the wrong
22911        // symbol — the parity law's blind spot); only prefill-vs-decode at depth caught it.
22912        let _ = kv_shared;
22913        // i2 twin: 2-key interleaved walk (MEMRA_FA_I2=0 reverts). i4 probed NEGATIVE
22914        // (157.3 vs 161.2 depth plain — register pressure past i2's sweet spot; jsonl).
22915        let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
22916        // v4-hd512 (MEMRA_FA_V512=1 opt-in, 2026-07-14): the v4 key-per-lane recipe on the
22917        // globals lane (depth profile: i2 ~4.6x off its byte floor — the v3-class
22918        // reduce-per-key latency signature). NEW NUMERIC CONFIG shared by every hd512
22919        // caller (decode+verify flip together); run-gen argmax + acceptance arbitrate.
22920        // T-BATCHED hd512 (DEFAULT ON 2026-07-14, MEMRA_FA_TB512=0 seam): one block per
22921        // (kv_head, split) stages its tile once and loops the rows over it — kills the
22922        // x t DRAM re-read of the full-ctx globals (depth cell +1.4%, plain flat, N=3
22923        // interleaved). FIXED absolute partition = NEW NUMERIC for the combine order,
22924        // shared by every hd512 caller through this wrapper (decode+verify flip together;
22925        // depth stream identical, acceptance unshifted, spec 256/256 x3 models).
22926        // Requires sp <= 32 (single staged tile; acc reused per row). The z-form v4_512
22927        // sibling (in-kernel dp4a port alone) probed FLAT — hd512 was DRAM-re-read-bound,
22928        // not unpack-bound; jsonl 2026-07-14.
22929        static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
22930        // gqa <= 16 = fa_v4_smem_512's q-array capacity; past it fall to the register twins.
22931        let tb512 = head_dim == 512
22932            && sp <= 32
22933            && n_head / n_head_kv.max(1) <= 16
22934            && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
22935        let fname = if tb512 {
22936            "fa_decode_vec_q_rows_v4_512_tb"
22937        } else if i2 {
22938            "fa_decode_vec_q_rows_dpl16_i2"
22939        } else if head_dim == 512 {
22940            "fa_decode_vec_q_rows_dpl16"
22941        }
22942        // gemma globals (parity law)
22943        else if v4 {
22944            "fa_decode_vec_q_rows_v4"
22945        } else if v3 {
22946            "fa_decode_vec_q_rows_v3"
22947        } else if fa_v2_on() {
22948            "fa_decode_vec_q_rows_v2"
22949        } else if smem_rows {
22950            "fa_decode_vec_q_rows_smem"
22951        } else {
22952            "fa_decode_vec_q_rows"
22953        };
22954        let f = if head_dim == 512 {
22955            self.fa_func(fname, head_dim)
22956        } else if g {
22957            // FP8-WINDOWED: hd256 rows over an e4m3 cache — kf8vf8 module, SAME symbol
22958            // choice as decode's kvmod dispatch (parity law: excluding v4 here paired
22959            // g-module rows against decode's g-module v4 — different programs, short-VG
22960            // maxdiff 2.0 / spec stream 0/128, 2026-07-12). rows_v4 is format-aware
22961            // since fda9790; only the smem twin stays excluded (V-stage q5_1-only).
22962            // hd128 (qwen fp8-KV) lands on the base/register rows via fname — the
22963            // dq macros are format-aware.
22964            self.func_g(if smem_rows {
22965                "fa_decode_vec_q_rows"
22966            } else {
22967                fname
22968            })
22969        } else {
22970            self.func(fname)
22971        };
22972        let shmem = if tb512 {
22973            // fa_v4_smem_512 (q 9KB gqa<=16 + k tile 18KB) + sV 32*512 (e4m3 module halves it)
22974            let gk = Self::gkv_on();
22975            let sh =
22976                (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
22977            use cudarc::driver::sys::CUfunction_attribute_enum as A;
22978            f.set_attribute(
22979                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22980                sh as i32,
22981            )?;
22982            sh
22983        } else if v4 || v3 || smem_rows || fa_v2_on() {
22984            // v4: fa_v4_smem (11.5KB) + sV; v3 stages sV only; v2/smem twins stage sK+sV.
22985            let sh = (if v4 {
22986                11520 + 32 * head_dim * if g { 1 } else { 2 }
22987            } else if v3 {
22988                32 * head_dim * 2
22989            } else {
22990                2 * 32 * head_dim * 2
22991            }) as u32;
22992            use cudarc::driver::sys::CUfunction_attribute_enum as A;
22993            f.set_attribute(
22994                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22995                sh as i32,
22996            )?;
22997            sh
22998        } else {
22999            0
23000        };
23001        // Per-GROUP launches (single group in the common case — identical to the pre-fix
23002        // single launch there): each group gets its own partials (the rows kernel indexes
23003        // partials by its LOCAL grid.z row) and q/o row-offset views.
23004        for &(r0, t_g, sp_g) in &groups {
23005            let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
23006            let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
23007            let base_i = (base_len + r0) as i32;
23008            let o_len = t_g * n_head * n_splits_g * head_dim;
23009            let ml_len = t_g * n_head * n_splits_g;
23010            let mut part_guard = self.fa_part_pool.lock().unwrap();
23011            if part_guard
23012                .as_ref()
23013                .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
23014                .unwrap_or(true)
23015            {
23016                // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
23017                // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
23018                // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
23019                // later live allocations land at those addresses, and the next graph REPLAY writes
23020                // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
23021                // output corruption began the burst after the trunk's t_kv growth first realloc'd
23022                // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
23023                // the baked addresses alive (single-stream: eager writes the new buffers, replays
23024                // touch only the old — never concurrently). Doubling growth bounds retired VRAM
23025                // (total retired < final size).
23026                let old = part_guard.take();
23027                let (co, cm) = old
23028                    .as_ref()
23029                    .map(|pp| (pp.0.len(), pp.1.len()))
23030                    .unwrap_or((0, 0));
23031                if let Some(old) = old {
23032                    self.fa_part_retired.lock().unwrap().push(old);
23033                }
23034                if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
23035                    eprintln!(
23036                        "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
23037                        co, o_len, cm, ml_len
23038                    );
23039                }
23040                *part_guard =
23041                    Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
23042            }
23043            let pg = part_guard.as_mut().unwrap();
23044            self.gpu
23045                .stream()
23046                .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
23047            self.gpu
23048                .stream()
23049                .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
23050            self.gpu
23051                .stream()
23052                .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
23053            let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
23054            let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
23055            let qv = self.view(q, t * n_head * head_dim);
23056            let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
23057            let cfg = LaunchConfig {
23058                grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
23059                block_dim: (32, gqa, 1),
23060                shared_mem_bytes: shmem,
23061            };
23062            {
23063                let __s_b = self.gpu.stream();
23064                let mut b = __s_b.launch_builder(&f);
23065                if tb512 {
23066                    // rows-inner launch: grid.z dropped, the kernel loops n_rows itself.
23067                    let (bd, plus) =
23068                        base_dev.expect("hd512 rows twin requires a device base counter");
23069                    let plus_g = plus + r0 as i32;
23070                    let nr = t_g as i32;
23071                    if Self::pdl_on() && Self::pdl_wb_on() {
23072                        // wave-B2b: flavor mirrors fa_func(fname, 512) = gkv.
23073                        use cudarc::driver::{DevicePtr, DevicePtrMut};
23074                        let s = &self.gpu.stream();
23075                        let (pq, _b0) = q_g.device_ptr(s);
23076                        let (pk, _b1) = k.device_ptr(s);
23077                        let (pv, _b2) = v.device_ptr(s);
23078                        let (po, _b3) = part_o.device_ptr_mut(s);
23079                        let (pm, _b4) = part_m.device_ptr_mut(s);
23080                        let (pl, _b5) = part_l.device_ptr_mut(s);
23081                        let (pb, _b6) = bd.device_ptr(s);
23082                        let mut ps = [
23083                            &pq as *const _ as *mut std::ffi::c_void,
23084                            &pk as *const _ as *mut _,
23085                            &pv as *const _ as *mut _,
23086                            &po as *const _ as *mut _,
23087                            &pm as *const _ as *mut _,
23088                            &pl as *const _ as *mut _,
23089                            &hd as *const _ as *mut _,
23090                            &nh as *const _ as *mut _,
23091                            &nhkv as *const _ as *mut _,
23092                            &pb as *const _ as *mut _,
23093                            &plus_g as *const _ as *mut _,
23094                            &scale as *const _ as *mut _,
23095                            &nspm as *const _ as *mut _,
23096                            &spk as *const _ as *mut _,
23097                            &ktb as *const _ as *mut _,
23098                            &vtb as *const _ as *mut _,
23099                            &nr as *const _ as *mut _,
23100                        ];
23101                        unsafe {
23102                            self.launch_pdl_flash(
23103                                Self::gkv_on(),
23104                                "fa_decode_vec_q_rows_v4_512_tb",
23105                                (n_head_kv as u32, n_splits_g as u32, 1),
23106                                (32, gqa, 1),
23107                                shmem,
23108                                &mut ps,
23109                            )?;
23110                        }
23111                    } else {
23112                        let cfg_tb = LaunchConfig {
23113                            grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
23114                            block_dim: (32, gqa, 1),
23115                            shared_mem_bytes: shmem,
23116                        };
23117                        b.arg(&q_g)
23118                            .arg(k)
23119                            .arg(v)
23120                            .arg(&mut *part_o)
23121                            .arg(&mut *part_m)
23122                            .arg(&mut *part_l)
23123                            .arg(&hd)
23124                            .arg(&nh)
23125                            .arg(&nhkv)
23126                            .arg(bd)
23127                            .arg(&plus_g)
23128                            .arg(&scale)
23129                            .arg(&nspm)
23130                            .arg(&spk)
23131                            .arg(&ktb)
23132                            .arg(&vtb)
23133                            .arg(&nr);
23134                        unsafe {
23135                            b.launch(cfg_tb)?;
23136                        }
23137                    }
23138                } else if head_dim == 512 {
23139                    let (bd, plus) =
23140                        base_dev.expect("hd512 rows twin requires a device base counter");
23141                    let plus_g = plus + r0 as i32;
23142                    b.arg(&q_g)
23143                        .arg(k)
23144                        .arg(v)
23145                        .arg(&mut *part_o)
23146                        .arg(&mut *part_m)
23147                        .arg(&mut *part_l)
23148                        .arg(&hd)
23149                        .arg(&nh)
23150                        .arg(&nhkv)
23151                        .arg(bd)
23152                        .arg(&plus_g)
23153                        .arg(&scale)
23154                        .arg(&nspm)
23155                        .arg(&spk)
23156                        .arg(&ktb)
23157                        .arg(&vtb);
23158                    unsafe {
23159                        b.launch(cfg)?;
23160                    }
23161                } else {
23162                    b.arg(&q_g)
23163                        .arg(k)
23164                        .arg(v)
23165                        .arg(&mut *part_o)
23166                        .arg(&mut *part_m)
23167                        .arg(&mut *part_l)
23168                        .arg(&hd)
23169                        .arg(&nh)
23170                        .arg(&nhkv)
23171                        .arg(&base_i)
23172                        .arg(&scale)
23173                        .arg(&nspm)
23174                        .arg(&spk)
23175                        .arg(&ktb)
23176                        .arg(&vtb);
23177                    unsafe {
23178                        b.launch(cfg)?;
23179                    }
23180                }
23181            }
23182            let cfg2 = LaunchConfig {
23183                grid_dim: (n_head as u32, t_g as u32, 1),
23184                block_dim: (head_dim as u32, 1, 1),
23185                shared_mem_bytes: 0,
23186            };
23187            let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
23188            if head_dim == 512 {
23189                // device-len combine (shared by verify/eager/graph — parity by symbol): the
23190                // per-row n_splits derives from the SAME counter the rows kernel read.
23191                let (bd, plus) = base_dev.unwrap();
23192                let plus_g = plus + r0 as i32;
23193                if let Some((oq, od)) = q8_out.as_mut() {
23194                    // wave-5b port (2026-07-23, t=1 decode only): q8-emitting dc combine.
23195                    debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
23196                    if Self::pdl_on() && Self::pdl_wb_on() {
23197                        // wave-B2: flavor mirrors fa_func (hd512 + gkv → kf8vf8).
23198                        use cudarc::driver::{DevicePtr, DevicePtrMut};
23199                        let s = &self.gpu.stream();
23200                        let (po, _g0) = part_o.device_ptr(s);
23201                        let (pm, _g1) = part_m.device_ptr(s);
23202                        let (pl, _g2) = part_l.device_ptr(s);
23203                        let (pq, _g3) = oq.device_ptr_mut(s);
23204                        let (pd, _g4) = od.device_ptr_mut(s);
23205                        let (pb, _g5) = bd.device_ptr(s);
23206                        let mut ps = [
23207                            &po as *const _ as *mut std::ffi::c_void,
23208                            &pm as *const _ as *mut _,
23209                            &pl as *const _ as *mut _,
23210                            &pq as *const _ as *mut _,
23211                            &pd as *const _ as *mut _,
23212                            &hd as *const _ as *mut _,
23213                            &nh as *const _ as *mut _,
23214                            &pb as *const _ as *mut _,
23215                            &plus_g as *const _ as *mut _,
23216                            &nspm as *const _ as *mut _,
23217                            &spk as *const _ as *mut _,
23218                        ];
23219                        unsafe {
23220                            self.launch_pdl_flash(
23221                                Self::gkv_on(),
23222                                "fa_decode_combine_rows_dc_q8_1",
23223                                cfg2.grid_dim,
23224                                cfg2.block_dim,
23225                                0,
23226                                &mut ps,
23227                            )?;
23228                        }
23229                        continue;
23230                    }
23231                    let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
23232                    let __s_b2 = self.gpu.stream();
23233                    let mut b2 = __s_b2.launch_builder(&fc);
23234                    b2.arg(&*part_o)
23235                        .arg(&*part_m)
23236                        .arg(&*part_l)
23237                        .arg(&mut **oq)
23238                        .arg(&mut **od)
23239                        .arg(&hd)
23240                        .arg(&nh)
23241                        .arg(bd)
23242                        .arg(&plus_g)
23243                        .arg(&nspm)
23244                        .arg(&spk);
23245                    unsafe {
23246                        b2.launch(cfg2)?;
23247                    }
23248                    continue;
23249                }
23250                let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
23251                let __s_b2 = self.gpu.stream();
23252                let mut b2 = __s_b2.launch_builder(&fc);
23253                b2.arg(&*part_o)
23254                    .arg(&*part_m)
23255                    .arg(&*part_l)
23256                    .arg(&mut o_g)
23257                    .arg(&hd)
23258                    .arg(&nh)
23259                    .arg(bd)
23260                    .arg(&plus_g)
23261                    .arg(&nspm)
23262                    .arg(&spk);
23263                unsafe {
23264                    b2.launch(cfg2)?;
23265                }
23266            } else {
23267                // q8 emit is wired for the hd512 dc-combine arm only — a Some here would
23268                // leave the caller's pair unwritten (consumer would read garbage).
23269                assert!(
23270                    q8_out.is_none(),
23271                    "rows q8 emit requires the hd512 dc combine"
23272                );
23273                let fc = self.func("fa_decode_combine_rows");
23274                let __s_b2 = self.gpu.stream();
23275                let mut b2 = __s_b2.launch_builder(&fc);
23276                b2.arg(&*part_o)
23277                    .arg(&*part_m)
23278                    .arg(&*part_l)
23279                    .arg(&mut o_g)
23280                    .arg(&hd)
23281                    .arg(&nh)
23282                    .arg(&base_i)
23283                    .arg(&nspm)
23284                    .arg(&spk);
23285                unsafe {
23286                    b2.launch(cfg2)?;
23287                }
23288            }
23289        }
23290        Ok(())
23291    }
23292
23293    /// WINDOWED verify rows (gemma R6 deep-ctx): every row attends exactly `window` keys —
23294    /// bit-identical per row to the T=1 decode's fa_decode over the window VIEW. Caller gates
23295    /// base_len + 1 >= window (no under-window rows) and head_dim == 256 (v4 stamp).
23296    #[allow(clippy::too_many_arguments)]
23297    pub fn fa_decode_rows_w(
23298        &self,
23299        q: &CudaSlice<f32>,
23300        k: &cudarc::driver::CudaView<u8>,
23301        v: &cudarc::driver::CudaView<u8>,
23302        o: &mut CudaSlice<f32>,
23303        head_dim: usize,
23304        n_head: usize,
23305        n_head_kv: usize,
23306        base_dev: &CudaSlice<i32>,
23307        base_plus: i32,
23308        t: usize,
23309        scale: f32,
23310        window: usize,
23311        k_tok_bytes: usize,
23312        v_tok_bytes: usize,
23313        q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
23314    ) -> Result<(), Box<dyn std::error::Error>> {
23315        // DEVICE-LEN (graph arc step 1, 2026-07-11): the causal base rides an i32 counter
23316        // (kernel T_kv = dev[0] + base_plus + r + 1) so depth graphs can replay with len
23317        // advancing on-device. dc paths pass kvl.len_d with plus=-1; verify/eager sync the
23318        // counter with one async set_i32_one first. Partials/splits size from `window` (host).
23319        debug_assert!(head_dim == 256);
23320        // windowed split (MEMRA_FA_SPW, default 32 — re-swept 2026-07-12 under the raw-e4m3 sV
23321        // occupancy ceiling (4 blocks/SM): t=1 decode is GRID-limited (win/sp splits x nkv
23322        // blocks), so smaller splits fill the ceiling — 1.7k 174.4/174.0 vs 48's 170.7/170.3,
23323        // 4.9k 159.8 vs 157.4 (N=2 interleaved, stable window). Spec serving prefers 64
23324        // (verify t=K+1 fills the grid via grid.z=t; depth K=7 281.3 vs 249.3 at 32) — set
23325        // MEMRA_FA_SPW=64 there, same config law as MEMRA_GEMMA_GKV=0. MUST be one value for
23326        // ALL widths: a t-keyed probe broke decode-vs-verify combine order (stream 9/128).
23327        let sp = {
23328            static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
23329            let v = *SPW.get_or_init(|| {
23330                std::env::var("MEMRA_FA_SPW")
23331                    .ok()
23332                    .and_then(|x| x.parse().ok())
23333                    .unwrap_or(0)
23334            });
23335            if v >= 8 {
23336                v
23337            } else {
23338                FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
23339            }
23340        };
23341        let n_splits_max = (window + sp - 1) / sp;
23342        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
23343        let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
23344        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
23345        let gqa = (n_head / n_head_kv).max(1) as u32;
23346        let o_len = t * n_head * n_splits_max * head_dim;
23347        let ml_len = t * n_head * n_splits_max;
23348        let mut part_guard = self.fa_part_pool.lock().unwrap();
23349        if part_guard
23350            .as_ref()
23351            .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
23352            .unwrap_or(true)
23353        {
23354            // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
23355            // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
23356            // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
23357            // later live allocations land at those addresses, and the next graph REPLAY writes
23358            // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
23359            // output corruption began the burst after the trunk's t_kv growth first realloc'd
23360            // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
23361            // the baked addresses alive (single-stream: eager writes the new buffers, replays
23362            // touch only the old — never concurrently). Doubling growth bounds retired VRAM
23363            // (total retired < final size).
23364            let old = part_guard.take();
23365            let (co, cm) = old
23366                .as_ref()
23367                .map(|pp| (pp.0.len(), pp.1.len()))
23368                .unwrap_or((0, 0));
23369            if let Some(old) = old {
23370                self.fa_part_retired.lock().unwrap().push(old);
23371            }
23372            if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
23373                eprintln!(
23374                    "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
23375                    co, o_len, cm, ml_len
23376                );
23377            }
23378            *part_guard =
23379                Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
23380        }
23381        let pg = part_guard.as_mut().unwrap();
23382        self.gpu
23383            .stream()
23384            .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
23385        self.gpu
23386            .stream()
23387            .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
23388        self.gpu
23389            .stream()
23390            .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
23391        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
23392        // Lane pick: decode AND verify both land here in the windowed regime (parity law —
23393        // hybrid_forward verify_attn), so the pick only needs internal consistency, not
23394        // clone-of-decode bit fidelity (SASS-proven impossible for textually identical
23395        // kernels, jsonl 2026-07-10). v4 under the threshold; smem twin at/above the smem
23396        // floor (deep-ctx broadcast win); register twin between.
23397        static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
23398        let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
23399            std::env::var("MEMRA_FA_SMEM_TKV")
23400                .ok()
23401                .and_then(|v| v.parse().ok())
23402                .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
23403        });
23404        // MULTI-ROW v4: resurrected 2026-07-14 (the '33 tok/s collapse' was a paired-map
23405        // partial-write bug, not the mechanism) and falsified HONESTLY at gqa 2: bit-exact
23406        // but −1.7% on the 31B depth cell — the sp helper warp already hides staging
23407        // in-block, and mr trades L2-cheap redundant bytes for serialized per-warp gqa
23408        // score/B3 chains. Arm deleted; jsonl row 2026-07-14 is the record.
23409        use cudarc::driver::sys::CUfunction_attribute_enum as A;
23410        // FP8-WINDOWED (wkv): the v4 family is format-aware (2026-07-12 KFMT/VFMT staging
23411        // arms) — wkv rides the SAME lane logic, resolved from the kf8vf8 module. One symbol
23412        // per (lane, format-module) keeps parity structural; the old register-i2 detour
23413        // (-33%) is retired.
23414        let wg = Self::wkv_on();
23415        // STAGING-PARALLEL v4 (MEMRA_FA_SPW2, default ON at gqa==1): warp 1 = staging helper
23416        // (v4 is 61% staging); score phases identical to v4_w. Same symbol all t.
23417        let sp2 =
23418            gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
23419        if sp2 {
23420            let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
23421            if Self::pdl_on() && Self::pdl_wb_on() {
23422                // wave-B2b: flavor mirrors wg.
23423                use cudarc::driver::{DevicePtr, DevicePtrMut};
23424                let s = &self.gpu.stream();
23425                let (pq, _b0) = q.device_ptr(s);
23426                let (pk, _b1) = k.device_ptr(s);
23427                let (pv, _b2) = v.device_ptr(s);
23428                let (po, _b3) = part_o.device_ptr_mut(s);
23429                let (pm, _b4) = part_m.device_ptr_mut(s);
23430                let (pl, _b5) = part_l.device_ptr_mut(s);
23431                let (pb, _b6) = base_dev.device_ptr(s);
23432                let mut ps = [
23433                    &pq as *const _ as *mut std::ffi::c_void,
23434                    &pk as *const _ as *mut _,
23435                    &pv as *const _ as *mut _,
23436                    &po as *const _ as *mut _,
23437                    &pm as *const _ as *mut _,
23438                    &pl as *const _ as *mut _,
23439                    &hd as *const _ as *mut _,
23440                    &nh as *const _ as *mut _,
23441                    &nhkv as *const _ as *mut _,
23442                    &pb as *const _ as *mut _,
23443                    &base_plus as *const _ as *mut _,
23444                    &scale as *const _ as *mut _,
23445                    &nspm as *const _ as *mut _,
23446                    &spk as *const _ as *mut _,
23447                    &ktb as *const _ as *mut _,
23448                    &vtb as *const _ as *mut _,
23449                    &wini as *const _ as *mut _,
23450                ];
23451                unsafe {
23452                    self.launch_pdl_flash(
23453                        wg,
23454                        "fa_decode_vec_q_rows_v4_w_sp",
23455                        (n_head_kv as u32, n_splits_max as u32, t as u32),
23456                        (32, gqa + 1, 1),
23457                        sh,
23458                        &mut ps,
23459                    )?;
23460                }
23461            } else {
23462                let f = if wg {
23463                    self.func_g("fa_decode_vec_q_rows_v4_w_sp")
23464                } else {
23465                    self.func("fa_decode_vec_q_rows_v4_w_sp")
23466                };
23467                f.set_attribute(
23468                    A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
23469                    sh as i32,
23470                )?;
23471                let cfg = LaunchConfig {
23472                    grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
23473                    block_dim: (32, gqa + 1, 1),
23474                    shared_mem_bytes: sh,
23475                };
23476                let __s_b = self.gpu.stream();
23477                let mut b = __s_b.launch_builder(&f);
23478                b.arg(q)
23479                    .arg(k)
23480                    .arg(v)
23481                    .arg(&mut *part_o)
23482                    .arg(&mut *part_m)
23483                    .arg(&mut *part_l)
23484                    .arg(&hd)
23485                    .arg(&nh)
23486                    .arg(&nhkv)
23487                    .arg(base_dev)
23488                    .arg(&base_plus)
23489                    .arg(&scale)
23490                    .arg(&nspm)
23491                    .arg(&spk)
23492                    .arg(&ktb)
23493                    .arg(&vtb)
23494                    .arg(&wini);
23495                unsafe {
23496                    b.launch(cfg)?;
23497                }
23498            }
23499        } else {
23500            if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
23501                // wave-B2b: the v4_w pick only (smem/reg twins stay builder-launched).
23502                let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
23503                use cudarc::driver::{DevicePtr, DevicePtrMut};
23504                let s = &self.gpu.stream();
23505                let (pq, _b0) = q.device_ptr(s);
23506                let (pk, _b1) = k.device_ptr(s);
23507                let (pv, _b2) = v.device_ptr(s);
23508                let (po, _b3) = part_o.device_ptr_mut(s);
23509                let (pm, _b4) = part_m.device_ptr_mut(s);
23510                let (pl, _b5) = part_l.device_ptr_mut(s);
23511                let (pb, _b6) = base_dev.device_ptr(s);
23512                let mut ps = [
23513                    &pq as *const _ as *mut std::ffi::c_void,
23514                    &pk as *const _ as *mut _,
23515                    &pv as *const _ as *mut _,
23516                    &po as *const _ as *mut _,
23517                    &pm as *const _ as *mut _,
23518                    &pl as *const _ as *mut _,
23519                    &hd as *const _ as *mut _,
23520                    &nh as *const _ as *mut _,
23521                    &nhkv as *const _ as *mut _,
23522                    &pb as *const _ as *mut _,
23523                    &base_plus as *const _ as *mut _,
23524                    &scale as *const _ as *mut _,
23525                    &nspm as *const _ as *mut _,
23526                    &spk as *const _ as *mut _,
23527                    &ktb as *const _ as *mut _,
23528                    &vtb as *const _ as *mut _,
23529                    &wini as *const _ as *mut _,
23530                ];
23531                unsafe {
23532                    self.launch_pdl_flash(
23533                        wg,
23534                        "fa_decode_vec_q_rows_v4_w",
23535                        (n_head_kv as u32, n_splits_max as u32, t as u32),
23536                        (32, gqa, 1),
23537                        sh,
23538                        &mut ps,
23539                    )?;
23540                }
23541            } else {
23542                let pick = |name: &str| {
23543                    if wg {
23544                        self.func_g(name)
23545                    } else {
23546                        self.func(name)
23547                    }
23548                };
23549                let (f, sh) = if fa_v4_at(window) {
23550                    let f = pick("fa_decode_vec_q_rows_v4_w");
23551                    (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
23552                } else if smem_tkv > 0 && window >= smem_tkv {
23553                    // NOTE: the smem twin's V-stage is still q5_1-hardcoded — unreachable under wkv
23554                    // at the gemma window (v4 covers it); revisit if the smem floor ever drops.
23555                    (
23556                        pick("fa_decode_vec_q_rows_smem_w"),
23557                        (2 * 32 * head_dim * 2) as u32,
23558                    )
23559                } else {
23560                    (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
23561                };
23562                f.set_attribute(
23563                    A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
23564                    sh as i32,
23565                )?;
23566                let cfg = LaunchConfig {
23567                    grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
23568                    block_dim: (32, gqa, 1),
23569                    shared_mem_bytes: sh,
23570                };
23571                let __s_b = self.gpu.stream();
23572                let mut b = __s_b.launch_builder(&f);
23573                b.arg(q)
23574                    .arg(k)
23575                    .arg(v)
23576                    .arg(&mut *part_o)
23577                    .arg(&mut *part_m)
23578                    .arg(&mut *part_l)
23579                    .arg(&hd)
23580                    .arg(&nh)
23581                    .arg(&nhkv)
23582                    .arg(base_dev)
23583                    .arg(&base_plus)
23584                    .arg(&scale)
23585                    .arg(&nspm)
23586                    .arg(&spk)
23587                    .arg(&ktb)
23588                    .arg(&vtb)
23589                    .arg(&wini);
23590                unsafe {
23591                    b.launch(cfg)?;
23592                }
23593            }
23594        }
23595        let cfg2 = LaunchConfig {
23596            grid_dim: (n_head as u32, t as u32, 1),
23597            block_dim: (head_dim as u32, 1, 1),
23598            shared_mem_bytes: 0,
23599        };
23600        if let Some((oq, od)) = q8_out {
23601            // wave-5b port (2026-07-23): q8-emitting combine — the t=1 decode's wo matvec
23602            // consumes the pair directly; the standalone quantize launch folds away.
23603            if Self::pdl_on() && Self::pdl_wb_on() {
23604                // wave-B2: flavor mirrors the builder's wg choice.
23605                use cudarc::driver::{DevicePtr, DevicePtrMut};
23606                let s = &self.gpu.stream();
23607                let (po, _g0) = part_o.device_ptr(s);
23608                let (pm, _g1) = part_m.device_ptr(s);
23609                let (pl, _g2) = part_l.device_ptr(s);
23610                let (pq, _g3) = oq.device_ptr_mut(s);
23611                let (pd, _g4) = od.device_ptr_mut(s);
23612                let mut ps = [
23613                    &po as *const _ as *mut std::ffi::c_void,
23614                    &pm as *const _ as *mut _,
23615                    &pl as *const _ as *mut _,
23616                    &pq as *const _ as *mut _,
23617                    &pd as *const _ as *mut _,
23618                    &hd as *const _ as *mut _,
23619                    &nh as *const _ as *mut _,
23620                    &nspm as *const _ as *mut _,
23621                    &spk as *const _ as *mut _,
23622                    &wini as *const _ as *mut _,
23623                ];
23624                unsafe {
23625                    self.launch_pdl_flash(
23626                        wg,
23627                        "fa_decode_combine_rows_w_q8_1",
23628                        cfg2.grid_dim,
23629                        cfg2.block_dim,
23630                        0,
23631                        &mut ps,
23632                    )?;
23633                }
23634                return Ok(());
23635            }
23636            let fc = if wg {
23637                self.func_g("fa_decode_combine_rows_w_q8_1")
23638            } else {
23639                self.func("fa_decode_combine_rows_w_q8_1")
23640            };
23641            let __s_b2 = self.gpu.stream();
23642            let mut b2 = __s_b2.launch_builder(&fc);
23643            b2.arg(&*part_o)
23644                .arg(&*part_m)
23645                .arg(&*part_l)
23646                .arg(oq)
23647                .arg(od)
23648                .arg(&hd)
23649                .arg(&nh)
23650                .arg(&nspm)
23651                .arg(&spk)
23652                .arg(&wini);
23653            unsafe {
23654                b2.launch(cfg2)?;
23655            }
23656            return Ok(());
23657        }
23658        let fc = if wg {
23659            self.func_g("fa_decode_combine_rows_w")
23660        } else {
23661            self.func("fa_decode_combine_rows_w")
23662        };
23663        let __s_b2 = self.gpu.stream();
23664        let mut b2 = __s_b2.launch_builder(&fc);
23665        b2.arg(&*part_o)
23666            .arg(&*part_m)
23667            .arg(&*part_l)
23668            .arg(o)
23669            .arg(&hd)
23670            .arg(&nh)
23671            .arg(&nspm)
23672            .arg(&spk)
23673            .arg(&wini);
23674        unsafe {
23675            b2.launch(cfg2)?;
23676        }
23677        Ok(())
23678    }
23679
23680    /// ROUND-STREAM stage (c): fa rows with the causal base from a device counter. Two lanes:
23681    /// v3 (qwen stream, fa_v3_active) and v4 (gemma hd256 burst — rows_v4_dc, g-module aware);
23682    /// `t_kv_upper` sizes splits/partials — the same one-sp-for-all-rows approximation class
23683    /// the host rows path already uses (battery-arbitrated); actual per-row bounds derive
23684    /// in-kernel from the counter (+ base_plus, v4 lane only — v3's kernel has no plus arg).
23685    #[allow(clippy::too_many_arguments)]
23686    pub fn fa_decode_rows_dc(
23687        &self,
23688        q: &CudaSlice<f32>,
23689        k: &cudarc::driver::CudaView<u8>,
23690        v: &cudarc::driver::CudaView<u8>,
23691        o: &mut CudaSlice<f32>,
23692        head_dim: usize,
23693        n_head: usize,
23694        n_head_kv: usize,
23695        base_dev: &CudaSlice<i32>,
23696        t_kv_upper: usize,
23697        t: usize,
23698        scale: f32,
23699        k_tok_bytes: usize,
23700        v_tok_bytes: usize,
23701        base_plus: i32,
23702        g: bool,
23703    ) -> Result<(), Box<dyn std::error::Error>> {
23704        let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
23705        assert!(
23706            v4 || fa_v3_active(head_dim),
23707            "stream fa rows requires the v3 or v4 lane"
23708        );
23709        assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
23710        if v4 {
23711            let sp = fa_split_keys(t_kv_upper, n_head_kv);
23712            let n_splits_max = (t_kv_upper + sp - 1) / sp;
23713            let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
23714            let (nspm, spk) = (n_splits_max as i32, sp as i32);
23715            let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
23716            let gqa = (n_head / n_head_kv).max(1) as u32;
23717            let o_len = t * n_head * n_splits_max * head_dim;
23718            let ml_len = t * n_head * n_splits_max;
23719            let mut part_guard = self.fa_part_pool.lock().unwrap();
23720            if part_guard
23721                .as_ref()
23722                .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
23723                .unwrap_or(true)
23724            {
23725                // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
23726                // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
23727                // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
23728                // later live allocations land at those addresses, and the next graph REPLAY writes
23729                // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
23730                // output corruption began the burst after the trunk's t_kv growth first realloc'd
23731                // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
23732                // the baked addresses alive (single-stream: eager writes the new buffers, replays
23733                // touch only the old — never concurrently). Doubling growth bounds retired VRAM
23734                // (total retired < final size).
23735                let old = part_guard.take();
23736                let (co, cm) = old
23737                    .as_ref()
23738                    .map(|pp| (pp.0.len(), pp.1.len()))
23739                    .unwrap_or((0, 0));
23740                if let Some(old) = old {
23741                    self.fa_part_retired.lock().unwrap().push(old);
23742                }
23743                if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
23744                    eprintln!(
23745                        "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
23746                        co, o_len, cm, ml_len
23747                    );
23748                }
23749                *part_guard =
23750                    Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
23751            }
23752            let pg = part_guard.as_mut().unwrap();
23753            self.gpu
23754                .stream()
23755                .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
23756            self.gpu
23757                .stream()
23758                .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
23759            self.gpu
23760                .stream()
23761                .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
23762            let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
23763            let f = if g {
23764                self.func_g("fa_decode_vec_q_rows_v4_dc")
23765            } else {
23766                self.func("fa_decode_vec_q_rows_v4_dc")
23767            };
23768            let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
23769            use cudarc::driver::sys::CUfunction_attribute_enum as A;
23770            f.set_attribute(
23771                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
23772                sh as i32,
23773            )?;
23774            let cfg = LaunchConfig {
23775                grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
23776                block_dim: (32, gqa, 1),
23777                shared_mem_bytes: sh,
23778            };
23779            let __s_b = self.gpu.stream();
23780            let mut b = __s_b.launch_builder(&f);
23781            b.arg(q)
23782                .arg(k)
23783                .arg(v)
23784                .arg(&mut *part_o)
23785                .arg(&mut *part_m)
23786                .arg(&mut *part_l)
23787                .arg(&hd)
23788                .arg(&nh)
23789                .arg(&nhkv)
23790                .arg(base_dev)
23791                .arg(&base_plus)
23792                .arg(&scale)
23793                .arg(&nspm)
23794                .arg(&spk)
23795                .arg(&ktb)
23796                .arg(&vtb);
23797            unsafe {
23798                b.launch(cfg)?;
23799            }
23800            let fc = self.func("fa_decode_combine_rows_dc");
23801            let cfg2 = LaunchConfig {
23802                grid_dim: (n_head as u32, t as u32, 1),
23803                block_dim: (head_dim as u32, 1, 1),
23804                shared_mem_bytes: 0,
23805            };
23806            let __s_b2 = self.gpu.stream();
23807            let mut b2 = __s_b2.launch_builder(&fc);
23808            b2.arg(&*part_o)
23809                .arg(&*part_m)
23810                .arg(&*part_l)
23811                .arg(o)
23812                .arg(&hd)
23813                .arg(&nh)
23814                .arg(base_dev)
23815                .arg(&base_plus)
23816                .arg(&nspm)
23817                .arg(&spk);
23818            unsafe {
23819                b2.launch(cfg2)?;
23820            }
23821            return Ok(());
23822        }
23823        let sp = fa_split_keys(t_kv_upper, n_head_kv);
23824        let n_splits_max = (t_kv_upper + sp - 1) / sp;
23825        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
23826        let (nspm, spk) = (n_splits_max as i32, sp as i32);
23827        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
23828        let gqa = (n_head / n_head_kv).max(1) as u32;
23829        let o_len = t * n_head * n_splits_max * head_dim;
23830        let ml_len = t * n_head * n_splits_max;
23831        let mut part_guard = self.fa_part_pool.lock().unwrap();
23832        if part_guard
23833            .as_ref()
23834            .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
23835            .unwrap_or(true)
23836        {
23837            // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
23838            // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
23839            // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
23840            // later live allocations land at those addresses, and the next graph REPLAY writes
23841            // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
23842            // output corruption began the burst after the trunk's t_kv growth first realloc'd
23843            // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
23844            // the baked addresses alive (single-stream: eager writes the new buffers, replays
23845            // touch only the old — never concurrently). Doubling growth bounds retired VRAM
23846            // (total retired < final size).
23847            let old = part_guard.take();
23848            let (co, cm) = old
23849                .as_ref()
23850                .map(|pp| (pp.0.len(), pp.1.len()))
23851                .unwrap_or((0, 0));
23852            if let Some(old) = old {
23853                self.fa_part_retired.lock().unwrap().push(old);
23854            }
23855            if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
23856                eprintln!(
23857                    "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
23858                    co, o_len, cm, ml_len
23859                );
23860            }
23861            *part_guard =
23862                Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
23863        }
23864        let pg = part_guard.as_mut().unwrap();
23865        self.gpu
23866            .stream()
23867            .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
23868        self.gpu
23869            .stream()
23870            .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
23871        self.gpu
23872            .stream()
23873            .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
23874        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
23875        let f = self.func("fa_decode_vec_q_rows_v3_dc");
23876        let sh = (32 * head_dim * 2) as u32;
23877        use cudarc::driver::sys::CUfunction_attribute_enum as A;
23878        f.set_attribute(
23879            A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
23880            sh as i32,
23881        )?;
23882        let cfg = LaunchConfig {
23883            grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
23884            block_dim: (32, gqa, 1),
23885            shared_mem_bytes: sh,
23886        };
23887        let __s_b = self.gpu.stream();
23888        let mut b = __s_b.launch_builder(&f);
23889        b.arg(q)
23890            .arg(k)
23891            .arg(v)
23892            .arg(&mut *part_o)
23893            .arg(&mut *part_m)
23894            .arg(&mut *part_l)
23895            .arg(&hd)
23896            .arg(&nh)
23897            .arg(&nhkv)
23898            .arg(base_dev)
23899            .arg(&scale)
23900            .arg(&nspm)
23901            .arg(&spk)
23902            .arg(&ktb)
23903            .arg(&vtb);
23904        unsafe {
23905            b.launch(cfg)?;
23906        }
23907        let fc = self.func("fa_decode_combine_rows_dc");
23908        let cfg2 = LaunchConfig {
23909            grid_dim: (n_head as u32, t as u32, 1),
23910            block_dim: (head_dim as u32, 1, 1),
23911            shared_mem_bytes: 0,
23912        };
23913        let plus0 = 0i32;
23914        let __s_b2 = self.gpu.stream();
23915        let mut b2 = __s_b2.launch_builder(&fc);
23916        b2.arg(&*part_o)
23917            .arg(&*part_m)
23918            .arg(&*part_l)
23919            .arg(o)
23920            .arg(&hd)
23921            .arg(&nh)
23922            .arg(base_dev)
23923            .arg(&plus0)
23924            .arg(&nspm)
23925            .arg(&spk);
23926        unsafe {
23927            b2.launch(cfg2)?;
23928        }
23929        Ok(())
23930    }
23931
23932    /// Device-counter variant of `fa_decode` (CUDA-GRAPH-PLAN Phase 2). The sequence length is read
23933    /// from `t_kv_dev[0]` (resident device i32[1]) for the attention loop bound + per-split key range;
23934    /// the GRID `n_splits` is sized for `bucket_max` (the bucket's max t_kv — baked at capture time).
23935    /// Empty splits (key range beyond the actual t_kv) write an empty partial (m=NEG_INF) so the
23936    /// shared combine skips them -> bit-correct for ANY actual t_kv <= bucket_max.
23937    ///
23938    /// BIT-IDENTITY (the gate): pass `bucket_max == actual_t_kv` and this reproduces `fa_decode`
23939    /// EXACTLY (same n_splits, same per, same split boundaries, same combine) while reading t_kv from
23940    /// device. Bucketing (bucket_max > t_kv) is for the future captured path and changes split
23941    /// grouping (different but mathematically-equal log-sum-exp merge).
23942    pub fn fa_decode_dc(
23943        &self,
23944        q: &CudaSlice<f32>,
23945        k: &cudarc::driver::CudaView<u8>,
23946        v: &cudarc::driver::CudaView<u8>,
23947        o: &mut CudaSlice<f32>,
23948        head_dim: usize,
23949        n_head: usize,
23950        n_head_kv: usize,
23951        t_kv_dev: &CudaSlice<i32>,
23952        bucket_max: usize,
23953        scale: f32,
23954        k_tok_bytes: usize,
23955        v_tok_bytes: usize,
23956        g: bool,
23957    ) -> Result<(), Box<dyn std::error::Error>> {
23958        self.fa_decode_dc_q8(
23959            q,
23960            k,
23961            v,
23962            o,
23963            head_dim,
23964            n_head,
23965            n_head_kv,
23966            t_kv_dev,
23967            bucket_max,
23968            scale,
23969            k_tok_bytes,
23970            v_tok_bytes,
23971            g,
23972            None,
23973        )
23974    }
23975
23976    /// `fa_decode_dc` with an optional q8_1 sink (wave 5b): when `q8_out` is given the
23977    /// combine emits (int8, per-32 scales) for the wo matmul_pre and skips the f32 O write.
23978    #[allow(clippy::too_many_arguments)]
23979    pub fn fa_decode_dc_q8(
23980        &self,
23981        q: &CudaSlice<f32>,
23982        k: &cudarc::driver::CudaView<u8>,
23983        v: &cudarc::driver::CudaView<u8>,
23984        o: &mut CudaSlice<f32>,
23985        head_dim: usize,
23986        n_head: usize,
23987        n_head_kv: usize,
23988        t_kv_dev: &CudaSlice<i32>,
23989        bucket_max: usize,
23990        scale: f32,
23991        k_tok_bytes: usize,
23992        v_tok_bytes: usize,
23993        g: bool,
23994        q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
23995    ) -> Result<(), Box<dyn std::error::Error>> {
23996        // The fa_vec gate + n_splits are sized from bucket_max (host, fixed at capture). The kernel
23997        // reads the ACTUAL t_kv from t_kv_dev for the per-split bound. DEFAULT-ON to MATCH the eager
23998        // `fa_decode` gate above — graph capture must mirror eager's kernel choice or the graph-vs-eager
23999        // bit-identity gate breaks. MEMRA_NO_FA_VEC forces scalar on BOTH paths in lockstep.
24000        // `g` = this layer's cache is e4m3 (gemma windowed under wkv) — every pick below must
24001        // mirror fa_decode_kvmod's g-routing or the graph diverges from eager (short/mid 1/96,
24002        // 2026-07-12).
24003        let mut fa_vec =
24004            std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
24005        if g && head_dim == 256 && !fa_v4_at(bucket_max) {
24006            fa_vec = false;
24007        } // mirror kvmod/geom
24008        let sp = fa_split_keys(bucket_max, n_head_kv);
24009        let n_splits = if fa_vec {
24010            ((bucket_max + sp - 1) / sp).max(1)
24011        } else {
24012            ((bucket_max + 255) / 256).max(1)
24013        };
24014        let o_len = n_head * n_splits * head_dim;
24015        let ml_len = n_head * n_splits;
24016        let mut part_guard = self.fa_part_pool.lock().unwrap();
24017        if part_guard
24018            .as_ref()
24019            .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
24020            .unwrap_or(true)
24021        {
24022            // RETIRE-ON-GROW, never free (#68 root cause, 2026-08-04): captured graphs (the
24023            // per-session persistent draft graph, the decode/prime graph doors) BAKE these pool
24024            // buffer addresses. Dropping the old buffers on grow returns them to the async pool,
24025            // later live allocations land at those addresses, and the next graph REPLAY writes
24026            // its fa partials over them — the ST serve-spec corruption (acceptance collapse +
24027            // output corruption began the burst after the trunk's t_kv growth first realloc'd
24028            // this pool past the draft-capture size; research/fp8ship-20260804). Retiring keeps
24029            // the baked addresses alive (single-stream: eager writes the new buffers, replays
24030            // touch only the old — never concurrently). Doubling growth bounds retired VRAM
24031            // (total retired < final size).
24032            let old = part_guard.take();
24033            let (co, cm) = old
24034                .as_ref()
24035                .map(|pp| (pp.0.len(), pp.1.len()))
24036                .unwrap_or((0, 0));
24037            if let Some(old) = old {
24038                self.fa_part_retired.lock().unwrap().push(old);
24039            }
24040            if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
24041                eprintln!(
24042                    "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
24043                    co, o_len, cm, ml_len
24044                );
24045            }
24046            *part_guard =
24047                Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
24048        }
24049        let pg = part_guard.as_mut().unwrap();
24050        self.gpu
24051            .stream()
24052            .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
24053        self.gpu
24054            .stream()
24055            .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
24056        self.gpu
24057            .stream()
24058            .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
24059        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
24060        let (hd, nh, nhkv, nsp) = (
24061            head_dim as i32,
24062            n_head as i32,
24063            n_head_kv as i32,
24064            n_splits as i32,
24065        );
24066        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
24067        let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
24068        // FA-DEEP pick keyed on bucket_max (the fa_v4_at precedent) — bit-identical twins,
24069        // so a threshold falling between t_kv and bucket_max cannot diverge eager-vs-graph.
24070        let deep = fa_vec
24071            && head_dim == 256
24072            && fa_v4_at(bucket_max)
24073            && !g
24074            && fa_deep_at(bucket_max)
24075            && !matches!(fa_v4_mode(), "noB3" | "stage");
24076        let (f, cfg) = if fa_vec
24077            && head_dim == 512
24078            && bucket_max >= {
24079                static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
24080                *FA512_MIN_DC.get_or_init(|| {
24081                    std::env::var("MEMRA_FA512_MIN")
24082                        .ok()
24083                        .and_then(|v| v.parse().ok())
24084                        .unwrap_or(512)
24085                })
24086            } {
24087            // gemma globals dc twin (mirror the eager dpl16 pick incl the crossover floor).
24088            let gqa = (n_head / n_head_kv).max(1) as u32;
24089            (
24090                self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
24091                LaunchConfig {
24092                    grid_dim: (n_head_kv as u32, n_splits as u32, 1),
24093                    block_dim: (32, gqa, 1),
24094                    shared_mem_bytes: 0,
24095                },
24096            )
24097        } else if fa_vec && head_dim == 512 {
24098            // under the 512 floor eager runs scalar — the SAME unified symbol, ctr non-null;
24099            // ns_eff in-kernel reproduces eager's ceil(t_kv/sp) partition for the LIVE len.
24100            let q_view = q.as_view();
24101            let mut o_view = o.as_view_mut();
24102            return self.fa_decode_scalar_unified(
24103                &q_view,
24104                k,
24105                v,
24106                &mut o_view,
24107                head_dim,
24108                n_head,
24109                n_head_kv,
24110                0,
24111                Some(t_kv_dev),
24112                scale,
24113                n_splits,
24114                sp,
24115                k_tok_bytes,
24116                v_tok_bytes,
24117                g,
24118                &mut *part_o,
24119                &mut *part_m,
24120                &mut *part_l,
24121                q8_out,
24122            );
24123        } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
24124            // gemma/qwen v4 dc twin (eager default lane) — capture must mirror eager's pick,
24125            // incl the g-module route + raw-e4m3 sV sizing.
24126            let gqa = (n_head / n_head_kv).max(1) as u32;
24127            let fv = if g {
24128                self.func_g("fa_decode_vec_q_v4_dc")
24129            } else if deep {
24130                self.func("fa_decode_vec_q_v4_deep_dc")
24131            } else {
24132                self.func("fa_decode_vec_q_v4_dc")
24133            };
24134            let shmem =
24135                (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
24136            use cudarc::driver::sys::CUfunction_attribute_enum as A;
24137            fv.set_attribute(
24138                A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
24139                shmem as i32,
24140            )?;
24141            (
24142                fv,
24143                LaunchConfig {
24144                    grid_dim: (n_head_kv as u32, n_splits as u32, 1),
24145                    block_dim: (32, gqa, 1),
24146                    shared_mem_bytes: shmem,
24147                },
24148            )
24149        } else if fa_vec && fa_v3_active(head_dim) {
24150            // FA v3 lane _dc twin: the captured graph must run the SAME walk body as eager
24151            // under MEMRA_FA_V3=1 (eager, rows-verify and graph switch together).
24152            let gqa = (n_head / n_head_kv).max(1) as u32;
24153            let fv = if g {
24154                self.func_g("fa_decode_vec_q_v3_dc")
24155            } else {
24156                self.func("fa_decode_vec_q_v3_dc")
24157            };
24158            let shmem = (32 * head_dim * 2) as u32; // sV bf16 [FA_DEC_TILE=32][hd]
24159            (
24160                fv,
24161                LaunchConfig {
24162                    grid_dim: (n_head_kv as u32, n_splits as u32, 1),
24163                    block_dim: (32, gqa, 1),
24164                    shared_mem_bytes: shmem,
24165                },
24166            )
24167        } else if fa_vec && fa_v2_on() {
24168            // FAVENDOR lane: v2 _dc twin — the captured graph must run the SAME walk body as
24169            // eager under MEMRA_FA_V2=1 or graph_decode_gate's bit-identity breaks (the flag is
24170            // a numeric config; eager, rows-verify and graph all switch together).
24171            let gqa = (n_head / n_head_kv).max(1) as u32;
24172            let fv = if g {
24173                self.func_g("fa_decode_vec_q_v2_dc")
24174            } else {
24175                self.func("fa_decode_vec_q_v2_dc")
24176            };
24177            let shmem = (2 * 32 * head_dim * 2) as u32; // sK+sV bf16 [FA_DEC_TILE=32][hd]
24178            (
24179                fv,
24180                LaunchConfig {
24181                    grid_dim: (n_head_kv as u32, n_splits as u32, 1),
24182                    block_dim: (32, gqa, 1),
24183                    shared_mem_bytes: shmem,
24184                },
24185            )
24186        } else if fa_vec {
24187            let gqa = (n_head / n_head_kv).max(1) as u32;
24188            // REGISTER-DEQUANT twin: zero dynamic smem (see fa_decode above).
24189            let fv = if g {
24190                self.func_g("fa_decode_vec_q_dc")
24191            } else {
24192                self.func("fa_decode_vec_q_dc")
24193            };
24194            (
24195                fv,
24196                LaunchConfig {
24197                    grid_dim: (n_head_kv as u32, n_splits as u32, 1),
24198                    block_dim: (32, gqa, 1),
24199                    shared_mem_bytes: 0,
24200                },
24201            )
24202        } else {
24203            let q_view = q.as_view();
24204            let mut o_view = o.as_view_mut();
24205            return self.fa_decode_scalar_unified(
24206                &q_view,
24207                k,
24208                v,
24209                &mut o_view,
24210                head_dim,
24211                n_head,
24212                n_head_kv,
24213                0,
24214                Some(t_kv_dev),
24215                scale,
24216                n_splits,
24217                if fa_vec { sp } else { 256 },
24218                k_tok_bytes,
24219                v_tok_bytes,
24220                g,
24221                &mut *part_o,
24222                &mut *part_m,
24223                &mut *part_l,
24224                q8_out,
24225            );
24226        };
24227        let ski = sp as i32; // one-partition law: the twins derive ns_eff from (T_kv, ski)
24228        let __s_b = self.gpu.stream();
24229        let mut b = __s_b.launch_builder(&f);
24230        b.arg(q)
24231            .arg(k)
24232            .arg(v)
24233            .arg(&mut *part_o)
24234            .arg(&mut *part_m)
24235            .arg(&mut *part_l)
24236            .arg(&hd)
24237            .arg(&nh)
24238            .arg(&nhkv)
24239            .arg(t_kv_dev)
24240            .arg(&scale)
24241            .arg(&nsp)
24242            .arg(&ski)
24243            .arg(&ktb)
24244            .arg(&vtb);
24245        unsafe {
24246            b.launch(cfg)?;
24247        }
24248        let cfg2 = LaunchConfig {
24249            grid_dim: (n_head as u32, 1, 1),
24250            block_dim: (head_dim as u32, 1, 1),
24251            shared_mem_bytes: 0,
24252        };
24253        if let Some((oq, od)) = q8_out {
24254            let fc = if g {
24255                self.func_g("fa_decode_combine_q8_1")
24256            } else {
24257                self.fa_func("fa_decode_combine_q8_1", head_dim)
24258            };
24259            let __s_b2 = self.gpu.stream();
24260            let mut b2 = __s_b2.launch_builder(&fc);
24261            b2.arg(&*part_o)
24262                .arg(&*part_m)
24263                .arg(&*part_l)
24264                .arg(oq)
24265                .arg(od)
24266                .arg(&hd)
24267                .arg(&nh)
24268                .arg(&nsp);
24269            unsafe {
24270                b2.launch(cfg2)?;
24271            }
24272            return Ok(());
24273        }
24274        let fc = if g {
24275            self.func_g("fa_decode_combine_f32")
24276        } else {
24277            self.fa_func("fa_decode_combine_f32", head_dim)
24278        };
24279        let __s_b2 = self.gpu.stream();
24280        let mut b2 = __s_b2.launch_builder(&fc);
24281        b2.arg(&*part_o)
24282            .arg(&*part_m)
24283            .arg(&*part_l)
24284            .arg(o)
24285            .arg(&hd)
24286            .arg(&nh)
24287            .arg(&nsp);
24288        unsafe {
24289            b2.launch(cfg2)?;
24290        }
24291        Ok(())
24292    }
24293
24294    /// _dcw append (t=1): physical write row = len_dev[0] - base_dev[0] in-kernel; follow
24295    /// with `inc_i32(len_dev)` on the same stream. Bit-identical bytes to the host-row append
24296    /// at equal rows.
24297    #[allow(clippy::too_many_arguments)]
24298    pub fn append_kv_quantized_dcw(
24299        &self,
24300        k_row: &CudaSlice<f32>,
24301        v_row: &CudaSlice<f32>,
24302        kc: &mut CudaSlice<u8>,
24303        vc: &mut CudaSlice<u8>,
24304        len_dev: &CudaSlice<i32>,
24305        base_dev: Option<&CudaSlice<i32>>,
24306        kv_dim_k: usize,
24307        kv_dim_v: usize,
24308        k_tok_bytes: usize,
24309        v_tok_bytes: usize,
24310    ) -> Result<(), Box<dyn std::error::Error>> {
24311        let f = self.func("append_quantize_kv_q8_0_q5_1_dcw");
24312        let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
24313        let cfg = LaunchConfig {
24314            grid_dim: (nblk, 1, 1),
24315            block_dim: (32, 1, 1),
24316            shared_mem_bytes: 0,
24317        };
24318        let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
24319        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
24320        let null: u64 = 0;
24321        let __s_b = self.gpu.stream();
24322        let mut b = __s_b.launch_builder(&f);
24323        b.arg(k_row).arg(v_row).arg(kc).arg(vc).arg(len_dev);
24324        match base_dev {
24325            Some(base) => {
24326                b.arg(base);
24327            }
24328            None => {
24329                b.arg(&null);
24330            }
24331        }
24332        b.arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
24333        unsafe {
24334            b.launch(cfg)?;
24335        }
24336        Ok(())
24337    }
24338
24339    /// Increment a device i32 counter (graph-capturable; the `inc_i32` kernel).
24340    pub fn inc_i32(&self, counter: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
24341        let f = self.func("inc_i32");
24342        let cfg = LaunchConfig {
24343            grid_dim: (1, 1, 1),
24344            block_dim: (1, 1, 1),
24345            shared_mem_bytes: 0,
24346        };
24347        let __s_b = self.gpu.stream();
24348        let mut b = __s_b.launch_builder(&f);
24349        b.arg(counter);
24350        unsafe {
24351            b.launch(cfg)?;
24352        }
24353        Ok(())
24354    }
24355
24356    /// Windowed device-counter fa decode (step TP graph increment A): the KV view derives
24357    /// entirely from device state — `len_dev` (staged length), `base_dev` (physical row of
24358    /// logical 0 after the last ring rebase; None reads as 0), and `window` (0 = global) — so
24359    /// a captured child replays with ZERO per-token node updates. v3-vec only (the default
24360    /// kernel class on this lane); callers keep eager below the vec floor and for any other
24361    /// class. Scratch comes from the engine's fa partial pool sized at `bucket_max` (for SWA
24362    /// layers pass min(bucket, window)); the pool's retire-on-grow keeps captured addresses
24363    /// alive across bucket growth.
24364    #[allow(clippy::too_many_arguments)]
24365    /// Retire-on-grow ensure for the fa partial pool (see the #68 comment on the eager
24366    /// twin). Split out so graph capture can pre-run it OUTSIDE the capture region — an
24367    /// alloc inside a captured section becomes a mem node, and child graphs reject those.
24368    /// THE ONE PLACE THE FA PARTIAL POOL IS ALLOCATED.
24369    ///
24370    /// Eight call sites grow this pool and all eight retire-on-grow correctly, but only ONE
24371    /// of them carried the `[fa-pool] grow` receipt, so that receipt under-reported grows by
24372    /// seven eighths and no grow could honestly be dated against a request. Routing every
24373    /// grower through here makes the count real. The receipt names the site so a ladder can
24374    /// be attributed, and stays bounded so a pathological ladder cannot flood a serving log.
24375    ///
24376    /// `MEMRA_FA_PART_ZERO=1` (DEFAULT OFF, diagnostic only) zeroes the fresh buffers. A grow
24377    /// hands every subsequent launch three UNINITIALIZED banks; if the poison is a combine
24378    /// reading a partial bank its producer never wrote, that makes every row and every head
24379    /// non-finite at once, which is the shape the level-2 bad-row bitmap reports at the
24380    /// global-attention join.
24381    ///
24382    /// READ IT IN ONE DIRECTION ONLY. Zeroed banks carry m = 0.0, not NEG_INF, so the
24383    /// empty-split no-op guard never engages: a bank that is entirely unwritten still
24384    /// combines to L = 0 and O/L = 0/0 = NaN. So **silence under this arm convicts the pool;
24385    /// continued trapping acquits nothing**, because only the PARTIALLY unwritten class (real
24386    /// splits beside stale zeroed ones) goes quiet. Discriminator, never a fix, and never a
24387    /// serving arm: where it does go quiet the output is still wrong, it just looks plausible.
24388    fn fa_part_alloc(
24389        &self,
24390        o_len: usize,
24391        ml_len: usize,
24392        co: usize,
24393        cm: usize,
24394    ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
24395        static GROWS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
24396        let n = GROWS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
24397        if n < 64 {
24398            eprintln!(
24399                "[fa-pool] grow #{n} dev={} o_len {co} -> {o_len} ml_len {cm} -> {ml_len} (retired kept, zero={})",
24400                self.ctx().ordinal(),
24401                fa_part_zero_on()
24402            );
24403        }
24404        let mut po = self.alloc_uninit::<f32>(o_len)?;
24405        let mut pm = self.alloc_uninit::<f32>(ml_len)?;
24406        let mut pl = self.alloc_uninit::<f32>(ml_len)?;
24407        if fa_part_zero_on() {
24408            self.gpu.stream().memset_zeros(&mut po)?;
24409            self.gpu.stream().memset_zeros(&mut pm)?;
24410            self.gpu.stream().memset_zeros(&mut pl)?;
24411        }
24412        Ok((po, pm, pl))
24413    }
24414
24415    fn fa_part_pool_grow(
24416        &self,
24417        part_guard: &mut Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>,
24418        o_len: usize,
24419        ml_len: usize,
24420    ) -> Result<(), Box<dyn std::error::Error>> {
24421        if part_guard
24422            .as_ref()
24423            .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
24424            .unwrap_or(true)
24425        {
24426            let old = part_guard.take();
24427            let (co, cm) = old
24428                .as_ref()
24429                .map(|pp| (pp.0.len(), pp.1.len()))
24430                .unwrap_or((0, 0));
24431            if let Some(old) = old {
24432                self.fa_part_retired.lock().unwrap().push(old);
24433            }
24434            // GROW RECEIPT. This pool is grow-only, retires-on-grow and never frees, and every
24435            // FA decode/verify launch in the process reads and writes it. A grow is therefore a
24436            // process-lifetime EVENT — new addresses, a retired buffer kept alive forever, and
24437            // a different partial layout — and it is invisible in every log we have. The step37
24438            // spec fault is clean for the first two or three requests of a process and then
24439            // poisons trunk layer 20 (research: MEMRA_SPEC_NAN_SCAN), which is exactly the
24440            // shape a mid-life pool grow would produce, so the grows have to be datable
24441            // against the requests. Cap raised from 8 after the first run measured FOUR
24442            // grows per device (380928 -> 761856 -> 1523712 -> 3047424): with two devices the
24443            // 8 slots were spent before any grow could be dated against a request, which was
24444            // the entire point of the receipt. Still bounded so a pathological ladder cannot
24445            // flood a serving log.
24446            *part_guard =
24447                Some(self.fa_part_alloc(o_len.max(2 * co), ml_len.max(2 * cm), co, cm)?);
24448        }
24449        Ok(())
24450    }
24451
24452    /// Pre-grow the fa partial pool for a dcw call at (n_head, bucket_max) geometry, from
24453    /// OUTSIDE any capture region. Idempotent and cheap when already big enough.
24454    pub fn fa_dcw_pool_ensure(
24455        &self,
24456        head_dim: usize,
24457        n_head: usize,
24458        n_head_kv: usize,
24459        bucket_max: usize,
24460    ) -> Result<(), Box<dyn std::error::Error>> {
24461        let sp = fa_split_keys(bucket_max, n_head_kv);
24462        let n_splits = ((bucket_max + sp - 1) / sp).max(1);
24463        let o_len = n_head * n_splits * head_dim;
24464        let ml_len = n_head * n_splits;
24465        let mut part_guard = self.fa_part_pool.lock().unwrap();
24466        self.fa_part_pool_grow(&mut part_guard, o_len, ml_len)
24467    }
24468
24469    /// T=2 dcw decode attention (MEMRA_SPEC_FA2): both verify columns' rows are ALREADY
24470    /// appended; one launch walks the KV stream once with two query rows (per-row causal
24471    /// bounds len-1 / len) and the per-row combine consumes each half of the partials.
24472    /// BIT-IDENTICAL per row to that row's own per-column launch under the equal-partition
24473    /// guard the caller enforces (ns_eff/per equal for both bounds; boundary rounds fall
24474    /// back per column). `q2` = [2, n_head, head_dim]; `o2` = [2, n_head*head_dim] gated
24475    /// outputs (the head gate fuses into the combine as in the t=1 path).
24476    #[allow(clippy::too_many_arguments)]
24477    pub fn fa_decode_dcw2(
24478        &self,
24479        q2: &CudaSlice<f32>,
24480        k_ring: &cudarc::driver::CudaView<u8>,
24481        v_ring: &cudarc::driver::CudaView<u8>,
24482        o2: &mut CudaSlice<f32>,
24483        head_dim: usize,
24484        n_head: usize,
24485        n_head_kv: usize,
24486        len_dev: &CudaSlice<i32>,
24487        base_dev: Option<&CudaSlice<i32>>,
24488        window: usize,
24489        bucket_max: usize,
24490        scale: f32,
24491        k_tok_bytes: usize,
24492        v_tok_bytes: usize,
24493        gate2: &CudaSlice<f32>,
24494    ) -> Result<(), Box<dyn std::error::Error>> {
24495        let fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
24496        if !fa_vec || head_dim > 256 || head_dim % 32 != 0 || !fa_v3_on() {
24497            return Err("fa_decode_dcw2 supports the default v3-vec class only".into());
24498        }
24499        let sp = fa_split_keys(bucket_max, n_head_kv);
24500        let n_splits = ((bucket_max + sp - 1) / sp).max(1);
24501        // Partials for BOTH rows: row-major halves.
24502        let o_len = 2 * n_head * n_splits * head_dim;
24503        let ml_len = 2 * n_head * n_splits;
24504        let mut part_guard = self.fa_part_pool.lock().unwrap();
24505        Self::fa_part_pool_grow(self, &mut part_guard, o_len, ml_len)?;
24506        let pg = part_guard.as_mut().unwrap();
24507        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
24508        let (hd, nh, nhkv, nsp) = (
24509            head_dim as i32,
24510            n_head as i32,
24511            n_head_kv as i32,
24512            n_splits as i32,
24513        );
24514        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
24515        let (ski, win) = (sp as i32, window as i32);
24516        let gqa = (n_head / n_head_kv).max(1) as u32;
24517        let smem = (32 * head_dim * 2) as u32;
24518        let f = self.func("fa_decode_vec_q_v3_dcw2");
24519        let cfg = LaunchConfig {
24520            grid_dim: (n_head_kv as u32, n_splits as u32, 1),
24521            block_dim: (32, gqa, 1),
24522            shared_mem_bytes: smem,
24523        };
24524        let null: u64 = 0;
24525        {
24526            let __s_b = self.gpu.stream();
24527            let mut b = __s_b.launch_builder(&f);
24528            b.arg(q2)
24529                .arg(k_ring)
24530                .arg(v_ring)
24531                .arg(&mut *part_o)
24532                .arg(&mut *part_m)
24533                .arg(&mut *part_l)
24534                .arg(&hd)
24535                .arg(&nh)
24536                .arg(&nhkv)
24537                .arg(len_dev);
24538            match base_dev {
24539                Some(base) => {
24540                    b.arg(base);
24541                }
24542                None => {
24543                    b.arg(&null);
24544                }
24545            }
24546            b.arg(&win)
24547                .arg(&scale)
24548                .arg(&nsp)
24549                .arg(&ski)
24550                .arg(&ktb)
24551                .arg(&vtb);
24552            unsafe {
24553                b.launch(cfg)?;
24554            }
24555        }
24556        // Per-row combine+gate: the t=1 combine kernel over each half (its `head` axis spans
24557        // 2*n_head rows laid out row-major, and the gate rows are stacked the same way), so
24558        // one launch covers both rows with the exact t=1 program per (row, head).
24559        let fc = {
24560            static CS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24561            if *CS.get_or_init(|| std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1")) {
24562                self.func("fa_decode_combine_gate_f32_s")
24563            } else {
24564                self.func("fa_decode_combine_gate_f32")
24565            }
24566        };
24567        let combine_shared = std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1");
24568        let nh2 = (2 * n_head) as i32;
24569        let cfg2 = LaunchConfig {
24570            grid_dim: ((2 * n_head) as u32, 1, 1),
24571            block_dim: (head_dim as u32, 1, 1),
24572            shared_mem_bytes: if combine_shared {
24573                (2 * n_splits * 4) as u32
24574            } else {
24575                0
24576            },
24577        };
24578        let __s_b2 = self.gpu.stream();
24579        let mut b2 = __s_b2.launch_builder(&fc);
24580        b2.arg(&*part_o)
24581            .arg(&*part_m)
24582            .arg(&*part_l)
24583            .arg(gate2)
24584            .arg(o2)
24585            .arg(&hd)
24586            .arg(&nh2)
24587            .arg(&nsp);
24588        unsafe {
24589            b2.launch(cfg2)?;
24590        }
24591        Ok(())
24592    }
24593
24594    /// T-ROW dcw decode attention over a per-row session table (the per-session
24595    /// distributed-KV primitive). `tab` = t entries of five u64 words {k_ring, v_ring,
24596    /// len_ptr, base_ptr, len_back}; every (row, head, split) block runs the t=1 dcw
24597    /// program verbatim with that row's ring/len/base and its own split geometry, so each
24598    /// row is bit-identical to its own per-row launch. The kernel embeds the big-rig
24599    /// split ladder, so this refuses when the ladder env overrides are armed or the rig
24600    /// is not the >=128-SM class. `q_rows` = [t, n_head, head_dim]; `o_rows` = [t,
24601    /// n_head*head_dim] gated; `gate_rows` = [t, n_head].
24602    #[allow(clippy::too_many_arguments)]
24603    pub fn fa_decode_dcw_rows(
24604        &self,
24605        q_rows: &CudaSlice<f32>,
24606        tab: &CudaSlice<u64>,
24607        o_rows: &mut CudaSlice<f32>,
24608        t: usize,
24609        head_dim: usize,
24610        n_head: usize,
24611        n_head_kv: usize,
24612        window: usize,
24613        max_ns: usize,
24614        scale: f32,
24615        k_tok_bytes: usize,
24616        v_tok_bytes: usize,
24617        gate_rows: &CudaSlice<f32>,
24618    ) -> Result<(), Box<dyn std::error::Error>> {
24619        if std::env::var("MEMRA_NO_FA_VEC").is_ok()
24620            || head_dim > 256
24621            || head_dim % 32 != 0
24622            || !fa_v3_on()
24623        {
24624            return Err("fa_decode_dcw_rows supports the default v3-vec class only".into());
24625        }
24626        if fa_sm_count() < 128
24627            || std::env::var("MEMRA_FA_SPLIT").is_ok()
24628            || std::env::var("MEMRA_FA_SP_SHORT").is_ok()
24629            || std::env::var("MEMRA_FA_SP16").is_ok()
24630        {
24631            return Err(
24632                "fa_decode_dcw_rows embeds the big-rig split ladder; env split overrides \
24633                 (or a <128-SM rig) keep the per-row path"
24634                    .into(),
24635            );
24636        }
24637        if t == 0 || t > 32 || max_ns == 0 || tab.len() < t * 6 {
24638            return Err("fa_decode_dcw_rows geometry".into());
24639        }
24640        let o_len = t * n_head * max_ns * head_dim;
24641        let ml_len = t * n_head * max_ns;
24642        let mut part_guard = self.fa_part_pool.lock().unwrap();
24643        Self::fa_part_pool_grow(self, &mut part_guard, o_len, ml_len)?;
24644        let pg = part_guard.as_mut().unwrap();
24645        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
24646        let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
24647        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
24648        let (win, mns) = (window as i32, max_ns as i32);
24649        let gqa = (n_head / n_head_kv).max(1) as u32;
24650        let smem = (32 * head_dim * 2) as u32;
24651        let f = self.func("fa_decode_vec_q_v3_dcw_rows");
24652        let cfg = LaunchConfig {
24653            grid_dim: (n_head_kv as u32, max_ns as u32, t as u32),
24654            block_dim: (32, gqa, 1),
24655            shared_mem_bytes: smem,
24656        };
24657        {
24658            let __s_b = self.gpu.stream();
24659            let mut b = __s_b.launch_builder(&f);
24660            b.arg(q_rows)
24661                .arg(tab)
24662                .arg(&mut *part_o)
24663                .arg(&mut *part_m)
24664                .arg(&mut *part_l)
24665                .arg(&hd)
24666                .arg(&nh)
24667                .arg(&nhkv)
24668                .arg(&win)
24669                .arg(&scale)
24670                .arg(&mns)
24671                .arg(&ktb)
24672                .arg(&vtb);
24673            unsafe {
24674                b.launch(cfg)?;
24675            }
24676        }
24677        // Per-(row, head) combine+gate: the t=1 combine over t*n_head stacked heads —
24678        // row r head h reads its own partial bank; splits past a row's ns_eff carry
24679        // (-inf, 0) partials the NEG_INF guard no-ops bit-exactly.
24680        let fc = {
24681            static CS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24682            if *CS.get_or_init(|| std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1")) {
24683                self.func("fa_decode_combine_gate_f32_s")
24684            } else {
24685                self.func("fa_decode_combine_gate_f32")
24686            }
24687        };
24688        let combine_shared = std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1");
24689        let nht = (t * n_head) as i32;
24690        let cfg2 = LaunchConfig {
24691            grid_dim: ((t * n_head) as u32, 1, 1),
24692            block_dim: (head_dim as u32, 1, 1),
24693            shared_mem_bytes: if combine_shared {
24694                (2 * max_ns * 4) as u32
24695            } else {
24696                0
24697            },
24698        };
24699        let __s_b2 = self.gpu.stream();
24700        let mut b2 = __s_b2.launch_builder(&fc);
24701        b2.arg(&*part_o)
24702            .arg(&*part_m)
24703            .arg(&*part_l)
24704            .arg(gate_rows)
24705            .arg(o_rows)
24706            .arg(&hd)
24707            .arg(&nht)
24708            .arg(&mns);
24709        unsafe {
24710            b2.launch(cfg2)?;
24711        }
24712        Ok(())
24713    }
24714
24715    pub fn fa_decode_dcw(
24716        &self,
24717        q: &CudaSlice<f32>,
24718        k_ring: &cudarc::driver::CudaView<u8>,
24719        v_ring: &cudarc::driver::CudaView<u8>,
24720        o: &mut CudaSlice<f32>,
24721        head_dim: usize,
24722        n_head: usize,
24723        n_head_kv: usize,
24724        len_dev: &CudaSlice<i32>,
24725        base_dev: Option<&CudaSlice<i32>>,
24726        window: usize,
24727        bucket_max: usize,
24728        scale: f32,
24729        k_tok_bytes: usize,
24730        v_tok_bytes: usize,
24731        // FUSION #2d: Some(gate_row) fuses the head gate into the combine (bit-identical,
24732        // one launch saved); `o` then receives the GATED output and the caller skips its
24733        // attn_head_gate call.
24734        fused_gate: Option<&CudaSlice<f32>>,
24735    ) -> Result<(), Box<dyn std::error::Error>> {
24736        let fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
24737        if !fa_vec || head_dim > 256 || head_dim % 32 != 0 || !fa_v3_on() {
24738            return Err("fa_decode_dcw supports the default v3-vec class only                         (bucket >= vec floor, head_dim <= 256, MEMRA_FA_V3 on);                         keep eager outside it"
24739                .into());
24740        }
24741        let sp = fa_split_keys(bucket_max, n_head_kv);
24742        let n_splits = ((bucket_max + sp - 1) / sp).max(1);
24743        let o_len = n_head * n_splits * head_dim;
24744        let ml_len = n_head * n_splits;
24745        let mut part_guard = self.fa_part_pool.lock().unwrap();
24746        Self::fa_part_pool_grow(self, &mut part_guard, o_len, ml_len)?;
24747        let pg = part_guard.as_mut().unwrap();
24748        // MEMRA_FA_DCW_MEMSET=0: skip the partial-pool zeroing — every (head, split) in
24749        // [0, nsp) writes its partial before the combine reads it (per = ceil(len/nsp), so
24750        // split s starts at s*per < len for all s < nsp), making the zeros dead stores.
24751        // Door-gated pending the identity battery; =0 saves 3 memset launches/rank/layer.
24752        static MEMSET_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24753        // Token-graph capture ALWAYS keeps the memsets: the retarget path (increment C)
24754        // finds the attention children BY their three-memset signature and updates the
24755        // memset widths per bucket — capturing without them silently kills retargeting
24756        // (battery-v8 token drift, 2026-08-21).
24757        let memset_on = *MEMSET_ON
24758            .get_or_init(|| std::env::var("MEMRA_FA_DCW_MEMSET").as_deref() != Ok("0"))
24759            || crate::tp::token_graph_building();
24760        if memset_on {
24761            self.gpu
24762                .stream()
24763                .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
24764            self.gpu
24765                .stream()
24766                .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
24767            self.gpu
24768                .stream()
24769                .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
24770        }
24771        let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
24772        let (hd, nh, nhkv, nsp) = (
24773            head_dim as i32,
24774            n_head as i32,
24775            n_head_kv as i32,
24776            n_splits as i32,
24777        );
24778        let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
24779        let (ski, win) = (sp as i32, window as i32);
24780        let gqa = (n_head / n_head_kv).max(1) as u32;
24781        let smem = (32 * head_dim * 2) as u32; // sV bf16 [FA_DEC_TILE=32][hd] (v3 uses sV only)
24782        // MEMRA_FA_UNROLL=8: the B1-unroll-8 twin (deeper K load pipeline, bit-identical —
24783        // see fa_dec_v3_walk_u). Same launch geometry.
24784        static U8: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24785        static HOIST: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
24786        let hoist = *HOIST.get_or_init(|| match std::env::var("MEMRA_FA_HOIST").as_deref() {
24787            Ok("2") => 2,
24788            Ok("1") => 1,
24789            _ => 0,
24790        });
24791        // MEMRA_FA_PROF=1: clock64() phase profile of the decode-attention walk. ncu is
24792        // permission-blocked in this container and the module params are not exposed, so this
24793        // is how the ~1.18us/key gets localised. Diagnostic only (extra atomics per block);
24794        // prints cumulative cycle shares every 430 launches.
24795        static FPROF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24796        let fprof = *FPROF.get_or_init(|| std::env::var("MEMRA_FA_PROF").as_deref() == Ok("1"));
24797        static PROF_BUF: std::sync::Mutex<Option<(usize, CudaSlice<u64>)>> =
24798            std::sync::Mutex::new(None);
24799        // MEMRA_FA_HSPLIT=2: split each kv_head's gqa warp group across TWO blocks (2x grid,
24800        // duplicated Phase A staging) — bit-identical per (head, split). Tests whether B1's
24801        // 59-63% cycle share is occupancy-starved latency (grid is only n_head_kv x n_splits).
24802        static HS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24803        let hs2 = *HS.get_or_init(|| std::env::var("MEMRA_FA_HSPLIT").as_deref() == Ok("2"))
24804            && (n_head / n_head_kv) % 2 == 0
24805            && (n_head / n_head_kv) >= 2;
24806        let f = if fprof {
24807            self.func("fa_decode_vec_q_v3_dcw_prof")
24808        } else if hs2 {
24809            self.func("fa_decode_vec_q_v3_dcw_hs2")
24810        } else if hoist == 2 {
24811            // + typed 4-byte K loads (memcpy from uint8_t* can lower to byte loads).
24812            self.func("fa_decode_vec_q_v3_dcw_hc")
24813        } else if hoist == 1 {
24814            // Loop-invariant K alignment class hoisted out of B1 (bit-identical).
24815            self.func("fa_decode_vec_q_v3_dcw_h")
24816        } else if *U8.get_or_init(|| std::env::var("MEMRA_FA_UNROLL").as_deref() == Ok("8")) {
24817            self.func("fa_decode_vec_q_v3_dcw_u8")
24818        } else {
24819            self.func("fa_decode_vec_q_v3_dcw")
24820        };
24821        let cfg = LaunchConfig {
24822            grid_dim: if hs2 {
24823                ((2 * n_head_kv) as u32, n_splits as u32, 1)
24824            } else {
24825                (n_head_kv as u32, n_splits as u32, 1)
24826            },
24827            block_dim: if hs2 { (32, gqa / 2, 1) } else { (32, gqa, 1) },
24828            shared_mem_bytes: smem,
24829        };
24830        let null: u64 = 0;
24831        let __s_b = self.gpu.stream();
24832        let mut b = __s_b.launch_builder(&f);
24833        b.arg(q)
24834            .arg(k_ring)
24835            .arg(v_ring)
24836            .arg(&mut *part_o)
24837            .arg(&mut *part_m)
24838            .arg(&mut *part_l)
24839            .arg(&hd)
24840            .arg(&nh)
24841            .arg(&nhkv)
24842            .arg(len_dev);
24843        match base_dev {
24844            Some(base) => {
24845                b.arg(base);
24846            }
24847            None => {
24848                b.arg(&null);
24849            }
24850        }
24851        b.arg(&win)
24852            .arg(&scale)
24853            .arg(&nsp)
24854            .arg(&ski)
24855            .arg(&ktb)
24856            .arg(&vtb);
24857        if fprof {
24858            let mut guard = PROF_BUF.lock().map_err(|_| "fa prof buffer lock")?;
24859            if guard
24860                .as_ref()
24861                .is_none_or(|(d, _)| *d != self.ctx().ordinal())
24862            {
24863                *guard = Some((self.ctx().ordinal(), self.htod_u64(&vec![0u64; 8])?));
24864            }
24865            let (_, buf) = guard.as_mut().expect("armed above");
24866            b.arg(&*buf);
24867            unsafe {
24868                b.launch(cfg)?;
24869            }
24870            static CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
24871            let n = CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1;
24872            if n % 430 == 0 {
24873                self.stream().synchronize()?;
24874                let h = self.dtoh_u64(buf)?;
24875                let phases = ["setup", "stageV", "b1_klo", "b2_soft", "sync", "b3_vacc"];
24876                let tot: u64 = h[..6].iter().sum();
24877                let mut line = format!("[fa-prof] calls={n} keys={} cycles={tot}", h[6]);
24878                for (i, name) in phases.iter().enumerate() {
24879                    let pct = if tot > 0 {
24880                        h[i] as f64 / tot as f64 * 100.0
24881                    } else {
24882                        0.0
24883                    };
24884                    line.push_str(&format!(" {name}={pct:.1}%"));
24885                }
24886                if h[6] > 0 {
24887                    line.push_str(&format!(" cyc/key={:.0}", tot as f64 / h[6] as f64));
24888                }
24889                eprintln!("{line}");
24890            }
24891        } else {
24892            unsafe {
24893                b.launch(cfg)?;
24894            }
24895        }
24896        let mut combine_shared = false;
24897        let fc = if fused_gate.is_some() {
24898            // MEMRA_FA_COMBINE_S=1: shared-staged split metadata (bit-identical; kills the
24899            // n_splits-deep dependent global load chain every thread used to walk twice).
24900            static CS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24901            if *CS.get_or_init(|| std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1")) {
24902                combine_shared = true;
24903                self.func("fa_decode_combine_gate_f32_s")
24904            } else {
24905                self.func("fa_decode_combine_gate_f32")
24906            }
24907        } else {
24908            self.fa_func("fa_decode_combine_f32", head_dim)
24909        };
24910        let cfg2 = LaunchConfig {
24911            grid_dim: (n_head as u32, 1, 1),
24912            block_dim: (head_dim as u32, 1, 1),
24913            shared_mem_bytes: if combine_shared {
24914                (2 * n_splits * 4) as u32
24915            } else {
24916                0
24917            },
24918        };
24919        let __s_b2 = self.gpu.stream();
24920        let mut b2 = __s_b2.launch_builder(&fc);
24921        b2.arg(&*part_o).arg(&*part_m).arg(&*part_l);
24922        if let Some(gate_row) = fused_gate {
24923            b2.arg(gate_row);
24924        }
24925        b2.arg(o).arg(&hd).arg(&nh).arg(&nsp);
24926        unsafe {
24927            b2.launch(cfg2)?;
24928        }
24929        Ok(())
24930    }
24931
24932    /// EAGER fa_decode geometry for a given actual `t_kv` (CUDA-GRAPH-PLAN §3.3 bucketing). Returns
24933    /// `(fa_vec, n_splits)` EXACTLY as `fa_decode` computes them so the graph-capture path can key its
24934    /// bucket on the same `(kernel, n_splits)` pair and pass a `bucket_max` that reproduces eager's
24935    /// n_splits bit-for-bit. (Per = ceil(t_kv/n_splits) is then recomputed from the DEVICE t_kv inside
24936    /// the kernel and matches eager when n_splits matches — the bit-identity contract.)
24937    pub fn fa_geom_eager(
24938        &self,
24939        t_kv: usize,
24940        head_dim: usize,
24941        n_head_kv: usize,
24942        g: bool,
24943    ) -> (bool, usize) {
24944        // MUST mirror `fa_decode` / `fa_decode_dc` (default-ON 2026-06-28). This is the bucket-key
24945        // source: if it disagrees with the actual kernel pick, the graph captures the wrong path and
24946        // replay diverges from eager. All three sites read MEMRA_NO_FA_VEC in lockstep.
24947        let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
24948        // hd512 dpl16 vec lane (gemma globals, 2026-07-11 graph-arc fix): the original key
24949        // hardcoded vec = hd<=256, so for hd512 it bucketed by the SCALAR 256-key splits while
24950        // the dpl16/rows_dpl16 kernels split by the ladder — n_splits changed WITHIN a bucket
24951        // (mid-ctx graph mismatch at pos 19 + partials OOB at longer runs). Mirror the real
24952        // fa_decode dispatch: vec512 above the fa512 floor, vec256 as before.
24953        let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
24954        let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
24955        // g (fp8-windowed): mirror kvmod's clamp — only the v4 lane parses e4m3 in the vec
24956        // family; everything else falls to the g-module scalar.
24957        // hd256 under g: v4-or-scalar. hd128/other under g ride the REGISTER g-lane (the
24958        // dq_K_lane/dq_V_lane macros are format-aware; v3 is format-gated off, v2/smem
24959        // arms are excluded under g). Mirrored in kvmod / fa_decode_dc / fa_geom_eager.
24960        if g && head_dim == 256 && !fa_v4_at(t_kv) {
24961            fa_vec = false;
24962        }
24963        let sp = fa_split_keys(t_kv, n_head_kv);
24964        let n_splits = if fa_vec {
24965            ((t_kv + sp - 1) / sp).max(1)
24966        } else {
24967            ((t_kv + 255) / 256).max(1)
24968        };
24969        (fa_vec, n_splits)
24970    }
24971
24972    /// `bucket_max` (host t_kv to feed `fa_decode_dc` / `full_attn_decode_dc`) that makes the _dc
24973    /// kernel pick the SAME (fa_vec, n_splits) as eager would for actual `t_kv`. Because the dc
24974    /// launcher derives both from `bucket_max` via the same formulas, we just hand it `t_kv` itself:
24975    /// the n_splits is then identical, and the per-split boundaries (computed from the DEVICE t_kv in
24976    /// the kernel) match eager exactly. The bucket KEY (for the graph HashMap) is `(fa_vec, n_splits)`.
24977    pub fn fa_bucket_key(
24978        &self,
24979        t_kv: usize,
24980        head_dim: usize,
24981        n_head_kv: usize,
24982        g: bool,
24983    ) -> (bool, usize) {
24984        self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
24985    }
24986
24987    /// CUDA-graph capture wrapper (CUDA-GRAPH-PLAN §3.2, llama.cpp warmup pattern). Runs `step`
24988    /// inline TWICE (warmup — lets the caching allocator settle to stable pointers and any one-time
24989    /// kernel attribute/JIT happen outside capture), then captures a THIRD invocation on the Engine's
24990    /// decode stream (RELAXED mode) and instantiates it into a replayable `CudaGraph`. The closure
24991    /// must enqueue ONLY device work on `e.stream()` (no dtoh / no synchronize / no host branch on
24992    /// device data) — every per-step varying scalar must come from a device counter. Returns the
24993    /// instantiated graph; `CudaGraph::launch()` replays the whole step in one dispatch.
24994    /// `capture_graph` with CAPTURE-RETAIN: every Engine allocation made during the warmups
24995    /// and the capture is kept alive in the returned keeper — hold it as long as the graph
24996    /// replays (transients returning to the pool get reused by unrelated work and corrupt
24997    /// replays; the draft-graph root cause). Model-generic, next capture reuses it.
24998    pub fn capture_graph_retained<F>(
24999        &self,
25000        step: F,
25001    ) -> Result<
25002        (
25003            cudarc::driver::CudaGraph,
25004            Vec<Box<dyn std::any::Any + Send>>,
25005        ),
25006        Box<dyn std::error::Error>,
25007    >
25008    where
25009        F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
25010    {
25011        use cudarc::driver::sys::CUgraphInstantiate_flags;
25012        self.capture_graph_retained_flags(
25013            CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
25014            step,
25015        )
25016    }
25017
25018    /// Retained capture with an explicit instantiate flag. ALLOC-FREE captured graphs
25019    /// (zero mem nodes — the gemma slotted door) should pass UPLOAD instead of
25020    /// AUTO_FREE_ON_LAUNCH: the auto-free flag's launch-time mem-pool scan was measured at
25021    /// ~0.25us/node (205us on the 826-node step) even with nothing to free.
25022    pub fn capture_graph_retained_flags<F>(
25023        &self,
25024        flags: cudarc::driver::sys::CUgraphInstantiate_flags,
25025        mut step: F,
25026    ) -> Result<
25027        (
25028            cudarc::driver::CudaGraph,
25029            Vec<Box<dyn std::any::Any + Send>>,
25030        ),
25031        Box<dyn std::error::Error>,
25032    >
25033    where
25034        F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
25035    {
25036        use cudarc::driver::sys::CUstreamCaptureMode;
25037        // KEEP scope = WARMUPS ONLY (2026-07-13): keep_if_capturing retains via
25038        // CudaSlice::clone, which is a device ALLOC + D2D COPY on the stream — clones made
25039        // while the capture region is open become dead copy NODES replayed every launch
25040        // (E4B: 1440 copies = 0.74ms/token, the whole graph-vs-eager regression). The
25041        // warmup runs allocate the same transient sequence at the same pool addresses, so
25042        // retaining the warmup clones preserves the draft-graph fix without polluting the
25043        // captured graph.
25044        self.capture_keep.lock().unwrap().clear();
25045        let was_tracking = self.gpu.ctx.is_event_tracking();
25046        if was_tracking {
25047            unsafe {
25048                self.gpu.ctx.disable_event_tracking();
25049            }
25050        }
25051        let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
25052            self.capture_keep_on
25053                .store(true, std::sync::atomic::Ordering::Relaxed);
25054            let w = (|| {
25055                step(self)?;
25056                step(self)
25057            })();
25058            self.capture_keep_on
25059                .store(false, std::sync::atomic::Ordering::Relaxed);
25060            w?;
25061            self.gpu.stream().synchronize()?;
25062            self.gpu
25063                .stream()
25064                .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
25065            let r = step(self);
25066            let g = self.gpu.stream().end_capture(flags);
25067            r?;
25068            let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
25069            graph.upload()?;
25070            Ok(graph)
25071        };
25072        let result = run();
25073        self.capture_keep_on
25074            .store(false, std::sync::atomic::Ordering::Relaxed);
25075        if was_tracking {
25076            unsafe {
25077                self.gpu.ctx.enable_event_tracking();
25078            }
25079        }
25080        let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
25081        Ok((result?, keeper))
25082    }
25083
25084    /// Retained capture WITHOUT the two warmup executions. The warmups exist for transient
25085    /// pool-address stability (draft-graph lanes); the step TP token-graph sections are
25086    /// alloc-free with persistent operands, and their bodies carry device side effects
25087    /// (dcw KV appends + counter incs) that a warmup would REALLY EXECUTE — measured as a
25088    /// +2/rank len_d drift per bucket build that marched appends past the ring planes.
25089    pub fn capture_graph_retained_nowarm<F>(
25090        &self,
25091        mut step: F,
25092    ) -> Result<
25093        (
25094            cudarc::driver::CudaGraph,
25095            Vec<Box<dyn std::any::Any + Send>>,
25096        ),
25097        Box<dyn std::error::Error>,
25098    >
25099    where
25100        F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
25101    {
25102        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
25103        let was_tracking = self.gpu.ctx.is_event_tracking();
25104        if was_tracking {
25105            unsafe {
25106                self.gpu.ctx.disable_event_tracking();
25107            }
25108        }
25109        let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
25110            self.gpu.stream().synchronize()?;
25111            self.gpu
25112                .stream()
25113                .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
25114            let r = step(self);
25115            let g = self.gpu.stream().end_capture(
25116                CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
25117            );
25118            r?;
25119            let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
25120            graph.upload()?;
25121            Ok(graph)
25122        };
25123        let result = run();
25124        if was_tracking {
25125            unsafe {
25126                self.gpu.ctx.enable_event_tracking();
25127            }
25128        }
25129        Ok((result?, Vec::new()))
25130    }
25131
25132    pub fn capture_graph<F>(
25133        &self,
25134        mut step: F,
25135    ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
25136    where
25137        F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
25138    {
25139        use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
25140        // EVENT TRACKING OFF for capture. The Engine creates a 2nd stream (copy_stream) so cudarc is in
25141        // multi-stream mode and, by default, records a CudaEvent per CudaSlice alloc/use to serialize
25142        // cross-stream access. Those per-buffer event waits issue stream ops that are NOT permitted
25143        // inside a capture region (CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED). The captured decode step is
25144        // strictly SINGLE-STREAM (every kernel on gpu.stream), so this synchronization is unnecessary
25145        // here — disable it for the whole warmup+capture, re-enable after. SAFETY: the decode-dc path
25146        // touches only gpu.stream; no buffer crosses to copy_stream during capture.
25147        let was_tracking = self.gpu.ctx.is_event_tracking();
25148        if was_tracking {
25149            unsafe {
25150                self.gpu.ctx.disable_event_tracking();
25151            }
25152        }
25153        // Q1 PROBE (MEMRA_GRAPH_IFLAG): the generic capture body's cuMemAllocAsync nodes are
25154        // EXACTLY BALANCED by in-graph free nodes (measured census q27: 1589 ALLOC / 1589
25155        // FREE), so AUTO_FREE_ON_LAUNCH has nothing to reclaim at launch — it only pays its
25156        // per-node launch-time mem-pool scan. `upload` / `none` select the alternatives to
25157        // measure that scan's real cost on the generic path. Diagnostic door only; the
25158        // default stays AUTO_FREE until a measured A/B justifies moving it.
25159        let iflag = {
25160            static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
25161            *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
25162                // UPLOAD = the gemma slotted door's zero-mem-node choice; PRIORITY = the flag
25163                // hybrid_forward.rs:5935 actually ships (both drop the auto-free launch scan).
25164                Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
25165                Ok("priority") => {
25166                    CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
25167                }
25168                _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
25169            })
25170        };
25171        // MEMRA_GRAPH_CAPTIME=1 (Q1 lane): phase-resolved capture cost. Recapture is paid at
25172        // every kernel-class crossing, so it — not steady-state decode — is the quantity a
25173        // mem-node reduction could plausibly shrink. Only `instantiate` (cuStreamEndCapture +
25174        // cuGraphInstantiateWithFlags) and `upload` scale with node count; the warmups are
25175        // eager step executions and are node-count-invariant. Printing the split bounds the
25176        // refactor's ceiling instead of assuming it.
25177        let ct = {
25178            static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
25179            *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
25180        };
25181        // MEMRA_GRAPH_WARMUPS (Q1 lane; DEFAULT 1 since lane/graph-warmups 2026-08-05): the
25182        // phase split showed the eager warmups are 80% of recapture cost (q27 27.4 of 34.4 ms
25183        // pod / 42% of 52.6 ms 5090) — 3x larger than the ENTIRE mem-node ceiling the audit
25184        // chased, and node-count-invariant, so no capture-body refactor could touch it.
25185        // Warmup 2's theorized job was async-pool ADDRESS STABILITY: warmup 1's allocs may
25186        // grow/map the pool, warmup 2 re-walks the same sequence over the freed blocks so the
25187        // captured third run bakes settled addresses. That hazard is the #68 stale-baked-
25188        // address class — which the engine now guards STRUCTURALLY rather than by re-walking:
25189        // in-body transients are captured as BALANCED in-graph alloc/free node pairs (census
25190        // 1589/1589 — replays allocate for themselves; no baked transient pointers), every
25191        // externally-referenced buffer is stable-pointer by design (fa_part_pool retires-on-
25192        // grow and never frees, resident counters/scratch, cache set in place), and the
25193        // draft-graph path additionally rides capture_graph_retained (capture_keep holds all
25194        // warmup+capture allocs alive). One warmup therefore suffices for kernel-attr
25195        // settling and pool mapping. Arbitrated adversarially, not by taste:
25196        // graph-warmup-stress (pool-growth cycles large<->small x10, overlap arm, forced
25197        // recaptures over freed blocks — bit-identity vs eager + canary teeth) is GREEN at
25198        // warmups=1 on the deployment rig, plus graph-decode-gate 256-step bit-identity,
25199        // graph-session-gate, run-spec K=1..8 (receipts research/graph-warmups-5090-20260805/
25200        // + the pod's research/graph-allocfree-20260805/). Measured: recapture -38..-42% q27 /
25201        // -41% q9, decode +~1%, capture+prime -13ms. MEMRA_GRAPH_WARMUPS=2 = the rollback
25202        // seam; tools/graph-warmup-stress-gate.sh = the gate any regression re-runs.
25203        let warmups = {
25204            static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
25205            *W.get_or_init(|| {
25206                std::env::var("MEMRA_GRAPH_WARMUPS")
25207                    .ok()
25208                    .and_then(|v| v.parse().ok())
25209                    .filter(|n| *n >= 1)
25210                    .unwrap_or(1)
25211            })
25212        };
25213        let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
25214            let t_w = std::time::Instant::now();
25215            // warmup: inline runs (no capture) so allocator pointers + kernel attrs are stable.
25216            for _ in 0..warmups {
25217                step(self)?;
25218            }
25219            self.gpu.stream().synchronize()?;
25220            let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
25221            // capture the third run.
25222            let t_c = std::time::Instant::now();
25223            self.gpu
25224                .stream()
25225                .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
25226            // If the body errors mid-capture, end the capture before propagating so the stream isn't
25227            // left in a capturing state.
25228            let r = step(self);
25229            let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
25230            let t_i = std::time::Instant::now();
25231            let g = self.gpu.stream().end_capture(iflag);
25232            let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
25233            r?;
25234            let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
25235            let t_u = std::time::Instant::now();
25236            graph.upload()?;
25237            if ct {
25238                println!(
25239                    "[graph-captime] warmup2x {ms_warm:.2} ms  capture-body {ms_body:.2} ms  \
25240                          instantiate {ms_inst:.2} ms  upload {:.2} ms",
25241                    t_u.elapsed().as_secs_f64() * 1e3
25242                );
25243            }
25244            Ok(graph)
25245        };
25246        let result = run();
25247        if was_tracking {
25248            unsafe {
25249                self.gpu.ctx.enable_event_tracking();
25250            }
25251        }
25252        result
25253    }
25254
25255    /// gdn_scan variant where state_in/out are CudaViews (resident SSM state, in-place per step).
25256    pub fn gdn_scan_s128_view(
25257        &self,
25258        q: &CudaSlice<f32>,
25259        k: &CudaSlice<f32>,
25260        v: &CudaSlice<f32>,
25261        g: &CudaSlice<f32>,
25262        beta: &CudaSlice<f32>,
25263        state_in: &cudarc::driver::CudaView<f32>,
25264        state_out: &mut cudarc::driver::CudaViewMut<f32>,
25265        o: &mut CudaSlice<f32>,
25266        n_head: usize,
25267        t: usize,
25268        scale: f32,
25269    ) -> Result<(), Box<dyn std::error::Error>> {
25270        let f = self.func("gdn_scan_s128");
25271        const S_V: u32 = 128;
25272        const WARP: u32 = 32;
25273        const COLS: u32 = 4;
25274        let cfg = LaunchConfig {
25275            grid_dim: (n_head as u32, 1, S_V / COLS),
25276            block_dim: (WARP, COLS, 1),
25277            shared_mem_bytes: 0,
25278        };
25279        let (h, ti) = (n_head as i32, t as i32);
25280        let __s_b = self.gpu.stream();
25281        let mut b = __s_b.launch_builder(&f);
25282        b.arg(q)
25283            .arg(k)
25284            .arg(v)
25285            .arg(g)
25286            .arg(beta)
25287            .arg(state_in)
25288            .arg(state_out)
25289            .arg(o)
25290            .arg(&h)
25291            .arg(&ti)
25292            .arg(&scale);
25293        unsafe {
25294            b.launch(cfg)?;
25295        }
25296        Ok(())
25297    }
25298
25299    /// conv1d where the input is a CudaView (resident conv state assembled in place).
25300    pub fn ssm_conv1d_view(
25301        &self,
25302        x: &cudarc::driver::CudaView<f32>,
25303        w: &CudaSlice<f32>,
25304        y: &mut CudaSlice<f32>,
25305        conv_dim: usize,
25306        t: usize,
25307        d_conv: usize,
25308        silu: bool,
25309    ) -> Result<(), Box<dyn std::error::Error>> {
25310        let f = self.func("ssm_conv1d_silu_f32");
25311        // grid.x = channel, grid.y = T-tiles (block 256 strides over T) — parallel over both axes.
25312        let cfg = LaunchConfig {
25313            grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
25314            block_dim: (256, 1, 1),
25315            shared_mem_bytes: 0,
25316        };
25317        let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
25318        let __s_b = self.gpu.stream();
25319        let mut b = __s_b.launch_builder(&f);
25320        b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
25321        unsafe {
25322            b.launch(cfg)?;
25323        }
25324        Ok(())
25325    }
25326
25327    /// Depthwise causal conv1d + optional SiLU.
25328    /// x:[conv_dim, T+d_conv-1] channel-major (first d_conv-1 cols = carried state),
25329    /// w:[d_conv, conv_dim] kernel-major, y:[conv_dim, T] channel-major.
25330    /// FUSED prefill conv (token-major input, zero left-state): replaces
25331    /// transpose + zeros + conv_left_pad + ssm_conv1d with ONE launch reading the matmul output
25332    /// directly. Output channel-major [conv_dim, T], SiLU applied. BIT-IDENTICAL accumulation.
25333    pub fn ssm_conv1d_tm(
25334        &self,
25335        qkv_tm: &CudaSlice<f32>,
25336        w: &CudaSlice<f32>,
25337        y: &mut CudaSlice<f32>,
25338        conv_dim: usize,
25339        t: usize,
25340        d_conv: usize,
25341    ) -> Result<(), Box<dyn std::error::Error>> {
25342        let f = self.func("ssm_conv1d_tm_f32");
25343        let cfg = LaunchConfig {
25344            grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
25345            block_dim: (256, 1, 1),
25346            shared_mem_bytes: 0,
25347        };
25348        let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
25349        let __s_b = self.gpu.stream();
25350        let mut b = __s_b.launch_builder(&f);
25351        b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
25352        unsafe {
25353            b.launch(cfg)?;
25354        }
25355        Ok(())
25356    }
25357
25358    /// BATCHED verify conv (T>1, carried state): window reads the resident conv ring for
25359    /// negative rows; separate ring-update launch afterwards. BIT-IDENTICAL per value to the
25360    /// T=1 chain. T >= pad rides the pure input-column ring update (unchanged legacy path);
25361    /// T < pad (the MEMRA_SPEC_M2 t=2 verify arm) needs old-ring sources for the roll — the
25362    /// update kernel would race reading the ring it rewrites, so that arm clones the ring
25363    /// (dtod) and rolls via ssm_conv_ring_rebuild (PURE COPIES: the ring stores raw input
25364    /// columns; the final ring == what T sequential decode ring rolls leave).
25365    pub fn ssm_conv1d_tm_state(
25366        &self,
25367        qkv_tm: &CudaSlice<f32>,
25368        conv_state: &mut CudaSlice<f32>,
25369        w: &CudaSlice<f32>,
25370        y: &mut CudaSlice<f32>,
25371        conv_dim: usize,
25372        t: usize,
25373        d_conv: usize,
25374    ) -> Result<(), Box<dyn std::error::Error>> {
25375        self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
25376    }
25377
25378    /// task #14: `pad_len` = device true length for PADDED prime graphs — the ring update
25379    /// reads rows [len-pad, len) instead of the pad tail. None = the classic host-T path.
25380    #[allow(clippy::too_many_arguments)]
25381    pub fn ssm_conv1d_tm_state_pad(
25382        &self,
25383        qkv_tm: &CudaSlice<f32>,
25384        conv_state: &mut CudaSlice<f32>,
25385        w: &CudaSlice<f32>,
25386        y: &mut CudaSlice<f32>,
25387        conv_dim: usize,
25388        t: usize,
25389        d_conv: usize,
25390        pad_len: Option<&CudaSlice<i32>>,
25391    ) -> Result<(), Box<dyn std::error::Error>> {
25392        assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
25393        // clone BEFORE the window kernel is issued is not required (stream-ordered: the dtod and
25394        // the window kernel both read the pre-roll ring; the roll launches after both) — but
25395        // cloning first keeps the ordering trivially correct under any future stream split.
25396        let ring_old = if t < d_conv - 1 {
25397            Some(self.clone_dtod(conv_state)?)
25398        } else {
25399            None
25400        };
25401        {
25402            let f = self.func("ssm_conv1d_tm_state_f32");
25403            let cfg = LaunchConfig {
25404                grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
25405                block_dim: (256, 1, 1),
25406                shared_mem_bytes: 0,
25407            };
25408            let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
25409            let __s_b = self.gpu.stream();
25410            let mut b = __s_b.launch_builder(&f);
25411            b.arg(qkv_tm)
25412                .arg(&*conv_state)
25413                .arg(w)
25414                .arg(y)
25415                .arg(&cd)
25416                .arg(&ti)
25417                .arg(&dc);
25418            unsafe {
25419                b.launch(cfg)?;
25420            }
25421        }
25422        match (ring_old, pad_len) {
25423            (None, Some(len_d)) => {
25424                let f = self.func("ssm_conv_ring_update_dev_f32");
25425                let n = conv_dim * (d_conv - 1);
25426                let cfg = LaunchConfig::for_num_elems(n as u32);
25427                let (cd, dc) = (conv_dim as i32, d_conv as i32);
25428                let __s_b = self.gpu.stream();
25429                let mut b = __s_b.launch_builder(&f);
25430                b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
25431                unsafe {
25432                    b.launch(cfg)?;
25433                }
25434            }
25435            (None, None) => {
25436                let f = self.func("ssm_conv_ring_update_f32");
25437                let n = conv_dim * (d_conv - 1);
25438                let cfg = LaunchConfig::for_num_elems(n as u32);
25439                let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
25440                let __s_b = self.gpu.stream();
25441                let mut b = __s_b.launch_builder(&f);
25442                b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
25443                unsafe {
25444                    b.launch(cfg)?;
25445                }
25446            }
25447            (Some(old), _) => {
25448                self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
25449            }
25450        }
25451        Ok(())
25452    }
25453
25454    /// qkv-view twin (task #16): batched prime reads the concat GEMM output directly.
25455    pub fn ssm_conv1d_tm_state_pad_v(
25456        &self,
25457        qkv_tm: &cudarc::driver::CudaView<f32>,
25458        conv_state: &mut CudaSlice<f32>,
25459        w: &CudaSlice<f32>,
25460        y: &mut CudaSlice<f32>,
25461        conv_dim: usize,
25462        t: usize,
25463        d_conv: usize,
25464        pad_len: Option<&CudaSlice<i32>>,
25465    ) -> Result<(), Box<dyn std::error::Error>> {
25466        assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
25467        // clone BEFORE the window kernel is issued is not required (stream-ordered: the dtod and
25468        // the window kernel both read the pre-roll ring; the roll launches after both) — but
25469        // cloning first keeps the ordering trivially correct under any future stream split.
25470        let ring_old = if t < d_conv - 1 {
25471            Some(self.clone_dtod(conv_state)?)
25472        } else {
25473            None
25474        };
25475        {
25476            let f = self.func("ssm_conv1d_tm_state_f32");
25477            let cfg = LaunchConfig {
25478                grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
25479                block_dim: (256, 1, 1),
25480                shared_mem_bytes: 0,
25481            };
25482            let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
25483            let __s_b = self.gpu.stream();
25484            let mut b = __s_b.launch_builder(&f);
25485            b.arg(qkv_tm)
25486                .arg(&*conv_state)
25487                .arg(w)
25488                .arg(y)
25489                .arg(&cd)
25490                .arg(&ti)
25491                .arg(&dc);
25492            unsafe {
25493                b.launch(cfg)?;
25494            }
25495        }
25496        match (ring_old, pad_len) {
25497            (None, Some(len_d)) => {
25498                let f = self.func("ssm_conv_ring_update_dev_f32");
25499                let n = conv_dim * (d_conv - 1);
25500                let cfg = LaunchConfig::for_num_elems(n as u32);
25501                let (cd, dc) = (conv_dim as i32, d_conv as i32);
25502                let __s_b = self.gpu.stream();
25503                let mut b = __s_b.launch_builder(&f);
25504                b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
25505                unsafe {
25506                    b.launch(cfg)?;
25507                }
25508            }
25509            (None, None) => {
25510                let f = self.func("ssm_conv_ring_update_f32");
25511                let n = conv_dim * (d_conv - 1);
25512                let cfg = LaunchConfig::for_num_elems(n as u32);
25513                let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
25514                let __s_b = self.gpu.stream();
25515                let mut b = __s_b.launch_builder(&f);
25516                b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
25517                unsafe {
25518                    b.launch(cfg)?;
25519                }
25520            }
25521            (Some(_), _) => unreachable!(
25522                "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
25523            ),
25524        }
25525        Ok(())
25526    }
25527
25528    /// PREFIX conv-ring rebuild (spec REPLAY-FREE partial accept): overwrite the resident ring
25529    /// with the state a T=1 chain holds after only the FIRST `tc` columns of `qkv_tm` — the last
25530    /// `pad` entries of [ring_old | cols 0..tc-1]. PURE COPIES (the ring stores raw inputs; no
25531    /// arithmetic, cannot perturb FP order). `ring_old` = the pre-round snapshot ring.
25532    pub fn ssm_conv_ring_rebuild(
25533        &self,
25534        qkv_tm: &CudaSlice<f32>,
25535        ring_old: &CudaSlice<f32>,
25536        conv_state: &mut CudaSlice<f32>,
25537        conv_dim: usize,
25538        tc: usize,
25539        d_conv: usize,
25540    ) -> Result<(), Box<dyn std::error::Error>> {
25541        let f = self.func("ssm_conv_ring_rebuild_f32");
25542        let n = conv_dim * (d_conv - 1);
25543        let cfg = LaunchConfig::for_num_elems(n as u32);
25544        let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
25545        let __s_b = self.gpu.stream();
25546        let mut b = __s_b.launch_builder(&f);
25547        b.arg(qkv_tm)
25548            .arg(ring_old)
25549            .arg(conv_state)
25550            .arg(&cd)
25551            .arg(&ti)
25552            .arg(&dc);
25553        unsafe {
25554            b.launch(cfg)?;
25555        }
25556        Ok(())
25557    }
25558
25559    /// FUSED decode GDN prep (T=1): repack + q/k L2-norm + beta sigmoid + g_log in one launch.
25560    /// Replaces 5 tiny serialized kernels on the decode critical path. L2 reduce runs as a 32-lane
25561    /// warp tree (vs l2_norm_f32's 256-thread two-level tree) — same math, different FP sum order;
25562    /// the argmax + run-spec gates are the authority.
25563    #[allow(clippy::too_many_arguments)]
25564    pub fn gdn_prep_decode(
25565        &self,
25566        conv_out: &CudaSlice<f32>,
25567        beta_raw: &CudaSlice<f32>,
25568        alpha: &CudaSlice<f32>,
25569        dt_bias: &CudaSlice<f32>,
25570        a: &CudaSlice<f32>,
25571        q_l2: &mut CudaSlice<f32>,
25572        k_l2: &mut CudaSlice<f32>,
25573        v_g: &mut CudaSlice<f32>,
25574        beta: &mut CudaSlice<f32>,
25575        g_log: &mut CudaSlice<f32>,
25576        d_state: usize,
25577        num_v: usize,
25578        num_k: usize,
25579        key_dim: usize,
25580        eps: f32,
25581    ) -> Result<(), Box<dyn std::error::Error>> {
25582        let f = self.func("gdn_prep_decode_f32");
25583        let cfg = LaunchConfig {
25584            grid_dim: (num_v as u32, 1, 1),
25585            block_dim: (32, 4, 1),
25586            shared_mem_bytes: 0,
25587        };
25588        let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
25589        let __s_b = self.gpu.stream();
25590        let mut b = __s_b.launch_builder(&f);
25591        b.arg(conv_out)
25592            .arg(beta_raw)
25593            .arg(alpha)
25594            .arg(dt_bias)
25595            .arg(a)
25596            .arg(q_l2)
25597            .arg(k_l2)
25598            .arg(v_g)
25599            .arg(beta)
25600            .arg(g_log)
25601            .arg(&ds)
25602            .arg(&nv)
25603            .arg(&nk)
25604            .arg(&kd)
25605            .arg(&eps);
25606        unsafe {
25607            b.launch(cfg)?;
25608        }
25609        Ok(())
25610    }
25611
25612    /// FUSED prefill conv + GDN repack: token-major qkv -> q_g/k_g/v_g in ONE launch (no conv_out
25613    /// materialization, no qkv_to_gdn_repack pass). BIT-IDENTICAL values; scatter matches
25614    /// qkv_to_gdn_repack's modulo head-repeat mapping exactly.
25615    #[allow(clippy::too_many_arguments)]
25616    pub fn ssm_conv1d_gdn(
25617        &self,
25618        qkv_tm: &CudaSlice<f32>,
25619        w: &CudaSlice<f32>,
25620        q_g: &mut CudaSlice<f32>,
25621        k_g: &mut CudaSlice<f32>,
25622        v_g: &mut CudaSlice<f32>,
25623        conv_dim: usize,
25624        t: usize,
25625        d_conv: usize,
25626        d_state: usize,
25627        num_v: usize,
25628        num_k: usize,
25629        key_dim: usize,
25630    ) -> Result<(), Box<dyn std::error::Error>> {
25631        let f = self.func("ssm_conv1d_gdn_f32");
25632        let cfg = LaunchConfig {
25633            grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
25634            block_dim: (256, 1, 1),
25635            shared_mem_bytes: 0,
25636        };
25637        let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
25638        let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
25639        let __s_b = self.gpu.stream();
25640        let mut b = __s_b.launch_builder(&f);
25641        b.arg(qkv_tm)
25642            .arg(w)
25643            .arg(q_g)
25644            .arg(k_g)
25645            .arg(v_g)
25646            .arg(&cd)
25647            .arg(&ti)
25648            .arg(&dc)
25649            .arg(&ds)
25650            .arg(&nv)
25651            .arg(&nk)
25652            .arg(&kd);
25653        unsafe {
25654            b.launch(cfg)?;
25655        }
25656        Ok(())
25657    }
25658
25659    pub fn ssm_conv1d(
25660        &self,
25661        x: &CudaSlice<f32>,
25662        w: &CudaSlice<f32>,
25663        y: &mut CudaSlice<f32>,
25664        conv_dim: usize,
25665        t: usize,
25666        d_conv: usize,
25667        silu: bool,
25668    ) -> Result<(), Box<dyn std::error::Error>> {
25669        let f = self.func("ssm_conv1d_silu_f32");
25670        let cfg = LaunchConfig {
25671            grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
25672            block_dim: (256, 1, 1),
25673            shared_mem_bytes: 0,
25674        };
25675        let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
25676        let __s_b = self.gpu.stream();
25677        let mut b = __s_b.launch_builder(&f);
25678        b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
25679        unsafe {
25680            b.launch(cfg)?;
25681        }
25682        Ok(())
25683    }
25684
25685    /// Gated DeltaNet scan, S_v=128. q,k,v:[128,H,T]; g,beta:[H,T]; state:[128,128,H] transposed;
25686    /// o:[128,H,T]. Single sequence.
25687    pub fn gdn_scan_s128(
25688        &self,
25689        q: &CudaSlice<f32>,
25690        k: &CudaSlice<f32>,
25691        v: &CudaSlice<f32>,
25692        g: &CudaSlice<f32>,
25693        beta: &CudaSlice<f32>,
25694        state_in: &CudaSlice<f32>,
25695        state_out: &mut CudaSlice<f32>,
25696        o: &mut CudaSlice<f32>,
25697        n_head: usize,
25698        t: usize,
25699        scale: f32,
25700    ) -> Result<(), Box<dyn std::error::Error>> {
25701        let f = self.func("gdn_scan_s128");
25702        const S_V: u32 = 128;
25703        const WARP: u32 = 32;
25704        const COLS_PER_BLOCK: u32 = 4;
25705        let cfg = LaunchConfig {
25706            grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
25707            block_dim: (WARP, COLS_PER_BLOCK, 1),
25708            shared_mem_bytes: 0,
25709        };
25710        let (h, ti) = (n_head as i32, t as i32);
25711        let __s_b = self.gpu.stream();
25712        let mut b = __s_b.launch_builder(&f);
25713        b.arg(q)
25714            .arg(k)
25715            .arg(v)
25716            .arg(g)
25717            .arg(beta)
25718            .arg(state_in)
25719            .arg(state_out)
25720            .arg(o)
25721            .arg(&h)
25722            .arg(&ti)
25723            .arg(&scale);
25724        unsafe {
25725            b.launch(cfg)?;
25726        }
25727        Ok(())
25728    }
25729
25730    // ==== B2' batched decode state ops (decode_batch.rs) ====
25731    // Per-seq state pointers ride device u64 arrays (views into the per-step pointer table).
25732    // Bodies are the single-seq kernels per sequence — bit-identical per row.
25733
25734    #[allow(clippy::too_many_arguments)]
25735    pub fn ssm_conv1d_fused_decode_b(
25736        &self,
25737        qkv_cols: &CudaSlice<f32>,
25738        conv_state_ptrs: &cudarc::driver::CudaView<u64>,
25739        w: &CudaSlice<f32>,
25740        conv_outs: &mut CudaSlice<f32>,
25741        conv_dim: usize,
25742        d_conv: usize,
25743        b_n: usize,
25744    ) -> Result<(), Box<dyn std::error::Error>> {
25745        let f = self.func("ssm_conv1d_fused_decode_b_f32");
25746        let cfg = LaunchConfig {
25747            grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
25748            block_dim: (256, 1, 1),
25749            shared_mem_bytes: 0,
25750        };
25751        let (cd, dc) = (conv_dim as i32, d_conv as i32);
25752        let __s_b = self.gpu.stream();
25753        let mut b = __s_b.launch_builder(&f);
25754        b.arg(qkv_cols)
25755            .arg(conv_state_ptrs)
25756            .arg(w)
25757            .arg(conv_outs)
25758            .arg(&cd)
25759            .arg(&dc);
25760        unsafe {
25761            b.launch(cfg)?;
25762        }
25763        Ok(())
25764    }
25765
25766    #[allow(clippy::too_many_arguments)]
25767    pub fn gdn_prep_decode_b(
25768        &self,
25769        conv_outs: &CudaSlice<f32>,
25770        beta_raws: &CudaSlice<f32>,
25771        alphas: &CudaSlice<f32>,
25772        dt_bias: &CudaSlice<f32>,
25773        a: &CudaSlice<f32>,
25774        q_l2: &mut CudaSlice<f32>,
25775        k_l2: &mut CudaSlice<f32>,
25776        v_g: &mut CudaSlice<f32>,
25777        beta: &mut CudaSlice<f32>,
25778        g_log: &mut CudaSlice<f32>,
25779        d_state: usize,
25780        num_v: usize,
25781        num_k: usize,
25782        key_dim: usize,
25783        eps: f32,
25784        conv_dim: usize,
25785        b_n: usize,
25786    ) -> Result<(), Box<dyn std::error::Error>> {
25787        let f = self.func("gdn_prep_decode_b_f32");
25788        let cfg = LaunchConfig {
25789            grid_dim: (num_v as u32, 1, b_n as u32),
25790            block_dim: (32, 4, 1),
25791            shared_mem_bytes: 0,
25792        };
25793        let (ds, nv, nk, kd, cd) = (
25794            d_state as i32,
25795            num_v as i32,
25796            num_k as i32,
25797            key_dim as i32,
25798            conv_dim as i32,
25799        );
25800        let __s_b = self.gpu.stream();
25801        let mut b = __s_b.launch_builder(&f);
25802        b.arg(conv_outs)
25803            .arg(beta_raws)
25804            .arg(alphas)
25805            .arg(dt_bias)
25806            .arg(a)
25807            .arg(q_l2)
25808            .arg(k_l2)
25809            .arg(v_g)
25810            .arg(beta)
25811            .arg(g_log)
25812            .arg(&ds)
25813            .arg(&nv)
25814            .arg(&nk)
25815            .arg(&kd)
25816            .arg(&eps)
25817            .arg(&cd);
25818        unsafe {
25819            b.launch(cfg)?;
25820        }
25821        Ok(())
25822    }
25823
25824    #[allow(clippy::too_many_arguments)]
25825    pub fn gdn_scan_s128_batched(
25826        &self,
25827        q: &CudaSlice<f32>,
25828        k: &CudaSlice<f32>,
25829        v: &CudaSlice<f32>,
25830        g: &CudaSlice<f32>,
25831        beta: &CudaSlice<f32>,
25832        state_in_ptrs: &cudarc::driver::CudaView<u64>,
25833        state_out_ptrs: &cudarc::driver::CudaView<u64>,
25834        o: &mut CudaSlice<f32>,
25835        n_head: usize,
25836        b_n: usize,
25837        scale: f32,
25838    ) -> Result<(), Box<dyn std::error::Error>> {
25839        let f = self.func("gdn_scan_s128_b");
25840        const S_V: u32 = 128;
25841        const WARP: u32 = 32;
25842        const COLS_PER_BLOCK: u32 = 4;
25843        let cfg = LaunchConfig {
25844            grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
25845            block_dim: (WARP, COLS_PER_BLOCK, 1),
25846            shared_mem_bytes: 0,
25847        };
25848        let h = n_head as i32;
25849        let __s_b = self.gpu.stream();
25850        let mut b = __s_b.launch_builder(&f);
25851        b.arg(q)
25852            .arg(k)
25853            .arg(v)
25854            .arg(g)
25855            .arg(beta)
25856            .arg(state_in_ptrs)
25857            .arg(state_out_ptrs)
25858            .arg(o)
25859            .arg(&h)
25860            .arg(&scale);
25861        unsafe {
25862            b.launch(cfg)?;
25863        }
25864        Ok(())
25865    }
25866
25867    /// VIEW twins of the three GDN decode state wrappers (lane/verify-launchslim): identical
25868    /// launches, row args as views into the caller's packed [T, ...] buffers — the t-parallel
25869    /// verify's per-row loop passes slices instead of paying an arithmetic-free dtod per row
25870    /// per kernel (48 layers x T rows x 4 copies/round on the money path). Same kernels, same
25871    /// numeric class; only the pointer arithmetic moved host-side.
25872    #[allow(clippy::too_many_arguments)]
25873    pub fn ssm_conv1d_fused_decode_b_view(
25874        &self,
25875        qkv_cols: &cudarc::driver::CudaView<f32>,
25876        conv_state_ptrs: &cudarc::driver::CudaView<u64>,
25877        w: &CudaSlice<f32>,
25878        conv_outs: &mut CudaSlice<f32>,
25879        conv_dim: usize,
25880        d_conv: usize,
25881        b_n: usize,
25882    ) -> Result<(), Box<dyn std::error::Error>> {
25883        let f = self.func("ssm_conv1d_fused_decode_b_f32");
25884        let cfg = LaunchConfig {
25885            grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
25886            block_dim: (256, 1, 1),
25887            shared_mem_bytes: 0,
25888        };
25889        let (cd, dc) = (conv_dim as i32, d_conv as i32);
25890        let __s_b = self.gpu.stream();
25891        let mut b = __s_b.launch_builder(&f);
25892        b.arg(qkv_cols)
25893            .arg(conv_state_ptrs)
25894            .arg(w)
25895            .arg(conv_outs)
25896            .arg(&cd)
25897            .arg(&dc);
25898        unsafe {
25899            b.launch(cfg)?;
25900        }
25901        Ok(())
25902    }
25903
25904    #[allow(clippy::too_many_arguments)]
25905    pub fn gdn_prep_decode_b_view(
25906        &self,
25907        conv_outs: &CudaSlice<f32>,
25908        beta_raws: &cudarc::driver::CudaView<f32>,
25909        alphas: &cudarc::driver::CudaView<f32>,
25910        dt_bias: &CudaSlice<f32>,
25911        a: &CudaSlice<f32>,
25912        q_l2: &mut CudaSlice<f32>,
25913        k_l2: &mut CudaSlice<f32>,
25914        v_g: &mut CudaSlice<f32>,
25915        beta: &mut CudaSlice<f32>,
25916        g_log: &mut CudaSlice<f32>,
25917        d_state: usize,
25918        num_v: usize,
25919        num_k: usize,
25920        key_dim: usize,
25921        eps: f32,
25922        conv_dim: usize,
25923        b_n: usize,
25924    ) -> Result<(), Box<dyn std::error::Error>> {
25925        let f = self.func("gdn_prep_decode_b_f32");
25926        let cfg = LaunchConfig {
25927            grid_dim: (num_v as u32, 1, b_n as u32),
25928            block_dim: (32, 4, 1),
25929            shared_mem_bytes: 0,
25930        };
25931        let (ds, nv, nk, kd, cd) = (
25932            d_state as i32,
25933            num_v as i32,
25934            num_k as i32,
25935            key_dim as i32,
25936            conv_dim as i32,
25937        );
25938        let __s_b = self.gpu.stream();
25939        let mut b = __s_b.launch_builder(&f);
25940        b.arg(conv_outs)
25941            .arg(beta_raws)
25942            .arg(alphas)
25943            .arg(dt_bias)
25944            .arg(a)
25945            .arg(q_l2)
25946            .arg(k_l2)
25947            .arg(v_g)
25948            .arg(beta)
25949            .arg(g_log)
25950            .arg(&ds)
25951            .arg(&nv)
25952            .arg(&nk)
25953            .arg(&kd)
25954            .arg(&eps)
25955            .arg(&cd);
25956        unsafe {
25957            b.launch(cfg)?;
25958        }
25959        Ok(())
25960    }
25961
25962    #[allow(clippy::too_many_arguments)]
25963    pub fn gdn_scan_s128_batched_view(
25964        &self,
25965        q: &CudaSlice<f32>,
25966        k: &CudaSlice<f32>,
25967        v: &CudaSlice<f32>,
25968        g: &CudaSlice<f32>,
25969        beta: &CudaSlice<f32>,
25970        state_in_ptrs: &cudarc::driver::CudaView<u64>,
25971        state_out_ptrs: &cudarc::driver::CudaView<u64>,
25972        o: &mut cudarc::driver::CudaViewMut<f32>,
25973        n_head: usize,
25974        b_n: usize,
25975        scale: f32,
25976    ) -> Result<(), Box<dyn std::error::Error>> {
25977        let f = self.func("gdn_scan_s128_b");
25978        const S_V: u32 = 128;
25979        const WARP: u32 = 32;
25980        const COLS_PER_BLOCK: u32 = 4;
25981        let cfg = LaunchConfig {
25982            grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
25983            block_dim: (WARP, COLS_PER_BLOCK, 1),
25984            shared_mem_bytes: 0,
25985        };
25986        let h = n_head as i32;
25987        let __s_b = self.gpu.stream();
25988        let mut b = __s_b.launch_builder(&f);
25989        b.arg(q)
25990            .arg(k)
25991            .arg(v)
25992            .arg(g)
25993            .arg(beta)
25994            .arg(state_in_ptrs)
25995            .arg(state_out_ptrs)
25996            .arg(o)
25997            .arg(&h)
25998            .arg(&scale);
25999        unsafe {
26000            b.launch(cfg)?;
26001        }
26002        Ok(())
26003    }
26004
26005    /// A4 seam: chunked WY GDN prefill. DEFAULT ON (`MEMRA_GDN_CHUNKED=0` = rollback to the
26006    /// sequential scan). Flipped 2026-07-04 with the full battery green: kernel-check ALL
26007    /// GREEN x {9B, 27B} incl the f64-truth chunk gates; run-gen argmax 82==82 both models
26008    /// on AND off (24/24 sweep runs); run-spec K={1,2,3,4,6,8} PASS x {9B synth, 9B text,
26009    /// 27B p2, 27B p3}; e2e first-16-token agreement 6/6 (full-256 drifts at index 47-125
26010    /// on 5/6 prompts — accepted cache-state-FP class, batched-prime precedent).
26011    /// PREFILL-ONLY: decode + spec verify never route here (decode==verify dispatch
26012    /// identity law); prime_cache/forward/forward_last are the only callers.
26013    pub fn gdn_chunked_enabled() -> bool {
26014        static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
26015        *E.get_or_init(|| {
26016            std::env::var("MEMRA_GDN_CHUNKED")
26017                .map(|v| v != "0")
26018                .unwrap_or(true)
26019        })
26020    }
26021
26022    /// A4 chunk size (MEMRA_GDN_CHUNK, default 32 — the sweep winner: the O(T*C) chunk
26023    /// matrices grow with C while the sequential state pass is C-flat, so smaller chunks
26024    /// win; C=32/64 also get the register-history solve template). Clamped to multiples
26025    /// of 32 in [32, 128] (kernel row mappings require it).
26026    pub fn gdn_chunk_size() -> usize {
26027        static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
26028        *C.get_or_init(|| {
26029            let c: usize = std::env::var("MEMRA_GDN_CHUNK")
26030                .ok()
26031                .and_then(|v| v.parse().ok())
26032                .unwrap_or(32);
26033            c.clamp(32, 128) / 32 * 32
26034        })
26035    }
26036
26037    /// A4: chunked WY / blockwise-inverse GDN prefill (see cu/hybrid.cu K1-K5 header for the
26038    /// math). Same contract as `gdn_scan_s128` (layouts, state ping-pong) but chunk-parallel:
26039    /// NOT bit-identical to the sequential scan (chunked FP accumulation order); run-gen
26040    /// argmax + run-spec batteries are the accuracy authority. PREFILL callers only.
26041    #[allow(clippy::too_many_arguments)]
26042    /// task #18: K1-K3 of the chunked WY scan (shared by the per-seq path and the
26043    /// batched-prime varlen path). Returns (gcum, P, U, W); `A` is K3-internal.
26044    #[allow(clippy::too_many_arguments, clippy::type_complexity)]
26045    #[allow(clippy::too_many_arguments)]
26046    pub fn gdn_chunk_k123(
26047        &self,
26048        q: &CudaSlice<f32>,
26049        k: &CudaSlice<f32>,
26050        v: &CudaSlice<f32>,
26051        g: &CudaSlice<f32>,
26052        beta: &CudaSlice<f32>,
26053        wb16: Option<&mut CudaSlice<u8>>,
26054        n_head: usize,
26055        t: usize,
26056        c: usize,
26057        hk: usize,
26058        k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
26059    ) -> Result<
26060        (
26061            CudaSlice<f32>,
26062            CudaSlice<f32>,
26063            CudaSlice<f32>,
26064            CudaSlice<f32>,
26065        ),
26066        Box<dyn std::error::Error>,
26067    > {
26068        const D: usize = 128;
26069        let h = n_head;
26070        let nc = (t + c - 1) / c;
26071        let (hi, ti, ci) = (h as i32, t as i32, c as i32);
26072        let mut gcum = self.uninit(t * h)?;
26073        let mut a = self.uninit(nc * h * c * c)?;
26074        let mut p = self.uninit(nc * h * c * c)?;
26075        let mut u = self.uninit(nc * h * c * D)?;
26076        let mut w = self.uninit(nc * h * c * D)?;
26077        {
26078            // K1
26079            let f = self.func("gdn_chunk_cumgate_f32");
26080            let cfg = LaunchConfig {
26081                grid_dim: (nc as u32, h as u32, 1),
26082                block_dim: (32, 1, 1),
26083                shared_mem_bytes: 0,
26084            };
26085            let __s_b = self.gpu.stream();
26086            let mut b = __s_b.launch_builder(&f);
26087            b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
26088            unsafe {
26089                b.launch(cfg)?;
26090            }
26091        }
26092        if let Some((qb, kb, pb)) = k2w {
26093            // K2-wgmma (MEMRA_GDN_WGMMA path, c==32): A + pre-masked Pb16 in one kernel;
26094            // the P f32 buffer stays UNWRITTEN (its only wgmma-path consumer is Pb16).
26095            assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
26096            let f = self.func("gdn_k2_wgmma");
26097            let cfg = LaunchConfig {
26098                grid_dim: (nc as u32, h as u32, 1),
26099                block_dim: (128, 1, 1),
26100                shared_mem_bytes: 0,
26101            };
26102            let hki = hk as i32;
26103            let __s_b = self.gpu.stream();
26104            let mut b = __s_b.launch_builder(&f);
26105            b.arg(qb)
26106                .arg(kb)
26107                .arg(&gcum)
26108                .arg(beta)
26109                .arg(&mut a)
26110                .arg(&mut *pb)
26111                .arg(&hi)
26112                .arg(&ti)
26113                .arg(&ci)
26114                .arg(&hki);
26115            unsafe {
26116                b.launch(cfg)?;
26117            }
26118        } else if c <= 64 && !portable_mma_gated() {
26119            // K2 register-tiled (2x2 outputs/thread, whole-chunk smem k tile)
26120            let f = self.func("gdn_chunk_attn_f32");
26121            f.set_attribute(
26122                CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
26123                GDN_K2_DYNAMIC_SHARED_BYTES as i32,
26124            )?;
26125            let jt = ((c + 31) / 32) as u32;
26126            let cfg = LaunchConfig {
26127                grid_dim: (nc as u32, h as u32, jt),
26128                block_dim: (256, 1, 1),
26129                shared_mem_bytes: GDN_K2_DYNAMIC_SHARED_BYTES,
26130            };
26131            let hki = hk as i32;
26132            let __s_b = self.gpu.stream();
26133            let mut b = __s_b.launch_builder(&f);
26134            b.arg(q)
26135                .arg(k)
26136                .arg(&gcum)
26137                .arg(beta)
26138                .arg(&mut a)
26139                .arg(&mut p)
26140                .arg(&hi)
26141                .arg(&ti)
26142                .arg(&ci)
26143                .arg(&hki);
26144            unsafe {
26145                b.launch(cfg)?;
26146            }
26147        } else {
26148            // K2 generic (C = 128, or the portable target's low-smem fallback)
26149            assert!(
26150                hk == h,
26151                "generic K2 is broadcast-only (de-broadcast rides C==32)"
26152            );
26153            let f = self.func("gdn_chunk_attn_g_f32");
26154            let cfg = LaunchConfig {
26155                grid_dim: (nc as u32, h as u32, 1),
26156                block_dim: (32, 8, 1),
26157                shared_mem_bytes: 0,
26158            };
26159            let __s_b = self.gpu.stream();
26160            let mut b = __s_b.launch_builder(&f);
26161            b.arg(q)
26162                .arg(k)
26163                .arg(&gcum)
26164                .arg(beta)
26165                .arg(&mut a)
26166                .arg(&mut p)
26167                .arg(&hi)
26168                .arg(&ti)
26169                .arg(&ci);
26170            unsafe {
26171                b.launch(cfg)?;
26172            }
26173        }
26174        {
26175            // K3 (register-history templates for C=32/64; local-memory generic otherwise)
26176            let cfg = LaunchConfig {
26177                grid_dim: (nc as u32, h as u32, 1),
26178                block_dim: (256, 1, 1),
26179                shared_mem_bytes: 0,
26180            };
26181            match c {
26182                32 | 64 => {
26183                    let f = self.func(if c == 32 {
26184                        "gdn_chunk_solve32_f32"
26185                    } else {
26186                        "gdn_chunk_solve64_f32"
26187                    });
26188                    // mirror-fold: W's bf16 twin emitted on store (0 = skip)
26189                    let wb: u64 = match wb16 {
26190                        Some(d) => self.addr_u8(d),
26191                        None => 0,
26192                    };
26193                    let hki = hk as i32;
26194                    let __s_b = self.gpu.stream();
26195                    let mut b = __s_b.launch_builder(&f);
26196                    b.arg(v)
26197                        .arg(k)
26198                        .arg(&a)
26199                        .arg(&gcum)
26200                        .arg(&mut u)
26201                        .arg(&mut w)
26202                        .arg(&wb)
26203                        .arg(&hi)
26204                        .arg(&ti)
26205                        .arg(&hki);
26206                    unsafe {
26207                        b.launch(cfg)?;
26208                    }
26209                }
26210                _ => {
26211                    assert!(hk == h, "generic K3 is broadcast-only");
26212                    let f = self.func("gdn_chunk_solve_f32");
26213                    let __s_b = self.gpu.stream();
26214                    let mut b = __s_b.launch_builder(&f);
26215                    b.arg(v)
26216                        .arg(k)
26217                        .arg(&a)
26218                        .arg(&gcum)
26219                        .arg(&mut u)
26220                        .arg(&mut w)
26221                        .arg(&hi)
26222                        .arg(&ti)
26223                        .arg(&ci);
26224                    unsafe {
26225                        b.launch(cfg)?;
26226                    }
26227                }
26228            }
26229        }
26230        Ok((gcum, p, u, w))
26231    }
26232
26233    /// task #21 de-broadcast seam: q/k stored at num_k distinct GQA heads instead of
26234    /// the num_v broadcast. MEMRA_GDN_DB=0 reverts. Only the chunked prefill path
26235    /// consumes the compact layout (hk plumbed; hk == H reproduces broadcast exactly).
26236    pub fn gdn_db_on() -> bool {
26237        std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
26238    }
26239
26240    /// Whether the K4/K5 mma pair serves at chunk size `c` (mirrors gdn_scan_chunked's
26241    /// seam read — env re-read per call ON PURPOSE, kernel-check pins both configs).
26242    /// DEFAULT ON for sm_120a builds too (lane/moeprime-nvfp4-direct, 2026-08-21): the pair
26243    /// was qualified on 90a only and left env-opt-in elsewhere; measured on Blackwell it
26244    /// wins on BOTH rigs — one RTX PRO 6000 (ornith15 pp14715 12,036 -> 12,751/12,957,
26245    /// +6-8%, both orders) and the local 5090 (q38-27b pp6435 1,397/1,429 -> 1,427/1,446,
26246    /// both orders) — with kernel-check/run-gen/margin-gate/run-spec green under the flag.
26247    /// bf16 HMMA (m16n8k16) is sm_80-class PTX; only the wgmma nest stays Hopper-gated.
26248    pub fn gdn_mma_enabled(&self, c: usize) -> bool {
26249        !portable_mma_gated()
26250            && c == 32
26251            && match std::env::var("MEMRA_GDN_MMA").as_deref() {
26252                Ok("1") => true,
26253                Ok("0") => false,
26254                _ => gdn_mma_default_on(),
26255            }
26256    }
26257
26258    /// task #22: whether the fused K4+K5 (+K2) wgmma path serves (nested inside the
26259    /// mma config; same per-call env read discipline). HARD-gated to the Hopper build:
26260    /// the wgmma asm bodies exist only at __CUDA_ARCH__ == 900 (MEMRA_K45_REAL,
26261    /// wgmma_common.cuh) — on every other arch the kernel compiles EMPTY, so an env
26262    /// force would silently produce garbage. Required since the sm_120a mma default
26263    /// flip made MEMRA_GDN_WGMMA=1 alone reach this branch there.
26264    pub fn gdn_wgmma_on(&self, c: usize) -> bool {
26265        cfg!(memra_hopper_mma)
26266            && self.gdn_mma_enabled(c)
26267            && std::env::var("MEMRA_GDN_WGMMA").as_deref() != Ok("0")
26268    }
26269
26270    /// task #18 conv-fuse: carried-ring conv + SiLU + GDN repack in ONE pass (the
26271    /// conv_out intermediate and its transposed re-read disappear — 11.8ms of the
26272    /// T=2048 prime). Ring update stays the separate follow-up launch (pad-aware).
26273    /// BIT-IDENTICAL values to ssm_conv1d_tm_state_pad + qkv_to_gdn_repack.
26274    #[allow(clippy::too_many_arguments)]
26275    pub fn ssm_conv1d_gdn_state_pad(
26276        &self,
26277        qkv_tm: &cudarc::driver::CudaView<f32>,
26278        conv_state: &mut CudaSlice<f32>,
26279        w: &CudaSlice<f32>,
26280        q_g: &mut CudaSlice<f32>,
26281        k_g: &mut CudaSlice<f32>,
26282        v_g: &mut CudaSlice<f32>,
26283        conv_dim: usize,
26284        t: usize,
26285        d_conv: usize,
26286        d_state: usize,
26287        num_v: usize,
26288        num_k: usize,
26289        key_dim: usize,
26290        hk: usize,
26291        pad_len: Option<&CudaSlice<i32>>,
26292    ) -> Result<(), Box<dyn std::error::Error>> {
26293        assert!(
26294            t >= d_conv - 1,
26295            "fused state conv requires T >= pad (PRIME_MIN_T gates)"
26296        );
26297        {
26298            let f = self.func("ssm_conv1d_gdn_state_f32");
26299            let cfg = LaunchConfig {
26300                grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
26301                block_dim: (256, 1, 1),
26302                shared_mem_bytes: 0,
26303            };
26304            let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
26305            let (ds, nv, nk, kd, hki) = (
26306                d_state as i32,
26307                num_v as i32,
26308                num_k as i32,
26309                key_dim as i32,
26310                hk as i32,
26311            );
26312            let __s_b = self.gpu.stream();
26313            let mut b = __s_b.launch_builder(&f);
26314            b.arg(qkv_tm)
26315                .arg(&*conv_state)
26316                .arg(w)
26317                .arg(q_g)
26318                .arg(k_g)
26319                .arg(v_g)
26320                .arg(&cd)
26321                .arg(&ti)
26322                .arg(&dc)
26323                .arg(&ds)
26324                .arg(&nv)
26325                .arg(&nk)
26326                .arg(&kd)
26327                .arg(&hki);
26328            unsafe {
26329                b.launch(cfg)?;
26330            }
26331        }
26332        match pad_len {
26333            Some(len_d) => {
26334                let f = self.func("ssm_conv_ring_update_dev_f32");
26335                let n = conv_dim * (d_conv - 1);
26336                let cfg = LaunchConfig::for_num_elems(n as u32);
26337                let (cd, dc) = (conv_dim as i32, d_conv as i32);
26338                let __s_b = self.gpu.stream();
26339                let mut b = __s_b.launch_builder(&f);
26340                b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
26341                unsafe {
26342                    b.launch(cfg)?;
26343                }
26344            }
26345            None => {
26346                let f = self.func("ssm_conv_ring_update_f32");
26347                let n = conv_dim * (d_conv - 1);
26348                let cfg = LaunchConfig::for_num_elems(n as u32);
26349                let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
26350                let __s_b = self.gpu.stream();
26351                let mut b = __s_b.launch_builder(&f);
26352                b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
26353                unsafe {
26354                    b.launch(cfg)?;
26355                }
26356            }
26357        }
26358        Ok(())
26359    }
26360
26361    /// task #18 increment 2: allocate ONE sequence's chunk buffers (no launches) —
26362    /// K1-K5 all run varlen afterwards. `a`/`w` become struct members so the varlen
26363    /// K2/K3 can write them.
26364    pub fn gdn_chunk_alloc(
26365        &self,
26366        n_head: usize,
26367        t: usize,
26368        c: usize,
26369        hk: usize,
26370    ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
26371        const D: usize = 128;
26372        assert!(
26373            c == 32,
26374            "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
26375        );
26376        let h = n_head;
26377        let nc = (t + c - 1) / c;
26378        Ok(GdnChunkBufs {
26379            gcum: self.uninit(t * h)?,
26380            a: self.uninit(nc * h * c * c)?,
26381            p: self.uninit(nc * h * c * c)?,
26382            u: self.uninit(nc * h * c * D)?,
26383            w: self.uninit(nc * h * c * D)?,
26384            kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
26385            wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
26386            y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
26387            ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
26388            qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
26389            pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
26390            o: self.uninit(D * h * t)?,
26391            t,
26392            nc,
26393        })
26394    }
26395
26396    /// view-source twin of f32_to_bf16 (the batched FA3 v mirror reads a concat view).
26397    pub fn f32_to_bf16_v(
26398        &self,
26399        x: &cudarc::driver::CudaView<f32>,
26400        dst: &mut CudaSlice<u8>,
26401        n: usize,
26402    ) -> Result<(), Box<dyn std::error::Error>> {
26403        let f = self.func("f32_to_bf16_bulk");
26404        let ni = n as i64;
26405        let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
26406        let __s_b = self.gpu.stream();
26407        let mut b = __s_b.launch_builder(&f);
26408        b.arg(x).arg(dst).arg(&ni);
26409        unsafe {
26410            b.launch(cfg)?;
26411        }
26412        Ok(())
26413    }
26414
26415    /// f32 -> bf16 bulk mirror into a caller buffer (the K4/K5 operand mirrors).
26416    pub fn f32_to_bf16_into(
26417        &self,
26418        x: &CudaSlice<f32>,
26419        dst: &mut CudaSlice<u8>,
26420        n: usize,
26421    ) -> Result<(), Box<dyn std::error::Error>> {
26422        let f = self.func("f32_to_bf16_bulk");
26423        let ni = n as i64;
26424        let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
26425        let __s_b = self.gpu.stream();
26426        let mut b = __s_b.launch_builder(&f);
26427        b.arg(x).arg(dst).arg(&ni);
26428        unsafe {
26429            b.launch(cfg)?;
26430        }
26431        Ok(())
26432    }
26433
26434    /// task #18 increment 2: varlen K1+K2+K3 — three launches run every sequence's
26435    /// cumgate/attn/solve (per-block math identical to the per-seq kernels).
26436    pub fn gdn_chunk_k123_vl8(
26437        &self,
26438        seqs: &[GdnSeqVl],
26439        n_head: usize,
26440        hk: usize,
26441        wq: Option<&GdnWVl8>,
26442    ) -> Result<(), Box<dyn std::error::Error>> {
26443        let b = seqs.len();
26444        assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
26445        let mut packed = [GdnSeqVl::default(); 8];
26446        packed[..b].copy_from_slice(seqs);
26447        let v = GdnVl8(packed);
26448        let (hi, ci) = (n_head as i32, 32i32);
26449        let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
26450        {
26451            let f = self.func("gdn_chunk_cumgate_vl");
26452            let cfg = LaunchConfig {
26453                grid_dim: (max_nc, n_head as u32, b as u32),
26454                block_dim: (32, 1, 1),
26455                shared_mem_bytes: 0,
26456            };
26457            let __s_lb = self.gpu.stream();
26458            let mut lb = __s_lb.launch_builder(&f);
26459            lb.arg(&v).arg(&hi).arg(&ci);
26460            unsafe {
26461                lb.launch(cfg)?;
26462            }
26463        }
26464        let hki = hk as i32;
26465        if let Some(w) = wq {
26466            // K2-wgmma vl twin (writes A + pre-masked Pb16)
26467            let f = self.func("gdn_k2_wgmma_vl");
26468            let cfg = LaunchConfig {
26469                grid_dim: (max_nc, n_head as u32, b as u32),
26470                block_dim: (128, 1, 1),
26471                shared_mem_bytes: 0,
26472            };
26473            let __s_lb = self.gpu.stream();
26474            let mut lb = __s_lb.launch_builder(&f);
26475            lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
26476            unsafe {
26477                lb.launch(cfg)?;
26478            }
26479        } else {
26480            let f = self.func("gdn_chunk_attn_vl");
26481            f.set_attribute(
26482                CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
26483                GDN_K2_DYNAMIC_SHARED_BYTES as i32,
26484            )?;
26485            let cfg = LaunchConfig {
26486                grid_dim: (max_nc, n_head as u32, b as u32),
26487                block_dim: (256, 1, 1),
26488                shared_mem_bytes: GDN_K2_DYNAMIC_SHARED_BYTES,
26489            };
26490            let __s_lb = self.gpu.stream();
26491            let mut lb = __s_lb.launch_builder(&f);
26492            lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
26493            unsafe {
26494                lb.launch(cfg)?;
26495            }
26496        }
26497        {
26498            let f = self.func("gdn_chunk_solve32_vl");
26499            let cfg = LaunchConfig {
26500                grid_dim: (max_nc, n_head as u32, b as u32),
26501                block_dim: (256, 1, 1),
26502                shared_mem_bytes: 0,
26503            };
26504            let __s_lb = self.gpu.stream();
26505            let mut lb = __s_lb.launch_builder(&f);
26506            lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
26507            unsafe {
26508                lb.launch(cfg)?;
26509            }
26510        }
26511        Ok(())
26512    }
26513
26514    /// task #18 increment 3: varlen PREP chain — conv(+ring) / repack / fused-l2 /
26515    /// fused gate-prep, 5 launches for every sequence (per-element math identical
26516    /// to the per-seq kernels; l2/gate fusions write disjoint outputs).
26517    #[allow(clippy::too_many_arguments)]
26518    pub fn gdn_prep_vl8(
26519        &self,
26520        seqs: &[GdnPrepVl],
26521        conv_w: &CudaSlice<f32>,
26522        dt_bias: &CudaSlice<f32>,
26523        a: &CudaSlice<f32>,
26524        conv_dim: usize,
26525        d_conv: usize,
26526        d_state: usize,
26527        num_v: usize,
26528        num_k: usize,
26529        key_dim: usize,
26530        hk: usize,
26531        eps: f32,
26532    ) -> Result<(), Box<dyn std::error::Error>> {
26533        let b = seqs.len();
26534        assert!(b >= 1 && b <= 8);
26535        let mut packed = [GdnPrepVl::default(); 8];
26536        packed[..b].copy_from_slice(seqs);
26537        let v = GdnPrepVl8(packed);
26538        let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
26539        let (cdi, dci) = (conv_dim as i32, d_conv as i32);
26540        let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
26541        assert!(
26542            conv_fuse || hk == num_v,
26543            "de-broadcast requires the fused conv"
26544        );
26545        if conv_fuse {
26546            let f = self.func("ssm_conv1d_gdn_state_vl");
26547            let cfg = LaunchConfig {
26548                grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
26549                block_dim: (256, 1, 1),
26550                shared_mem_bytes: 0,
26551            };
26552            let (dsi, nvi, nki, kdi, hki) = (
26553                d_state as i32,
26554                num_v as i32,
26555                num_k as i32,
26556                key_dim as i32,
26557                hk as i32,
26558            );
26559            let __s_lb = self.gpu.stream();
26560            let mut lb = __s_lb.launch_builder(&f);
26561            lb.arg(&v)
26562                .arg(conv_w)
26563                .arg(&cdi)
26564                .arg(&dci)
26565                .arg(&dsi)
26566                .arg(&nvi)
26567                .arg(&nki)
26568                .arg(&kdi)
26569                .arg(&hki);
26570            unsafe {
26571                lb.launch(cfg)?;
26572            }
26573        } else {
26574            let f = self.func("ssm_conv1d_tm_state_vl");
26575            let cfg = LaunchConfig {
26576                grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
26577                block_dim: (256, 1, 1),
26578                shared_mem_bytes: 0,
26579            };
26580            let __s_lb = self.gpu.stream();
26581            let mut lb = __s_lb.launch_builder(&f);
26582            lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
26583            unsafe {
26584                lb.launch(cfg)?;
26585            }
26586        }
26587        {
26588            let f = self.func("ssm_conv_ring_update_vl");
26589            let n = (conv_dim * (d_conv - 1)) as u32;
26590            let cfg = LaunchConfig {
26591                grid_dim: (n.div_ceil(256), 1, b as u32),
26592                block_dim: (256, 1, 1),
26593                shared_mem_bytes: 0,
26594            };
26595            let __s_lb = self.gpu.stream();
26596            let mut lb = __s_lb.launch_builder(&f);
26597            lb.arg(&v).arg(&cdi).arg(&dci);
26598            unsafe {
26599                lb.launch(cfg)?;
26600            }
26601        }
26602        if !conv_fuse {
26603            let f = self.func("qkv_to_gdn_repack_vl");
26604            let n = max_t * (num_v * d_state) as u32;
26605            let cfg = LaunchConfig {
26606                grid_dim: (n.div_ceil(256), 1, b as u32),
26607                block_dim: (256, 1, 1),
26608                shared_mem_bytes: 0,
26609            };
26610            let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
26611            let __s_lb = self.gpu.stream();
26612            let mut lb = __s_lb.launch_builder(&f);
26613            lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
26614            unsafe {
26615                lb.launch(cfg)?;
26616            }
26617        }
26618        if Self::l2_v2_on(d_state) {
26619            let f = self.func("gdn_l2_v2_vl");
26620            let cfg = LaunchConfig {
26621                grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
26622                block_dim: (256, 1, 1),
26623                shared_mem_bytes: 0,
26624            };
26625            let (dsi, nvi) = (d_state as i32, hk as i32);
26626            let __s_lb = self.gpu.stream();
26627            let mut lb = __s_lb.launch_builder(&f);
26628            lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
26629            unsafe {
26630                lb.launch(cfg)?;
26631            }
26632        } else {
26633            let f = self.func("gdn_l2_vl");
26634            let cfg = LaunchConfig {
26635                grid_dim: (max_t * hk as u32, 2, b as u32),
26636                block_dim: (256, 1, 1),
26637                shared_mem_bytes: 0,
26638            };
26639            let (dsi, nvi) = (d_state as i32, hk as i32);
26640            let __s_lb = self.gpu.stream();
26641            let mut lb = __s_lb.launch_builder(&f);
26642            lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
26643            unsafe {
26644                lb.launch(cfg)?;
26645            }
26646        }
26647        {
26648            let f = self.func("gdn_gate_prep_vl");
26649            let n = max_t * num_v as u32;
26650            let cfg = LaunchConfig {
26651                grid_dim: (n.div_ceil(256), 1, b as u32),
26652                block_dim: (256, 1, 1),
26653                shared_mem_bytes: 0,
26654            };
26655            let nvi = num_v as i32;
26656            let __s_lb = self.gpu.stream();
26657            let mut lb = __s_lb.launch_builder(&f);
26658            lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
26659            unsafe {
26660                lb.launch(cfg)?;
26661            }
26662        }
26663        Ok(())
26664    }
26665
26666    /// varlen bf16 mirrors over the gdnseq_t table (which: 0 = k_l2 -> kb16, 1 = w -> wb16).
26667    pub fn gdn_mirror_vl8(
26668        &self,
26669        seqs: &[GdnSeqVl],
26670        n_head: usize,
26671        which: i32,
26672        hk: usize,
26673    ) -> Result<(), Box<dyn std::error::Error>> {
26674        let b = seqs.len();
26675        assert!(b >= 1 && b <= 8);
26676        let mut packed = [GdnSeqVl::default(); 8];
26677        packed[..b].copy_from_slice(seqs);
26678        let v = GdnVl8(packed);
26679        let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
26680        let max_n = seqs
26681            .iter()
26682            .map(|s| {
26683                if which == 0 {
26684                    s.t as i64 * ept as i64
26685                } else {
26686                    s.nc as i64 * ept as i64 * 32
26687                }
26688            })
26689            .max()
26690            .unwrap();
26691        let f = self.func("gdn_mirror_vl");
26692        let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
26693        let cfg = LaunchConfig {
26694            grid_dim: (blocks, 1, b as u32),
26695            block_dim: (256, 1, 1),
26696            shared_mem_bytes: 0,
26697        };
26698        let __s_lb = self.gpu.stream();
26699        let mut lb = __s_lb.launch_builder(&f);
26700        lb.arg(&v).arg(&ept).arg(&which);
26701        unsafe {
26702            lb.launch(cfg)?;
26703        }
26704        Ok(())
26705    }
26706
26707    /// varlen gated-norm tail (+f16out) — one launch replaces B gated_rmsnorm calls.
26708    pub fn gdn_tail_vl8(
26709        &self,
26710        seqs: &[GdnPrepVl],
26711        norm_w: &CudaSlice<f32>,
26712        d_state: usize,
26713        num_v: usize,
26714        eps: f32,
26715    ) -> Result<(), Box<dyn std::error::Error>> {
26716        let b = seqs.len();
26717        assert!(b >= 1 && b <= 8);
26718        let mut packed = [GdnPrepVl::default(); 8];
26719        packed[..b].copy_from_slice(seqs);
26720        let v = GdnPrepVl8(packed);
26721        let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
26722        let f = self.func("gated_rmsnorm_f16out_vl");
26723        // block_dim MUST match gated_rmsnorm's (128): the reduction tree order pins the scale
26724        let cfg = LaunchConfig {
26725            grid_dim: (max_t * num_v as u32, 1, b as u32),
26726            block_dim: (128, 1, 1),
26727            shared_mem_bytes: 0,
26728        };
26729        let (dsi, nvi) = (d_state as i32, num_v as i32);
26730        let __s_lb = self.gpu.stream();
26731        let mut lb = __s_lb.launch_builder(&f);
26732        lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
26733        unsafe {
26734            lb.launch(cfg)?;
26735        }
26736        Ok(())
26737    }
26738
26739    /// Raw device address helpers for the varlen by-value arg struct (single-stream
26740    /// launches; every buffer outlives the call — the f16 FFI discipline).
26741    pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
26742        use cudarc::driver::DevicePtr;
26743        let s = self.gpu.stream();
26744        let (p, _g) = x.device_ptr(&s);
26745        p as u64
26746    }
26747    pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
26748        use cudarc::driver::DevicePtrMut;
26749        let s = self.gpu.stream();
26750        let (p, _g) = x.device_ptr_mut(&s);
26751        p as u64
26752    }
26753    pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
26754        use cudarc::driver::DevicePtr;
26755        let s = self.gpu.stream();
26756        let (p, _g) = x.device_ptr(&s);
26757        p as u64
26758    }
26759    pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
26760        use cudarc::driver::DevicePtr;
26761        let s = self.gpu.stream();
26762        let (p, _g) = x.device_ptr(&s);
26763        p as u64
26764    }
26765
26766    /// task #18: the varlen K4+K5 pair — TWO launches run every sequence's state pass
26767    /// and output pass (grid gains a seq dim; per-block math identical to the per-seq
26768    /// launches, so this is strictly bit-gateable against them).
26769    pub fn gdn_chunk_vl8(
26770        &self,
26771        seqs: &[GdnSeqVl],
26772        n_head: usize,
26773        scale: f32,
26774        hk: usize,
26775        wq: Option<&GdnWVl8>,
26776    ) -> Result<(), Box<dyn std::error::Error>> {
26777        const NSPLIT: u32 = 4;
26778        let b = seqs.len();
26779        assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
26780        let mut packed = [GdnSeqVl::default(); 8];
26781        packed[..b].copy_from_slice(seqs);
26782        let v = GdnVl8(packed);
26783        let (hi, ci) = (n_head as i32, 32i32);
26784        let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
26785        let hki = hk as i32;
26786        if let Some(w) = wq {
26787            // K4+K5 fused wgmma vl twin: one launch, Y/Ssnap never materialized.
26788            let f = self.func("gdn_k45_wgmma_vl");
26789            let cfg = LaunchConfig {
26790                grid_dim: (n_head as u32, NSPLIT, b as u32),
26791                block_dim: (256, 1, 1),
26792                shared_mem_bytes: 0,
26793            };
26794            let __s_lb = self.gpu.stream();
26795            let mut lb = __s_lb.launch_builder(&f);
26796            lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
26797            unsafe {
26798                lb.launch(cfg)?;
26799            }
26800            let _ = max_nc;
26801            return Ok(());
26802        }
26803        {
26804            let f = self.func("gdn_chunk_state_mma_vl");
26805            let cfg = LaunchConfig {
26806                grid_dim: (n_head as u32, NSPLIT, b as u32),
26807                block_dim: (256, 1, 1),
26808                shared_mem_bytes: 0,
26809            };
26810            let __s_lb = self.gpu.stream();
26811            let mut lb = __s_lb.launch_builder(&f);
26812            lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
26813            unsafe {
26814                lb.launch(cfg)?;
26815            }
26816        }
26817        {
26818            let f = self.func("gdn_chunk_output_mma_vl");
26819            let cfg = LaunchConfig {
26820                grid_dim: (max_nc, n_head as u32, b as u32),
26821                block_dim: (256, 1, 1),
26822                shared_mem_bytes: 0,
26823            };
26824            let __s_lb = self.gpu.stream();
26825            let mut lb = __s_lb.launch_builder(&f);
26826            lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
26827            unsafe {
26828                lb.launch(cfg)?;
26829            }
26830        }
26831        Ok(())
26832    }
26833    pub fn gdn_scan_chunked(
26834        &self,
26835        q: &CudaSlice<f32>,
26836        k: &CudaSlice<f32>,
26837        v: &CudaSlice<f32>,
26838        g: &CudaSlice<f32>,
26839        beta: &CudaSlice<f32>,
26840        kb16_pre: Option<&CudaSlice<u8>>,
26841        qb16_pre: Option<&CudaSlice<u8>>,
26842        state_in: &CudaSlice<f32>,
26843        state_out: &mut CudaSlice<f32>,
26844        o: &mut CudaSlice<f32>,
26845        n_head: usize,
26846        t: usize,
26847        scale: f32,
26848        c: usize,
26849        hk: usize,
26850    ) -> Result<(), Box<dyn std::error::Error>> {
26851        const D: usize = 128;
26852        const NSPLIT: u32 = 4;
26853        assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
26854        let h = n_head;
26855        let nc = (t + c - 1) / c;
26856        let (hi, ti, ci) = (h as i32, t as i32, c as i32);
26857        // mirror-fold (round 27): on the mma path W's bf16 twin is emitted by K3's store
26858        // (wb16 pre-allocated and threaded through k123) and k's by the producer l2 when
26859        // the caller hands `kb16_pre` — both standalone mirror passes disappear.
26860        // default mirrors gdn_mma_enabled (incl. the sm_120a-build ON, lane/moeprime-nvfp4-direct)
26861        let gdn_mma_pre = !portable_mma_gated()
26862            && c == 32
26863            && match std::env::var("MEMRA_GDN_MMA").as_deref() {
26864                Ok("1") => true,
26865                Ok("0") => false,
26866                _ => gdn_mma_default_on(),
26867            };
26868        let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
26869            Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
26870        } else {
26871            None
26872        };
26873        // K2-wgmma pre-work (MEMRA_GDN_WGMMA): the kb16/qb16 mirrors hoist ABOVE K123 so
26874        // K2 rides them via cp.async; K2 writes the pre-masked Pb16 directly (the
26875        // gdn_p_bf16_masked pass and the in-branch mirror builds disappear).
26876        // mirrors gdn_wgmma_on: hard Hopper-build gate (empty wgmma bodies elsewhere)
26877        let gdn_wgmma_pre = cfg!(memra_hopper_mma)
26878            && gdn_mma_pre
26879            && std::env::var("MEMRA_GDN_WGMMA").as_deref() != Ok("0");
26880        let nk = t * hk * D;
26881        let mut kb16_local: Option<CudaSlice<u8>> = None;
26882        if gdn_mma_pre && kb16_pre.is_none() {
26883            let mut kb = self.alloc_u8_uninit(nk * 2)?;
26884            let f = self.func("f32_to_bf16_bulk");
26885            let n2 = nk as i64;
26886            let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
26887            let __s_b = self.gpu.stream();
26888            let mut b = __s_b.launch_builder(&f);
26889            b.arg(k).arg(&mut kb).arg(&n2);
26890            unsafe {
26891                b.launch(cfg2)?;
26892            }
26893            kb16_local = Some(kb);
26894        }
26895        let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
26896        if let Some(kb) = kb16_pre {
26897            assert!(kb.len() >= nk * 2, "kb16_pre too small");
26898        }
26899        let mut qb16: Option<CudaSlice<u8>> = None;
26900        let mut pb16: Option<CudaSlice<u8>> = None;
26901        if gdn_wgmma_pre {
26902            // mirror-fold (round 35): prep's l2 v2 emits qb16 in-epilogue (kb16 pattern);
26903            // the standalone bulk cvt only serves callers without the prep mirror.
26904            if qb16_pre.is_none() {
26905                let mut qb = self.alloc_u8_uninit(nk * 2)?;
26906                let f = self.func("f32_to_bf16_bulk");
26907                let n2 = nk as i64;
26908                let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
26909                let __s_b = self.gpu.stream();
26910                let mut b = __s_b.launch_builder(&f);
26911                b.arg(q).arg(&mut qb).arg(&n2);
26912                unsafe {
26913                    b.launch(cfg2)?;
26914                }
26915                qb16 = Some(qb);
26916            } else if let Some(qb) = qb16_pre {
26917                assert!(qb.len() >= nk * 2, "qb16_pre too small");
26918            }
26919            pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
26920        }
26921        let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
26922        let k2w = if gdn_wgmma_pre {
26923            Some((
26924                *qb16_ref0.as_ref().unwrap(),
26925                *kb16_ref0.as_ref().unwrap(),
26926                pb16.as_mut().unwrap(),
26927            ))
26928        } else {
26929            None
26930        };
26931        let (gcum, p, u, w) =
26932            self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
26933        let _ = &w;
26934        let mut y = self.uninit(nc * h * c * D)?;
26935        let mut ssnap = self.uninit(nc * h * D * D)?; // chunk-start state snapshots (K5 phase 1)
26936        // K4-MMA seam (MEMRA_GDN_MMA; harness verdict 1.75x — tools/bench_gdn_k4.cu, ledger
26937        // 2026-07-26): M in mma accumulator fragments, bf16 W/k mirrors through a cp.async
26938        // ring. C==32 only (the kernel's tile). PROMOTED default-ON on the Hopper lane
26939        // after the STATE-CARRY battery (2026-07-26): 2048-token prime (64 in-kernel state
26940        // carries) -> 256 greedy decode tokens IDENTICAL to f32 on 3 seeds, AND chunked-
26941        // continuation prime (MEMRA_PRIME_CHUNK=512, 4 cross-call carries via cache.recur)
26942        // IDENTICAL on 2 seeds; plus argmax MATCH, pp512 +3.5% (17286), oracle out
26943        // mean_rel ~1e-4. kernel-check pins BOTH configs (f32 tight band forced =0; mma
26944        // band 8e-2/8e-1 vs f64 truth). =0 reverts; portable stays f32. NOT read via
26945        // OnceLock ON PURPOSE: kernel-check toggles the env per call to pin both forms.
26946        // default mirrors gdn_mma_enabled (incl. the sm_120a-build ON) — all three read
26947        // sites must agree or the pre-work arms while the scan takes the scalar route.
26948        let gdn_mma = !portable_mma_gated()
26949            && c == 32
26950            && match std::env::var("MEMRA_GDN_MMA").as_deref() {
26951                Ok("1") => true,
26952                Ok("0") => false,
26953                _ => gdn_mma_default_on(),
26954            };
26955        if gdn_mma {
26956            let wb16 = wb16_pre
26957                .take()
26958                .expect("mma path pre-allocates wb16 (K3 store fold)");
26959            let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
26960            // K4+K5 FUSED wgmma seam (MEMRA_GDN_WGMMA, task #22; harness verdict
26961            // tools/bench_gdn_wgmma.cu v5, ledger 1f08b997: in-band Y 1.07e-2 / state
26962            // 1.03e-2 / O 1.08e-2, 91.3us vs 70.4 K4-only at H=32 T=512). K5's output
26963            // pass runs inside the persistent-M kernel; Y and Ssnap are never
26964            // materialized. New numeric class (gk folds into k^T instead of ys) —
26965            // explicit opt-in until the state-carry battery promotes it. Env read per
26966            // call (kernel-check pins configs by toggling env, GDN_MMA precedent).
26967            // PROMOTED default-ON hopper (2026-07-27): full battery green — harness
26968            // in-band, argmax gate PASS, 3-seed greedy IDENTICAL after ~2k prime,
26969            // chunked-continuation IDENTICAL, kernel-check + decode-batch gates green,
26970            // official prefill lane +0.74% interleaved x5 (5/5 rounds). =0 reverts.
26971            if gdn_wgmma_pre {
26972                // qb16/pb16 pre-built above K123 (K2-wgmma wrote the masked Pb16).
26973                let qb16 = qb16_ref0.unwrap();
26974                let pb16 = pb16.as_ref().unwrap();
26975                {
26976                    let f = self.func("gdn_k45_wgmma");
26977                    let cfg = LaunchConfig {
26978                        grid_dim: (h as u32, 4, 1),
26979                        block_dim: (256, 1, 1),
26980                        shared_mem_bytes: 0,
26981                    };
26982                    let hki = hk as i32;
26983                    let __s_b = self.gpu.stream();
26984                    let mut b = __s_b.launch_builder(&f);
26985                    b.arg(kb16_ref)
26986                        .arg(&gcum)
26987                        .arg(beta)
26988                        .arg(&u)
26989                        .arg(&wb16)
26990                        .arg(qb16)
26991                        .arg(pb16)
26992                        .arg(o)
26993                        .arg(&scale)
26994                        .arg(state_in)
26995                        .arg(&mut *state_out)
26996                        .arg(&hi)
26997                        .arg(&ti)
26998                        .arg(&ci)
26999                        .arg(&hki);
27000                    unsafe {
27001                        b.launch(cfg)?;
27002                    }
27003                }
27004                return Ok(());
27005            }
27006            // COUPLED PAIR: K4-mma writes Y and Ssnap as bf16 (their only consumer is
27007            // K5-mma, which rounds to bf16 regardless — identical numerics, half the
27008            // traffic; harness K5 63.0 -> 35.3us). Fresh bf16 buffers replace the f32 ones.
27009            let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
27010            let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
27011            {
27012                let f = self.func("gdn_chunk_state_mma");
27013                let cfg = LaunchConfig {
27014                    grid_dim: (h as u32, NSPLIT, 1),
27015                    block_dim: (256, 1, 1),
27016                    shared_mem_bytes: 0,
27017                };
27018                let hki = hk as i32;
27019                let __s_b = self.gpu.stream();
27020                let mut b = __s_b.launch_builder(&f);
27021                b.arg(kb16_ref)
27022                    .arg(&gcum)
27023                    .arg(beta)
27024                    .arg(&u)
27025                    .arg(&wb16)
27026                    .arg(&mut y16)
27027                    .arg(&mut ssnap16)
27028                    .arg(state_in)
27029                    .arg(&mut *state_out)
27030                    .arg(&hi)
27031                    .arg(&ti)
27032                    .arg(&ci)
27033                    .arg(&hki);
27034                unsafe {
27035                    b.launch(cfg)?;
27036                }
27037            }
27038            {
27039                // K5-mma (bf16 St/Y consumers)
27040                let f = self.func("gdn_chunk_output_mma");
27041                let jt = ((c + 31) / 32) as u32;
27042                let cfg = LaunchConfig {
27043                    grid_dim: (nc as u32, h as u32, jt),
27044                    block_dim: (256, 1, 1),
27045                    shared_mem_bytes: 0,
27046                };
27047                let hki = hk as i32;
27048                let __s_b = self.gpu.stream();
27049                let mut b = __s_b.launch_builder(&f);
27050                b.arg(q)
27051                    .arg(&gcum)
27052                    .arg(&p)
27053                    .arg(&y16)
27054                    .arg(&ssnap16)
27055                    .arg(o)
27056                    .arg(&hi)
27057                    .arg(&ti)
27058                    .arg(&ci)
27059                    .arg(&scale)
27060                    .arg(&hki);
27061                unsafe {
27062                    b.launch(cfg)?;
27063                }
27064            }
27065            return Ok(());
27066        }
27067        {
27068            // K4 (sequential over chunks inside; blocks col-partition the state)
27069            let f = self.func("gdn_chunk_state_f32");
27070            let cfg = LaunchConfig {
27071                grid_dim: (h as u32, NSPLIT, 1),
27072                block_dim: (256, 1, 1),
27073                shared_mem_bytes: 0,
27074            };
27075            let __s_b = self.gpu.stream();
27076            let mut b = __s_b.launch_builder(&f);
27077            b.arg(k)
27078                .arg(&gcum)
27079                .arg(beta)
27080                .arg(&u)
27081                .arg(&w)
27082                .arg(&mut y)
27083                .arg(&mut ssnap)
27084                .arg(state_in)
27085                .arg(&mut *state_out)
27086                .arg(&hi)
27087                .arg(&ti)
27088                .arg(&ci);
27089            unsafe {
27090                b.launch(cfg)?;
27091            }
27092        }
27093        {
27094            // K5 (j-blocked: grid.z = 32-row output blocks per chunk; writes o fully)
27095            let f = self.func("gdn_chunk_output_f32");
27096            let jt = ((c + 31) / 32) as u32;
27097            let cfg = LaunchConfig {
27098                grid_dim: (nc as u32, h as u32, jt),
27099                block_dim: (256, 1, 1),
27100                shared_mem_bytes: 0,
27101            };
27102            let __s_b = self.gpu.stream();
27103            let mut b = __s_b.launch_builder(&f);
27104            b.arg(q)
27105                .arg(&gcum)
27106                .arg(&p)
27107                .arg(&y)
27108                .arg(&ssnap)
27109                .arg(o)
27110                .arg(&hi)
27111                .arg(&ti)
27112                .arg(&ci)
27113                .arg(&scale);
27114            unsafe {
27115                b.launch(cfg)?;
27116            }
27117        }
27118        Ok(())
27119    }
27120
27121    /// PREFILL GDN scan dispatch (the A4 seam): chunked WY form when enabled and T is in the
27122    /// batched-prefill regime, else the sequential scan. Callers: hybrid_forward::linear_attn
27123    /// (forward/forward_last) + linear_attn_prime (prime_cache). Decode (T=1) and the spec
27124    /// verify call `gdn_scan_s128` DIRECTLY — the decode==verify dispatch identity is untouched.
27125    ///
27126    /// MEMRA_GDN_DIFF=1: numerical-oracle mode — runs BOTH forms on the same inputs, prints the
27127    /// per-call (== per-layer, in call order) output/state error distribution, and keeps the
27128    /// SEQUENTIAL results so the run stays on the shipped path (stage-1 prototype evidence).
27129    #[allow(clippy::too_many_arguments)]
27130    #[allow(clippy::too_many_arguments)]
27131    pub fn gdn_scan_prefill(
27132        &self,
27133        q: &CudaSlice<f32>,
27134        k: &CudaSlice<f32>,
27135        v: &CudaSlice<f32>,
27136        g: &CudaSlice<f32>,
27137        beta: &CudaSlice<f32>,
27138        kb16_pre: Option<&CudaSlice<u8>>,
27139        qb16_pre: Option<&CudaSlice<u8>>,
27140        state_in: &CudaSlice<f32>,
27141        state_out: &mut CudaSlice<f32>,
27142        o: &mut CudaSlice<f32>,
27143        n_head: usize,
27144        t: usize,
27145        scale: f32,
27146        hk: usize,
27147    ) -> Result<(), Box<dyn std::error::Error>> {
27148        if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
27149            assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
27150            return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
27151        }
27152        if Self::gdn_chunked_enabled() && t >= 16 {
27153            self.gdn_scan_chunked(
27154                q,
27155                k,
27156                v,
27157                g,
27158                beta,
27159                kb16_pre,
27160                qb16_pre,
27161                state_in,
27162                state_out,
27163                o,
27164                n_head,
27165                t,
27166                scale,
27167                Self::gdn_chunk_size(),
27168                hk,
27169            )
27170        } else {
27171            assert!(
27172                hk == n_head,
27173                "s128 scan is broadcast-only (prep guarantees by predicate)"
27174            );
27175            self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
27176        }
27177    }
27178
27179    /// Stage-1 oracle: run sequential AND chunked, report per-call error stats, keep sequential.
27180    #[allow(clippy::too_many_arguments)]
27181    fn gdn_scan_diff(
27182        &self,
27183        q: &CudaSlice<f32>,
27184        k: &CudaSlice<f32>,
27185        v: &CudaSlice<f32>,
27186        g: &CudaSlice<f32>,
27187        beta: &CudaSlice<f32>,
27188        state_in: &CudaSlice<f32>,
27189        state_out: &mut CudaSlice<f32>,
27190        o: &mut CudaSlice<f32>,
27191        n_head: usize,
27192        t: usize,
27193        scale: f32,
27194    ) -> Result<(), Box<dyn std::error::Error>> {
27195        static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
27196        let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
27197        let mut o_c = self.uninit(o.len())?;
27198        let mut st_c = self.uninit(state_out.len())?;
27199        self.gdn_scan_chunked(
27200            q,
27201            k,
27202            v,
27203            g,
27204            beta,
27205            None,
27206            None,
27207            state_in,
27208            &mut st_c,
27209            &mut o_c,
27210            n_head,
27211            t,
27212            scale,
27213            Self::gdn_chunk_size(),
27214            n_head,
27215        )?;
27216        self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
27217        let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
27218        let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
27219        let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
27220            let mut max_abs = 0f32;
27221            let mut max_rel = 0f32;
27222            let mut sum_rel = 0f64;
27223            for (x, y) in a.iter().zip(b) {
27224                let ad = (x - y).abs();
27225                let rel = ad / x.abs().max(y.abs()).max(1e-3);
27226                if ad > max_abs {
27227                    max_abs = ad;
27228                }
27229                if rel > max_rel {
27230                    max_rel = rel;
27231                }
27232                sum_rel += rel as f64;
27233            }
27234            (max_abs, max_rel, sum_rel / a.len() as f64)
27235        };
27236        let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
27237        let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
27238        println!(
27239            "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
27240                  state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
27241            Self::gdn_chunk_size()
27242        );
27243        Ok(())
27244    }
27245
27246    /// softplus-based g_log: g_log[h,t] = a[h] * softplus(alpha[h,t] + dt_bias[h]). a pre-negated.
27247    pub fn gdn_glog(
27248        &self,
27249        alpha: &CudaSlice<f32>,
27250        dt_bias: &CudaSlice<f32>,
27251        a: &CudaSlice<f32>,
27252        g_log: &mut CudaSlice<f32>,
27253        n_head: usize,
27254        t: usize,
27255    ) -> Result<(), Box<dyn std::error::Error>> {
27256        let f = self.func("gdn_glog_f32");
27257        let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
27258        let (h, ti) = (n_head as i32, t as i32);
27259        let __s_b = self.gpu.stream();
27260        let mut b = __s_b.launch_builder(&f);
27261        b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
27262        unsafe {
27263            b.launch(cfg)?;
27264        }
27265        Ok(())
27266    }
27267
27268    /// view twins (task #16): the batched prime's GDN core reads the CONCAT projection
27269    /// buffers at row offsets (CudaView) — same kernels, same values, no split copies.
27270    pub fn sigmoid_v(
27271        &self,
27272        x: &cudarc::driver::CudaView<f32>,
27273        y: &mut CudaSlice<f32>,
27274        n: usize,
27275    ) -> Result<(), Box<dyn std::error::Error>> {
27276        let f = self.func("sigmoid_f32");
27277        let cfg = LaunchConfig::for_num_elems(n as u32);
27278        let ni = n as i32;
27279        let __s_b = self.gpu.stream();
27280        let mut b = __s_b.launch_builder(&f);
27281        b.arg(x).arg(y).arg(&ni);
27282        unsafe {
27283            b.launch(cfg)?;
27284        }
27285        Ok(())
27286    }
27287
27288    pub fn gdn_glog_v(
27289        &self,
27290        alpha: &cudarc::driver::CudaView<f32>,
27291        dt_bias: &CudaSlice<f32>,
27292        a: &CudaSlice<f32>,
27293        g_log: &mut CudaSlice<f32>,
27294        n_head: usize,
27295        t: usize,
27296    ) -> Result<(), Box<dyn std::error::Error>> {
27297        let f = self.func("gdn_glog_f32");
27298        let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
27299        let (h, ti) = (n_head as i32, t as i32);
27300        let __s_b = self.gpu.stream();
27301        let mut b = __s_b.launch_builder(&f);
27302        b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
27303        unsafe {
27304            b.launch(cfg)?;
27305        }
27306        Ok(())
27307    }
27308
27309    pub fn sigmoid(
27310        &self,
27311        x: &CudaSlice<f32>,
27312        y: &mut CudaSlice<f32>,
27313        n: usize,
27314    ) -> Result<(), Box<dyn std::error::Error>> {
27315        let f = self.func("sigmoid_f32");
27316        let cfg = LaunchConfig::for_num_elems(n as u32);
27317        let ni = n as i32;
27318        let __s_b = self.gpu.stream();
27319        let mut b = __s_b.launch_builder(&f);
27320        b.arg(x).arg(y).arg(&ni);
27321        unsafe {
27322            b.launch(cfg)?;
27323        }
27324        Ok(())
27325    }
27326
27327    /// attn out-gate fused epilogue (task #17): dst = a * sigmoid(g) + fp16 twin, one launch
27328    /// (replaces sigmoid + mul + convert). Bit-identical class.
27329    pub fn sig_mul_f16out(
27330        &self,
27331        a: &CudaSlice<f32>,
27332        g: &CudaSlice<f32>,
27333        dst: &mut CudaSlice<f32>,
27334        dst16: &mut CudaSlice<u8>,
27335        n: usize,
27336    ) -> Result<(), Box<dyn std::error::Error>> {
27337        let f = self.func("sig_mul_f16out_f32");
27338        let cfg = LaunchConfig::for_num_elems(n as u32);
27339        let ni = n as i32;
27340        let __s_b = self.gpu.stream();
27341        let mut b = __s_b.launch_builder(&f);
27342        b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
27343        unsafe {
27344            b.launch(cfg)?;
27345        }
27346        Ok(())
27347    }
27348
27349    /// step35 (Step-3.7-Flash) SEPARATE head-wise attention gate: one scalar per query head,
27350    /// broadcast over head_dim. `dst = a * sigmoid(g)` where `a`/`dst` are `[head_dim, n_head, T]`
27351    /// (the `q_gate_split` layout) and `g` is the PRE-sigmoid `attn_gate` projection output in
27352    /// token-major `[T, n_head]`. `dst16` is the optional fp16 operand for wo (None -> skipped).
27353    ///
27354    /// NOT interchangeable with `sig_mul_f16out`, which gates FULL WIDTH (qwen35 packs one gate
27355    /// value per (head, dim) element inside wq). Using this for that, or that for this, silently
27356    /// applies the wrong number of distinct gate values.
27357    #[allow(clippy::too_many_arguments)]
27358    pub fn attn_head_gate(
27359        &self,
27360        a: &CudaSlice<f32>,
27361        g: &CudaSlice<f32>,
27362        dst: &mut CudaSlice<f32>,
27363        dst16: Option<&mut CudaSlice<u8>>,
27364        head_dim: usize,
27365        n_head: usize,
27366        t: usize,
27367    ) -> Result<(), Box<dyn std::error::Error>> {
27368        let f = self.func("attn_head_gate_f32");
27369        let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
27370        let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
27371        // nullable device pointer by value (0 = skip), same convention as `l2_norm_pp`.
27372        let d16: u64 = match dst16 {
27373            Some(d) => self.addr_u8(d),
27374            None => 0,
27375        };
27376        let __s_b = self.gpu.stream();
27377        let mut b = __s_b.launch_builder(&f);
27378        b.arg(a)
27379            .arg(g)
27380            .arg(dst)
27381            .arg(&d16)
27382            .arg(&hd)
27383            .arg(&nh)
27384            .arg(&ti);
27385        unsafe {
27386            b.launch(cfg)?;
27387        }
27388        Ok(())
27389    }
27390
27391    /// step35 CLAMPED SwiGLU: `dst = min(silu(gate*gs), limit) * clamp(up*us, +-limit)`.
27392    /// Verbatim from llama.cpp `llama-graph.cpp:2146-2165` (routed, `swiglu_clamp_exp`) and
27393    /// `:1751-1770` (shared, `swiglu_clamp_shexp`), non-DEEPSEEK4 branch.
27394    ///
27395    /// This is NOT `swigluoai_mul_scaled`: that one clamps the gate BEFORE swish and multiplies by
27396    /// `(1 + clamp(up))`. Caller MUST check `limit > 1e-6` (upstream's eps gate) and use the plain
27397    /// `silu_mul_scaled` path otherwise — at limit=0 this kernel would clamp every positive
27398    /// activation to zero. On Step-3.7-Flash only layers 43 (7.0) and 44 (16.0) have a live limit.
27399    #[allow(clippy::too_many_arguments)]
27400    pub fn swiglu_clamped_mul_scaled(
27401        &self,
27402        gate: &CudaSlice<f32>,
27403        up: &CudaSlice<f32>,
27404        gs: f32,
27405        us: f32,
27406        limit: f32,
27407        dst: &mut CudaSlice<f32>,
27408        n: usize,
27409    ) -> Result<(), Box<dyn std::error::Error>> {
27410        debug_assert!(
27411            limit > 1e-6,
27412            "swiglu_clamped needs a live limit; use silu_mul_scaled"
27413        );
27414        let f = self.func("swiglu_clamped_mul_scaled_f32");
27415        let cfg = LaunchConfig::for_num_elems(n as u32);
27416        let ni = n as i32;
27417        let __s_b = self.gpu.stream();
27418        let mut b = __s_b.launch_builder(&f);
27419        b.arg(gate)
27420            .arg(up)
27421            .arg(&gs)
27422            .arg(&us)
27423            .arg(&limit)
27424            .arg(dst)
27425            .arg(&ni);
27426        unsafe {
27427            b.launch(cfg)?;
27428        }
27429        Ok(())
27430    }
27431
27432    /// gated RMSNorm: dst = RMSNorm(o, w[ncols]) * silu(z), per row of ncols. nrows blocks.
27433    pub fn gated_rmsnorm(
27434        &self,
27435        o: &CudaSlice<f32>,
27436        w: &CudaSlice<f32>,
27437        z: &CudaSlice<f32>,
27438        dst: &mut CudaSlice<f32>,
27439        ncols: usize,
27440        nrows: usize,
27441        eps: f32,
27442    ) -> Result<(), Box<dyn std::error::Error>> {
27443        let f = self.func("gated_rmsnorm_f32");
27444        let cfg = LaunchConfig {
27445            grid_dim: (nrows as u32, 1, 1),
27446            block_dim: (128, 1, 1),
27447            shared_mem_bytes: 0,
27448        };
27449        let (nc, e) = (ncols as i32, eps);
27450        let __s_b = self.gpu.stream();
27451        let mut b = __s_b.launch_builder(&f);
27452        b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
27453        unsafe {
27454            b.launch(cfg)?;
27455        }
27456        Ok(())
27457    }
27458
27459    /// f16out twin of `gated_rmsnorm` (task #17): epilogue also emits the fp16 operand for
27460    /// the ssm_out GEMM. Bit-identical class (same floats + the cvt kernel's __float2half).
27461    pub fn gated_rmsnorm_f16out(
27462        &self,
27463        o: &CudaSlice<f32>,
27464        w: &CudaSlice<f32>,
27465        z: &CudaSlice<f32>,
27466        dst: &mut CudaSlice<f32>,
27467        dst16: &mut CudaSlice<u8>,
27468        ncols: usize,
27469        nrows: usize,
27470        eps: f32,
27471    ) -> Result<(), Box<dyn std::error::Error>> {
27472        let f = self.func("gated_rmsnorm_f16out_f32");
27473        // block_dim MUST match gated_rmsnorm's (128): the reduction tree order pins the scale
27474        let cfg = LaunchConfig {
27475            grid_dim: (nrows as u32, 1, 1),
27476            block_dim: (128, 1, 1),
27477            shared_mem_bytes: 0,
27478        };
27479        let (nc, e) = (ncols as i32, eps);
27480        let __s_b = self.gpu.stream();
27481        let mut b = __s_b.launch_builder(&f);
27482        b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
27483        unsafe {
27484            b.launch(cfg)?;
27485        }
27486        Ok(())
27487    }
27488
27489    /// add+RMSNorm emitting the f32 normed row AND its q8_1 quantization in one launch (the MoE
27490    /// layer input: z feeds the router matmul as f32, the expert dp4a as q8_1). BIT-IDENTICAL to
27491    /// add_rms_norm + quantize_q8_1. Returns (q, d) alongside the caller-provided res/z buffers.
27492    #[allow(clippy::too_many_arguments)]
27493    pub fn add_rms_norm_zq8(
27494        &self,
27495        a: &CudaSlice<f32>,
27496        b_in: &CudaSlice<f32>,
27497        w: &CudaSlice<f32>,
27498        res: &mut CudaSlice<f32>,
27499        z: &mut CudaSlice<f32>,
27500        ncols: usize,
27501        nrows: usize,
27502        eps: f32,
27503    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
27504        assert!(ncols % 32 == 0);
27505        let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
27506        let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
27507        let f = self.func("add_rms_norm_zq8");
27508        let cfg = LaunchConfig {
27509            grid_dim: (nrows as u32, 1, 1),
27510            block_dim: (1024, 1, 1),
27511            shared_mem_bytes: 0,
27512        };
27513        let (nc, ep) = (ncols as i32, eps);
27514        let __s_b = self.gpu.stream();
27515        let mut b = __s_b.launch_builder(&f);
27516        b.arg(a)
27517            .arg(b_in)
27518            .arg(w)
27519            .arg(res)
27520            .arg(z)
27521            .arg(&mut q)
27522            .arg(&mut d)
27523            .arg(&nc)
27524            .arg(&ep);
27525        unsafe {
27526            b.launch(cfg)?;
27527        }
27528        Ok((q, d))
27529    }
27530
27531    /// gated RMSNorm emitting q8_1 directly (fused quantize epilogue) — the ssm_out matvec input.
27532    /// BIT-IDENTICAL bytes to gated_rmsnorm + quantize_q8_1 (ncols % 32 == 0; blocks never straddle
27533    /// rows). Saves one launch per linear-attn layer (36/token on the 9B).
27534    /// z-view twins of gated_rmsnorm(+f16out) — task #16 batched-prime split removal.
27535    pub fn gated_rmsnorm_zv(
27536        &self,
27537        o: &CudaSlice<f32>,
27538        w: &CudaSlice<f32>,
27539        z: &cudarc::driver::CudaView<f32>,
27540        dst: &mut CudaSlice<f32>,
27541        ncols: usize,
27542        nrows: usize,
27543        eps: f32,
27544    ) -> Result<(), Box<dyn std::error::Error>> {
27545        let f = self.func("gated_rmsnorm_f32");
27546        let cfg = LaunchConfig {
27547            grid_dim: (nrows as u32, 1, 1),
27548            block_dim: (128, 1, 1),
27549            shared_mem_bytes: 0,
27550        };
27551        let (nc, e) = (ncols as i32, eps);
27552        let __s_b = self.gpu.stream();
27553        let mut b = __s_b.launch_builder(&f);
27554        b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
27555        unsafe {
27556            b.launch(cfg)?;
27557        }
27558        Ok(())
27559    }
27560
27561    pub fn gated_rmsnorm_f16out_zv(
27562        &self,
27563        o: &CudaSlice<f32>,
27564        w: &CudaSlice<f32>,
27565        z: &cudarc::driver::CudaView<f32>,
27566        dst: &mut CudaSlice<f32>,
27567        dst16: &mut CudaSlice<u8>,
27568        ncols: usize,
27569        nrows: usize,
27570        eps: f32,
27571    ) -> Result<(), Box<dyn std::error::Error>> {
27572        let f = self.func("gated_rmsnorm_f16out_f32");
27573        // block_dim MUST match gated_rmsnorm's (128): the reduction tree order pins the scale
27574        let cfg = LaunchConfig {
27575            grid_dim: (nrows as u32, 1, 1),
27576            block_dim: (128, 1, 1),
27577            shared_mem_bytes: 0,
27578        };
27579        let (nc, e) = (ncols as i32, eps);
27580        let __s_b = self.gpu.stream();
27581        let mut b = __s_b.launch_builder(&f);
27582        b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
27583        unsafe {
27584            b.launch(cfg)?;
27585        }
27586        Ok(())
27587    }
27588
27589    pub fn gated_rmsnorm_q8_1(
27590        &self,
27591        o: &CudaSlice<f32>,
27592        w: &CudaSlice<f32>,
27593        z: &CudaSlice<f32>,
27594        ncols: usize,
27595        nrows: usize,
27596        eps: f32,
27597    ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
27598        assert!(ncols % 32 == 0);
27599        let f = self.func("gated_rmsnorm_q8_1");
27600        let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
27601        let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
27602        let cfg = LaunchConfig {
27603            grid_dim: (nrows as u32, 1, 1),
27604            block_dim: (128, 1, 1),
27605            shared_mem_bytes: 0,
27606        };
27607        let (nc, ep) = (ncols as i32, eps);
27608        let __s_b = self.gpu.stream();
27609        let mut b = __s_b.launch_builder(&f);
27610        b.arg(o)
27611            .arg(w)
27612            .arg(z)
27613            .arg(&mut out_q)
27614            .arg(&mut out_d)
27615            .arg(&nc)
27616            .arg(&ep);
27617        unsafe {
27618            b.launch(cfg)?;
27619        }
27620        Ok((out_q, out_d))
27621    }
27622
27623    /// transpose [rows,cols] row-major -> [cols,rows] row-major.
27624    pub fn transpose(
27625        &self,
27626        inp: &CudaSlice<f32>,
27627        rows: usize,
27628        cols: usize,
27629    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
27630        let f = self.func("transpose_f32");
27631        let mut out = self.zeros(rows * cols)?;
27632        let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
27633        let (r, c) = (rows as i32, cols as i32);
27634        let __s_b = self.gpu.stream();
27635        let mut b = __s_b.launch_builder(&f);
27636        b.arg(inp).arg(&mut out).arg(&r).arg(&c);
27637        unsafe {
27638            b.launch(cfg)?;
27639        }
27640        Ok(out)
27641    }
27642
27643    /// repeat-interleave heads: in[head_dim,n_in,T] -> out[head_dim,n_out,T].
27644    pub fn repeat_heads(
27645        &self,
27646        inp: &CudaSlice<f32>,
27647        out: &mut CudaSlice<f32>,
27648        head_dim: usize,
27649        n_in: usize,
27650        n_out: usize,
27651        t: usize,
27652    ) -> Result<(), Box<dyn std::error::Error>> {
27653        let f = self.func("repeat_heads_f32");
27654        let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
27655        let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
27656        let __s_b = self.gpu.stream();
27657        let mut b = __s_b.launch_builder(&f);
27658        b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
27659        unsafe {
27660            b.launch(cfg)?;
27661        }
27662        Ok(())
27663    }
27664
27665    /// q|gate split (on-device). qf:[T, n_head*2*head_dim] -> q_out,gate_out:[head_dim,n_head,T].
27666    /// Replaces the dtoh->host-double-loop->htod in full_attn / full_attn_decode.
27667    ///
27668    /// Bounds-checked against `qf` before the launch: the kernel reads `2*head_dim*n_head*t`
27669    /// floats, and running it on a `wq` output that carries no fused gate reads 2x off the end
27670    /// (silently, on the device). A layout mismatch is a typed `FusedQGateExtent` here instead.
27671    pub fn q_gate_split(
27672        &self,
27673        qf: &CudaSlice<f32>,
27674        q_out: &mut CudaSlice<f32>,
27675        gate_out: &mut CudaSlice<f32>,
27676        head_dim: usize,
27677        n_head: usize,
27678        t: usize,
27679    ) -> Result<(), Box<dyn std::error::Error>> {
27680        memra_gguf::config::check_fused_q_gate_extent(qf.len(), head_dim, n_head, t)?;
27681        let out_need = head_dim * n_head * t;
27682        if q_out.len() < out_need || gate_out.len() < out_need {
27683            return Err(format!(
27684                "q_gate_split destinations too small: need {out_need} each, have q={} gate={}",
27685                q_out.len(),
27686                gate_out.len()
27687            )
27688            .into());
27689        }
27690        let f = self.func("q_gate_split_f32");
27691        let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
27692        let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
27693        let __s_b = self.gpu.stream();
27694        let mut b = __s_b.launch_builder(&f);
27695        b.arg(qf)
27696            .arg(q_out)
27697            .arg(gate_out)
27698            .arg(&hd)
27699            .arg(&nh)
27700            .arg(&ti);
27701        unsafe {
27702            b.launch(cfg)?;
27703        }
27704        Ok(())
27705    }
27706
27707    /// qkv->GDN repack (on-device). conv_out:[conv_dim,T] channel-major ->
27708    /// q_g/k_g/v_g:[d_state,num_v,T] with q/k head-repeat kh = vh % num_k (validated modulo mapping).
27709    /// Replaces the dtoh->host-q/k/v-repack->3x-htod in linear_attn / linear_attn_decode.
27710    pub fn qkv_to_gdn_repack(
27711        &self,
27712        conv_out: &CudaSlice<f32>,
27713        q_g: &mut CudaSlice<f32>,
27714        k_g: &mut CudaSlice<f32>,
27715        v_g: &mut CudaSlice<f32>,
27716        d_state: usize,
27717        num_v: usize,
27718        num_k: usize,
27719        key_dim: usize,
27720        t: usize,
27721    ) -> Result<(), Box<dyn std::error::Error>> {
27722        let f = self.func("qkv_to_gdn_repack_f32");
27723        let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
27724        let (ds, nv, nk, kd, ti) = (
27725            d_state as i32,
27726            num_v as i32,
27727            num_k as i32,
27728            key_dim as i32,
27729            t as i32,
27730        );
27731        let __s_b = self.gpu.stream();
27732        let mut b = __s_b.launch_builder(&f);
27733        b.arg(conv_out)
27734            .arg(q_g)
27735            .arg(k_g)
27736            .arg(v_g)
27737            .arg(&ds)
27738            .arg(&nv)
27739            .arg(&nk)
27740            .arg(&kd)
27741            .arg(&ti);
27742        unsafe {
27743            b.launch(cfg)?;
27744        }
27745        Ok(())
27746    }
27747
27748    /// conv left zero-pad (prefill from zero state). src:[conv_dim,T] -> dst:[conv_dim,T+pad],
27749    /// cols 0..pad = 0, cols pad..pad+T = src. `dst` MUST be pre-zeroed. No dtoh/host-loop/htod.
27750    pub fn conv_left_pad(
27751        &self,
27752        src: &CudaSlice<f32>,
27753        dst: &mut CudaSlice<f32>,
27754        conv_dim: usize,
27755        t: usize,
27756        pad: usize,
27757    ) -> Result<(), Box<dyn std::error::Error>> {
27758        let f = self.func("conv_left_pad_f32");
27759        let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
27760        let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
27761        let __s_b = self.gpu.stream();
27762        let mut b = __s_b.launch_builder(&f);
27763        b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
27764        unsafe {
27765            b.launch(cfg)?;
27766        }
27767        Ok(())
27768    }
27769
27770    /// conv-state assemble + ring roll (decode T=1). conv_state:[conv_dim,pad] (resident),
27771    /// qkv_col:[conv_dim] -> conv_in:[conv_dim,pad+1]; AND rolls conv_state (keep last pad cols).
27772    /// Replaces the dtoh->host-conv-ring-assemble->ring-update->htod in linear_attn_decode.
27773    pub fn conv_assemble_and_roll(
27774        &self,
27775        qkv_col: &CudaSlice<f32>,
27776        conv_state: &mut CudaSlice<f32>,
27777        conv_in: &mut CudaSlice<f32>,
27778        conv_dim: usize,
27779        pad: usize,
27780    ) -> Result<(), Box<dyn std::error::Error>> {
27781        let f = self.func("conv_assemble_and_roll_f32");
27782        let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
27783        let (cd, p) = (conv_dim as i32, pad as i32);
27784        let __s_b = self.gpu.stream();
27785        let mut b = __s_b.launch_builder(&f);
27786        b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
27787        unsafe {
27788            b.launch(cfg)?;
27789        }
27790        Ok(())
27791    }
27792
27793    /// RANK3 LEVER (conv fuse, T=1 DECODE): fused conv_assemble_and_roll + ssm_conv1d_silu in ONE
27794    /// launch. Assembles the conv window [conv_state | qkv_col] in registers, computes the depthwise
27795    /// causal conv + SiLU into `conv_out`, and rolls the ring — never materializing conv_in to HBM.
27796    /// Replaces e.conv_assemble_and_roll(...) + e.ssm_conv1d(...). BIT-IDENTICAL to that two-kernel
27797    /// sequence (same 8-wide accumulation order, same SiLU). `conv_out` is [conv_dim] (T=1).
27798    pub fn ssm_conv1d_fused_decode(
27799        &self,
27800        qkv_col: &CudaSlice<f32>,
27801        conv_state: &mut CudaSlice<f32>,
27802        w: &CudaSlice<f32>,
27803        conv_out: &mut CudaSlice<f32>,
27804        conv_dim: usize,
27805        d_conv: usize,
27806    ) -> Result<(), Box<dyn std::error::Error>> {
27807        let f = self.func("ssm_conv1d_fused_decode_f32");
27808        let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
27809        let (cd, dc) = (conv_dim as i32, d_conv as i32);
27810        let __s_b = self.gpu.stream();
27811        let mut b = __s_b.launch_builder(&f);
27812        b.arg(qkv_col)
27813            .arg(conv_state)
27814            .arg(w)
27815            .arg(conv_out)
27816            .arg(&cd)
27817            .arg(&dc);
27818        unsafe {
27819            b.launch(cfg)?;
27820        }
27821        Ok(())
27822    }
27823
27824    /// Copy a contiguous range [start, start+len) out of src into a fresh slice (device→device via host).
27825    /// Used for qkv split views. Small/rare; not perf-critical in Stage 1.
27826    pub fn slice_range(
27827        &self,
27828        src: &CudaSlice<f32>,
27829        start: usize,
27830        len: usize,
27831    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
27832        let host = self.gpu.stream().clone_dtoh(src)?;
27833        self.gpu.stream().synchronize()?;
27834        Ok(self.htod(&host[start..start + len])?)
27835    }
27836}
27837
27838#[cfg(test)]
27839mod target_dispatch_tests {
27840    use super::legacy_quant_gemm_allowed;
27841
27842    #[test]
27843    fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
27844        // sm_120a native lane
27845        assert!(legacy_quant_gemm_allowed(false, false, false));
27846        assert!(!legacy_quant_gemm_allowed(false, false, true));
27847        // pure portable lane (sm_89): gated
27848        assert!(!legacy_quant_gemm_allowed(true, false, false));
27849        assert!(!legacy_quant_gemm_allowed(true, false, true));
27850        // Hopper-MMA lane (sm_90a): portable build, int8-MMA GEMM re-admitted
27851        assert!(legacy_quant_gemm_allowed(true, true, false));
27852        assert!(!legacy_quant_gemm_allowed(true, true, true));
27853    }
27854
27855    #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
27856    #[test]
27857    fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
27858        assert!(!legacy_quant_gemm_allowed(
27859            cfg!(memra_portable_cuda),
27860            cfg!(memra_hopper_mma),
27861            false
27862        ));
27863    }
27864
27865    #[cfg(memra_hopper_mma)]
27866    #[test]
27867    fn hopper_mma_build_re_admits_legacy_quant_gemm() {
27868        assert!(legacy_quant_gemm_allowed(
27869            cfg!(memra_portable_cuda),
27870            cfg!(memra_hopper_mma),
27871            false
27872        ));
27873        assert!(super::portable_mma_gated() == false);
27874    }
27875}
27876
27877/// The memra-kv device seam (Phase D): the cache's 7 ops delegate to the engine's
27878/// inherent methods (inherent methods win name resolution, so no recursion).
27879impl memra_kv::KvDev for Engine {
27880    fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
27881        Engine::zeros(self, n)
27882    }
27883    fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
27884        Engine::uninit(self, n)
27885    }
27886    fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
27887        Engine::alloc_u8(self, n)
27888    }
27889    fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
27890        Engine::htod_i32(self, v)
27891    }
27892    fn clone_dtod(
27893        &self,
27894        src: &CudaSlice<f32>,
27895    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
27896        Engine::clone_dtod(self, src)
27897    }
27898    fn copy_into(
27899        &self,
27900        dst: &mut CudaSlice<f32>,
27901        off: usize,
27902        src: &CudaSlice<f32>,
27903        len: usize,
27904    ) -> Result<(), Box<dyn std::error::Error>> {
27905        Engine::copy_into(self, dst, off, src, len)
27906    }
27907    fn set_i32_one(
27908        &self,
27909        d: &mut CudaSlice<i32>,
27910        v: i32,
27911    ) -> Result<(), Box<dyn std::error::Error>> {
27912        Engine::set_i32_one(self, d, v)
27913    }
27914}
27915
27916#[cfg(test)]
27917mod fused_gate_bounds_tests {
27918    use super::*;
27919
27920    /// The fused `[q|gate]` split's read-site guard, on the device.
27921    ///
27922    /// `q_gate_split_f32` reads `2*head_dim*n_head*T` floats out of `qf`. A checkpoint whose gate
27923    /// is a SEPARATE tensor produces a `wq` output of exactly half that, so before 2026-08-19 the
27924    /// kernel launched and read 2x past the end of the allocation — an out-of-bounds DEVICE read:
27925    /// no panic, no error, just whatever memory follows. The guard turns it into a typed
27926    /// `FusedQGateExtent` before the launch.
27927    ///
27928    /// Catch demonstration for this test (guard temporarily removed, then restored):
27929    /// `compute-sanitizer --tool memcheck` on the half-width case reported invalid `__global__`
27930    /// reads of size 4 in `q_gate_split_f32`; with the guard in place the same run is clean and
27931    /// the call returns `Err`. Receipt in the lane report.
27932    #[test]
27933    #[ignore = "requires a CUDA GPU"]
27934    fn q_gate_split_refuses_a_separate_gate_wq_instead_of_reading_past_it() {
27935        let e = Engine::new(0).unwrap();
27936        let (head_dim, n_head, t) = (8usize, 4usize, 2usize);
27937        let fused = 2 * head_dim * n_head * t;
27938        let out_n = head_dim * n_head * t;
27939
27940        // half-width `qf` = the separate-gate / ungated layout. MUST be refused.
27941        let narrow = e.htod(&vec![1.0f32; out_n]).unwrap();
27942        let mut q = e.uninit(out_n).unwrap();
27943        let mut gate = e.uninit(out_n).unwrap();
27944        let err = e
27945            .q_gate_split(&narrow, &mut q, &mut gate, head_dim, n_head, t)
27946            .expect_err("half-width wq must be refused, not read past")
27947            .to_string();
27948        assert!(err.contains("NO fused gate"), "{err}");
27949        assert!(err.contains(&format!("{fused}")), "{err}");
27950
27951        // full-width `qf` = a real qwen3.5 fused layout. MUST still run, and split correctly:
27952        // per head hh the block is [q(head_dim) | gate(head_dim)] at stride 2*head_dim.
27953        let host: Vec<f32> = (0..fused).map(|i| i as f32).collect();
27954        let wide = e.htod(&host).unwrap();
27955        e.q_gate_split(&wide, &mut q, &mut gate, head_dim, n_head, t)
27956            .expect("full-width wq splits");
27957        let (qh, gh) = (e.dtoh(&q).unwrap(), e.dtoh(&gate).unwrap());
27958        for tok in 0..t {
27959            for hh in 0..n_head {
27960                for d in 0..head_dim {
27961                    let base = tok * (n_head * 2 * head_dim) + hh * (2 * head_dim);
27962                    let idx = tok * (n_head * head_dim) + hh * head_dim + d;
27963                    assert_eq!(qh[idx], host[base + d], "q t{tok} h{hh} d{d}");
27964                    assert_eq!(gh[idx], host[base + head_dim + d], "gate t{tok} h{hh} d{d}");
27965                }
27966            }
27967        }
27968
27969        // undersized destinations are refused too (the other half of the extent contract)
27970        let mut small = e.uninit(out_n - 1).unwrap();
27971        assert!(
27972            e.q_gate_split(&wide, &mut small, &mut gate, head_dim, n_head, t)
27973                .is_err()
27974        );
27975    }
27976}
27977
27978/// FULL-WIDTH-ROPE CONTRACT on the fused rms_norm+qkv+rope kernels
27979/// (lane/graph-s-key-exactness-20260819, probe O-10 follow-up). CPU-only: the guard runs before
27980/// any launch, so the refusal is testable without a device.
27981#[cfg(test)]
27982mod fused_rope_width_tests {
27983    use super::Engine;
27984
27985    /// gemma-4: rotary width == head width on both classes (GGUF 256/256 and 512/512, and the
27986    /// safetensors route derives the same), which is why the fusion is legal there today.
27987    #[test]
27988    fn full_width_is_accepted() {
27989        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 256).is_ok());
27990        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_cat", 512, 512).is_ok());
27991        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append", 128, 128).is_ok());
27992    }
27993
27994    /// The widths the gemma-4 31B OFFICIAL artifact declares, read from its own GGUF header
27995    /// (`gemma-4-31B-it-official-Q8_0-MTP.gguf`, box3, 2026-08-19):
27996    ///
27997    /// ```text
27998    /// attention.key_length     512   rope.dimension_count     512   (global class)
27999    /// attention.key_length_swa 256   rope.dimension_count_swa 256   (SWA class)
28000    /// ```
28001    ///
28002    /// Both classes satisfy `n_rot == head_dim`, which is why the fusion is legal for gemma and
28003    /// why `HybridModel::gemma4_rope_dims` can feed this guard without refusing what we serve.
28004    /// An artifact that ever declares otherwise gets a loud refusal at the first fused launch
28005    /// instead of a silently over-rotated head.
28006    #[test]
28007    fn gemma4_official_artifact_widths_pass() {
28008        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 512, 512).is_ok());
28009        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 256, 256).is_ok());
28010    }
28011
28012    /// The bug this guard exists to make impossible: a partial-rotary arch fused onto a kernel
28013    /// with no `n_dims`, silently rotating the pass-through band.
28014    #[test]
28015    fn partial_rotary_is_refused_with_the_geometry_named() {
28016        // qwen3.5: n_rot 64 of head_dim 256 (the shape probe O-10 pinned in the split path).
28017        let err = Engine::full_width_rope_only("rms_norm_qkv_rope", 64, 256)
28018            .expect_err("partial rotary must refuse");
28019        let msg = err.to_string();
28020        assert!(msg.contains("PARTIAL ROTARY REFUSED"), "{msg}");
28021        assert!(msg.contains("n_rot 64"), "{msg}");
28022        assert!(msg.contains("head_dim 256"), "{msg}");
28023        assert!(
28024            msg.contains("64..256"),
28025            "names the band it would corrupt: {msg}"
28026        );
28027        // step35 full-attn: 64 of 128 (upstream halves n_rot_full).
28028        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 64, 128).is_err());
28029        // and the reverse mismatch (a wider rope than the head) is not "close enough" either.
28030        assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 128).is_err());
28031    }
28032}