Skip to main content

memra_engine/
mmq_ffi.rs

1//! FFI to the MMQ prefill GEMMs (cu/mmq_fp4.cu + cu/mmq_q45k.cu) — vendored floor kernels.
2//!
3//! NVFP4: the 5150-pp512 kernel from llama.cpp, ggml-decoupled into a static lib with a C-ABI host
4//! launcher. The launcher quantizes the f32 activation to block_fp4_mmq internally (llama's 2-level
5//! FP8-e8m0/UE4M3 scale = the accurate W4A8-via-FP8 path that fixes memra's W4A4 maxdiff 1.46), then
6//! launches the native mxf4nvf4 block-scale tensor-core mma.
7//!
8//! Q4_K/Q5_K: llama's k-quant int8-MMA MMQ (dequant to int8 at tile-load, q8_1 DS4 activation with
9//! the (d, sum) pair that feeds the k-quant min-offset term, shared m16n8k32 s8 mma inner loop).
10//! Replaces the hand-rolled qmatvec_gemm k-quant GEMMs that dominate prefill (32% + 28% busy).
11//!
12//! All dispatched behind MEMRA_MMQ=1. Always built (no external deps) — unlike cutlass_ffi which is
13//! MEMRA_CUTLASS-gated.
14
15use crate::Engine;
16use cudarc::driver::{CudaSlice, DevicePtr, DevicePtrMut};
17
18/// Quantize-once seam state (see `Engine::mmq_act_begin`): window epoch + one cached
19/// (epoch, act_ptr, m, in_f, D4 scratch) slot. Slot drops (freeing the scratch) on each new window.
20static MMQ_ACT_EPOCH: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
21#[allow(clippy::type_complexity)]
22static MMQ_ACT_SLOT: std::sync::Mutex<Option<(u64, u64, usize, usize, CudaSlice<u8>)>> =
23    std::sync::Mutex::new(None);
24/// Stream-k fixup scratch (lazy; sized once per process — one slot per SM).
25static MMQ_FIXUP_SLOT: std::sync::Mutex<Option<cudarc::driver::CudaSlice<u8>>> =
26    std::sync::Mutex::new(None);
27
28unsafe extern "C" {
29    /// Bytes needed for the block_fp4_mmq activation scratch for (in_f, n_tokens).
30    pub fn memra_mmq_nvfp4_act_bytes(in_f: i32, n_tokens: i32) -> usize;
31    /// Run the NVFP4 W4A4 MMQ prefill GEMM. y[n_tokens, out_f] = act[n_tokens, in_f] @ W[out_f, in_f]^T.
32    ///   W_nvfp4_blocks : raw memra NVFP4 weight rows (block_nvfp4 36B blocks, in_f/64 per row).
33    ///   act_f32        : f32 activation [n_tokens, in_f] (contiguous).
34    ///   y              : f32 output [n_tokens, out_f].
35    ///   act_scratch    : pre-alloc'd quant buffer >= memra_mmq_nvfp4_act_bytes(in_f, n_tokens).
36    /// Returns 0 on success, else (1000 + cudaError).
37    pub fn memra_mmq_nvfp4(
38        w_nvfp4_blocks: *const core::ffi::c_void,
39        act_f32: *const f32,
40        y: *mut f32,
41        in_f: i32,
42        out_f: i32,
43        n_tokens: i32,
44        act_scratch: *mut core::ffi::c_void,
45        stream: *mut core::ffi::c_void,
46        out_scale: f32,
47    ) -> i32;
48    /// Same as `memra_mmq_nvfp4`, plus the activation-quantizer selector.
49    ///   per_token_scale = 1: two-level scaling (per-token row amax folded into the GEMM epilogue
50    ///     + per-sub-block UE4M3). This is what `memra_mmq_nvfp4` does.
51    ///   per_token_scale = 0: the v1 sub-block-only quantizer, retained as the numeric oracle so
52    ///     kernel-check can measure what the row scale bought, and as the rollback seam.
53    pub fn memra_mmq_nvfp4_ex(
54        w_nvfp4_blocks: *const core::ffi::c_void,
55        act_f32: *const f32,
56        y: *mut f32,
57        in_f: i32,
58        out_f: i32,
59        n_tokens: i32,
60        act_scratch: *mut core::ffi::c_void,
61        stream: *mut core::ffi::c_void,
62        out_scale: f32,
63        per_token_scale: i32,
64    ) -> i32;
65    /// Same as `memra_mmq_nvfp4_ex`, plus the residual high-precision channel count.
66    ///   residual_k = 0: off.
67    ///   residual_k > 0: the k largest-magnitude activation channels (ranked across the batch) are
68    ///     zeroed before quantization and their exact f32 contribution is added back as a rank-k
69    ///     correction. Requires per_token_scale = 1. Clamped to MMQ_MAX_RESIDUAL_K (64).
70    pub fn memra_mmq_nvfp4_ex2(
71        w_nvfp4_blocks: *const core::ffi::c_void,
72        act_f32: *const f32,
73        y: *mut f32,
74        in_f: i32,
75        out_f: i32,
76        n_tokens: i32,
77        act_scratch: *mut core::ffi::c_void,
78        stream: *mut core::ffi::c_void,
79        out_scale: f32,
80        per_token_scale: i32,
81        residual_k: i32,
82    ) -> i32;
83    /// Bytes needed for the block_q8_1_mmq activation scratch for the NVFP4 W4A8 path.
84    pub fn memra_mmq_nvfp4_w4a8_act_bytes(in_f: i32, n_tokens: i32) -> usize;
85    /// Run the NVFP4 W4A8 MMQ prefill GEMM (STAGE 2 accuracy-safe rung). Same fast MMQ tile as
86    /// memra_mmq_nvfp4 (W4A4) but the non-Blackwell int8 pair: weight FP4 LUT-dequantized to int8 at
87    /// tile-load, activation stays q8_1 int8 (D4, the same quant class as the default int8 GEMM).
88    /// `rp`: 0 = GGUF 36B-block weight layout, 1 = A6 split-plane repack (the resident decode
89    /// layout). The rp tile loader is a pure address remap of the GGUF loader (same dequant math,
90    /// same FP op order) — output is bit-identical either way.
91    /// Same contract as memra_mmq_nvfp4 otherwise. Returns 0 or (1000 + cudaError).
92    pub fn memra_mmq_nvfp4_w4a8(
93        w_nvfp4_blocks: *const core::ffi::c_void,
94        act_f32: *const f32,
95        y: *mut f32,
96        in_f: i32,
97        out_f: i32,
98        n_tokens: i32,
99        act_scratch: *mut core::ffi::c_void,
100        stream: *mut core::ffi::c_void,
101        out_scale: f32,
102        rp: i32,
103    ) -> i32;
104    /// Bytes for the block_e4m3_mmq activation scratch (footprint-identical to block_q8_1_mmq).
105    pub fn memra_mmq_nvfp4_f8f4_act_bytes(in_f: i32, n_tokens: i32) -> usize;
106    /// R-B W4A8-FP8 MMQ prefill GEMM (research/prefill-mxf8f6f4-design.md): NVFP4 per-16 scales
107    /// fold into e4m3 weight VALUES at tile load; e4m3 activations; ONE kind::f8f6f4 m16n8k32
108    /// MMA (381-TF class) where the int8 path issues two imma k16. NEW NUMERIC CONFIG — own
109    /// battery. Same contract/rp semantics as memra_mmq_nvfp4_w4a8. Returns 0 / 1000+cudaError /
110    /// 2000+cudaError.
111    pub fn memra_mmq_nvfp4_f8f4(
112        w_nvfp4_blocks: *const core::ffi::c_void,
113        act_f32: *const f32,
114        y: *mut f32,
115        in_f: i32,
116        out_f: i32,
117        n_tokens: i32,
118        act_scratch: *mut core::ffi::c_void,
119        stream: *mut core::ffi::c_void,
120        out_scale: f32,
121        rp: i32,
122    ) -> i32;
123    /// Bytes for the per-block FP8 MMQ activation scratch (delegates to the F8F4 sizing — the
124    /// two arms deliberately share ONE activation format, `block_e4m3_mmq`).
125    pub fn memra_mmq_fp8_blk_act_bytes(in_f: i32, n_tokens: i32) -> usize;
126    /// Scale-grid dims for an [out_f x in_f] block-128 FP8 tensor (ceil-div by 128).
127    pub fn memra_mmq_fp8_blk_scale_rows(out_f: i32) -> i32;
128    pub fn memra_mmq_fp8_blk_scale_cols(in_f: i32) -> i32;
129    /// PER-BLOCK FP8 MMQ prefill GEMM (cu/mmq_fp8_blk.cu, P1 option (b)): consumes the
130    /// Qwen-official e4m3 weight bytes + the per-[128x128] f32 scale grid DIRECTLY. The weight
131    /// side is never re-quantized (the checkpoint bytes are the MMA A operand), so unlike ARM A's
132    /// per-tensor fold there is no precision loss; unlike ARM B' it does not land on the Q8_0
133    /// floor. `blk_scales` is device f32 [ceil(out_f/128) x ceil(in_f/128)], row-major.
134    /// Requires in_f % 16 == 0. Returns 0 / 1 (bad dims) / 1000+cudaError / 2000+cudaError.
135    pub fn memra_mmq_fp8_blk(
136        w_e4m3: *const core::ffi::c_void,
137        blk_scales: *const f32,
138        act_f32: *const f32,
139        y: *mut f32,
140        in_f: i32,
141        out_f: i32,
142        n_tokens: i32,
143        act_scratch: *mut core::ffi::c_void,
144        stream: *mut core::ffi::c_void,
145        out_scale: f32,
146    ) -> i32;
147    /// Count e4m3 NaN codes (magnitude 0x7F) in a device weight buffer. Those decode to NaN in
148    /// hardware but to 0.0 in the host/ARM B' convention, so a tensor containing any must NOT
149    /// ride `memra_mmq_fp8_blk`. `out_count` is a device u32 (zeroed by the call).
150    pub fn memra_fp8_blk_count_nan(
151        w_e4m3: *const core::ffi::c_void,
152        nbytes: usize,
153        out_count: *mut u32,
154        stream: *mut core::ffi::c_void,
155    ) -> i32;
156    /// Bytes needed for the block_q8_1_mmq activation scratch (shared by Q4_K and Q5_K).
157    pub fn memra_mmq_q45k_act_bytes(in_f: i32, n_tokens: i32) -> usize;
158    /// Run the Q4_K W4A8 MMQ prefill GEMM. Same contract as memra_mmq_nvfp4 (raw ggml block_q4_K
159    /// weight rows, in_f/256 144B superblocks per row). Returns 0 or (1000 + cudaError).
160    pub fn memra_mmq_q4_K(
161        w_q4k_blocks: *const core::ffi::c_void,
162        act_f32: *const f32,
163        y: *mut f32,
164        in_f: i32,
165        out_f: i32,
166        n_tokens: i32,
167        act_scratch: *mut core::ffi::c_void,
168        stream: *mut core::ffi::c_void,
169    ) -> i32;
170    /// Run the Q5_K W4A8 MMQ prefill GEMM (176B superblocks). Same contract as memra_mmq_q4_K.
171    pub fn memra_mmq_q5_K(
172        w_q5k_blocks: *const core::ffi::c_void,
173        act_f32: *const f32,
174        y: *mut f32,
175        in_f: i32,
176        out_f: i32,
177        n_tokens: i32,
178        act_scratch: *mut core::ffi::c_void,
179        stream: *mut core::ffi::c_void,
180    ) -> i32;
181
182    /// Bytes needed for the block_q8_1_mmq (D4) activation scratch for the Q8_0 MMQ path.
183    pub fn memra_mmq_q8_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
184    /// Run the Q8_0 int8-MMA MMQ prefill GEMM (MEMRA_PP_Q8MMQ). Conventional xy-tiling only (no fixup
185    /// scratch). Weight = raw ggml block_q8_0 rows (34B blocks, in_f/32 per row); activation is
186    /// quantized internally to q8_1 D4. Requires in_f % 32 == 0. Returns 0 or (1000 + cudaError).
187    pub fn memra_mmq_q8_0(
188        w_q8_0_blocks: *const core::ffi::c_void,
189        act_f32: *const f32,
190        y: *mut f32,
191        in_f: i32,
192        out_f: i32,
193        n_tokens: i32,
194        act_scratch: *mut core::ffi::c_void,
195        stream: *mut core::ffi::c_void,
196    ) -> i32;
197
198    /// Bytes needed for the block_q8_1_mmq (D4) activation scratch for the Q4_0 MMQ path.
199    pub fn memra_mmq_q4_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
200    /// Run the Q4_0 int8-MMA MMQ prefill GEMM (MEMRA_PP_Q4MMQ). Nibbles dequant to int8 at
201    /// tile-load (the -8 zero-point folds into the quants, D4 epilogue — same accuracy class as
202    /// the Q8_0 MMQ). `rp`: 0 = raw ggml 18B blocks, 1 = MEMRA_Q4RP split-plane repack (qs plane +
203    /// fp16 d plane) — pure address remap, bit-identical output either way. Requires
204    /// in_f % 32 == 0. Returns 0 or (1000 + cudaError).
205    pub fn memra_mmq_q4_0(
206        w_q4_0: *const core::ffi::c_void,
207        act_f32: *const f32,
208        y: *mut f32,
209        in_f: i32,
210        out_f: i32,
211        n_tokens: i32,
212        act_scratch: *mut core::ffi::c_void,
213        stream: *mut core::ffi::c_void,
214        rp: i32,
215    ) -> i32;
216    /// Quantize-only entry (quantize-once seam): f32 activation -> block_q8_1_mmq scratch.
217    pub fn memra_mmq_q4_0_quant_act(
218        act_f32: *const f32,
219        act_scratch: *mut core::ffi::c_void,
220        in_f: i32,
221        n_tokens: i32,
222        stream: *mut core::ffi::c_void,
223    ) -> i32;
224    /// GEMM-only entry: consumes a pre-quantized scratch (from memra_mmq_q4_0_quant_act).
225    pub fn memra_mmq_q4_0_gemm(
226        w_q4_0: *const core::ffi::c_void,
227        act_scratch: *const core::ffi::c_void,
228        y: *mut f32,
229        in_f: i32,
230        out_f: i32,
231        n_tokens: i32,
232        stream: *mut core::ffi::c_void,
233        rp: i32,
234    ) -> i32;
235    /// Stream-k fixup scratch bytes (one [MMQ_X x MMQ_Y] f32 slot per SM).
236    pub fn memra_mmq_q4_0_fixup_bytes() -> usize;
237    /// Force the CLC work-stealing arm: 1 = on, 0 = off (static grid), -1 = MEMRA_MMQ_CLC env
238    /// default. Schedule-only swap of the xy-tiling kernel — bit-identical output by
239    /// construction (perf-frontier lever #1). Returns 1 when the CLC kernel is compiled in
240    /// (SM_100+ gencode), 0 on sm_89/90a builds (force is a no-op there; static grid always).
241    pub fn memra_mmq_q4_0_set_clc(force: i32) -> i32;
242    /// Stream-k GEMM entry: tiling when wave efficiency >= 90%, else stream-k + fixup.
243    pub fn memra_mmq_q4_0_gemm_sk(
244        w_q4_0: *const core::ffi::c_void,
245        act_scratch: *const core::ffi::c_void,
246        y: *mut f32,
247        fixup_scratch: *mut core::ffi::c_void,
248        in_f: i32,
249        out_f: i32,
250        n_tokens: i32,
251        stream: *mut core::ffi::c_void,
252        rp: i32,
253    ) -> i32;
254
255    // ---- IQ3_S / IQ4_XS expert-segmented int8-MMA MMQ (cu/mmq_iq_experts.cu, MEMRA_MOE_MMA) ----
256    /// Bytes for the token-major block_q8_1_mmq activation scratch (in_f, n_tokens).
257    pub fn memra_mmq_iq_experts_act_bytes(in_f: i32, n_tokens: i32) -> usize;
258    /// Quantize token-major f32 activation [n_tokens, in_f] -> block_q8_1_mmq (D4). Returns 0 or 1000+err.
259    pub fn memra_mmq_iq_quantize_act(
260        act_f32: *const f32,
261        act_scratch: *mut core::ffi::c_void,
262        in_f: i32,
263        n_tokens: i32,
264        stream: *mut core::ffi::c_void,
265    ) -> i32;
266    /// Fused act-epilogue: silu/gelu(gate)*up + q8_1_mmq (D4) quantize in ONE launch — no f32 act
267    /// buffer. gate/up pair-major [n_tokens, in_f]; scratch identical to memra_mmq_iq_quantize_act.
268    /// act_kind: 0=silu*mul, 1=gelu_tanh*mul. Byte-identical to the two-pass path (kernel-check gated).
269    pub fn memra_mmq_iq_fused_act_quant(
270        gate: *const f32,
271        up: *const f32,
272        act_scratch: *mut core::ffi::c_void,
273        in_f: i32,
274        n_tokens: i32,
275        act_kind: i32,
276        stream: *mut core::ffi::c_void,
277    ) -> i32;
278    /// Expert-segmented IQ MMA MMQ. Same CSR shape as moe_pairs_matvec_q8_dec: `table` = [3,n_expert]
279    /// device slab ptrs, CSR ex_ids/ex_off/ex_pairs group pairs by expert, pair_tok gathers the
280    /// activation row. y = [n_pairs, out_f] pair-major. `act_scratch` pre-quantized over n_tokens.
281    /// qtype: 5=IQ4_XS, 6=IQ3_S. Returns 0 or 1000+cudaError.
282    /// Dense-trunk IQ4_XS MMQ (lane/kquant-tile-loaders): the dense analog of the expert
283    /// kernel for non-expert IQ4_XS 2-D matmuls (the KAT-Coder trunk class). Quantizes the
284    /// f32 activation to D4 q8_1_mmq internally; `act_scratch` sized by
285    /// `memra_mmq_iq_experts_act_bytes`. Requires in_f % 256 == 0.
286    pub fn memra_mmq_iq4xs_dense(
287        w_blocks: *const core::ffi::c_void,
288        act_f32: *const f32,
289        y: *mut f32,
290        in_f: i32,
291        out_f: i32,
292        n_tokens: i32,
293        row_bytes: i64,
294        act_scratch: *mut core::ffi::c_void,
295        stream: *mut core::ffi::c_void,
296    ) -> i32;
297    pub fn memra_mmq_iq_experts(
298        table: *const u64,
299        proj: i32,
300        n_expert: i32,
301        ex_ids: *const i32,
302        ex_off: *const i32,
303        ex_pairs: *const i32,
304        pair_tok: *const i32,
305        act_scratch: *const core::ffi::c_void,
306        y: *mut f32,
307        in_f: i32,
308        out_f: i32,
309        n_active: i32,
310        n_tokens: i32,
311        qtype: i32,
312        row_bytes: i64,
313        stream: *mut core::ffi::c_void,
314    ) -> i32;
315
316    // ---- MoE grouped f16 GEMM (cu/moe_f16_grouped.cu, round 46 arc 2) ----
317    pub fn memra_moe_f16g_dequant(
318        table: *const u64, proj: i32, n_expert: i32, ex_ids: *const i32,
319        w_f16: *mut core::ffi::c_void, in_f: i32, out_f: i32, n_active: i32,
320        qtype: i32, row_bytes: i64, stream: *mut core::ffi::c_void,
321    ) -> i32;
322    pub fn memra_moe_f16g_gather_act(
323        x: *const f32, pair_tok_or_null: *const i32, act_f16: *mut core::ffi::c_void,
324        row_scale: *mut f32, in_f: i32, n_pairs: i32, stream: *mut core::ffi::c_void,
325    ) -> i32;
326    pub fn memra_moe_f16g_h2f_scaled(
327        src_f16: *const core::ffi::c_void, dst: *mut f32, row_scale: *const f32,
328        ncols: i32, nrows: i32, stream: *mut core::ffi::c_void,
329    ) -> i32;
330    pub fn memra_moe_f16g_gemm(
331        w_f16: *const core::ffi::c_void, act_f16: *const core::ffi::c_void,
332        y_f16: *mut core::ffi::c_void,
333        ex_off_host: *const i32, n_active: i32, in_f: i32, out_f: i32,
334        stream: *mut core::ffi::c_void,
335    ) -> i32;
336    pub fn memra_moe_f16g_h2f(
337        src_f16: *const core::ffi::c_void, dst: *mut f32, n: usize,
338        stream: *mut core::ffi::c_void,
339    ) -> i32;
340    // Single-kernel grouped GEMM (MEMRA_MOE_F16G=2, rounds 49+51): on OUR stream, f32 C with
341    // the act row-scale folded in — no cublas internal-stream race, no sync. Round 51 runs it
342    // as a persistent problem-visitor over the real tiles with two tile forms (32x64 tail
343    // / 128x64x64 3-stage): shape_sel < 0 = the round-49 grid-scan kernel (rollback
344    // arm); else groups with m_e >= cross ride the 128 form. ex_off_host sizes the visitor
345    // grids host-side (the offsets are already there at the call site — no extra transfer).
346    // tail != 0 (lane/sk-tail-form): sub-cross groups ride the DEEP tail (32x64x64 3-stage);
347    // 0 = the round-51 2-stage 32x64x32 (MEMRA_F16G_TAIL=0 rollback). Byte-identical arms.
348    pub fn memra_moe_f16g_gemm_sk(
349        w_f16: *const core::ffi::c_void, act_f16: *const core::ffi::c_void,
350        y_f32: *mut f32, row_scale: *const f32, ex_off_dev: *const i32,
351        ex_off_host: *const i32,
352        n_active: i32, max_m: i32, in_f: i32, out_f: i32, shape_sel: i32, cross: i32,
353        tail: i32, stream: *mut core::ffi::c_void,
354    ) -> i32;
355    // DIRECT-FROM-QUANT sk visitor grouped GEMM (lane/kquant-tile-loaders + iq-direct-loaders):
356    // the visitor forms with the B (weight) tiles dequanted in-register from the expert
357    // superblocks — no f16 dequant workspace pass. Bit-identical to the workspace path by
358    // construction (kernel-check "f16g-kq-direct"). qtype: QT_Q4_K | QT_Q6_K | QT_IQ4_XS |
359    // QT_IQ3_S; rc=2 = not admitted here (caller keeps the dequant-workspace path).
360    // tail: as memra_moe_f16g_gemm_sk.
361    pub fn memra_moe_kq_gemm_sk(
362        table: *const u64, proj: i32, n_expert: i32, ex_ids: *const i32,
363        act_f16: *const core::ffi::c_void, y_f32: *mut f32,
364        row_scale: *const f32, ex_off_dev: *const i32, ex_off_host: *const i32,
365        n_active: i32, max_m: i32, in_f: i32, out_f: i32, qtype: i32, cross: i32,
366        tail: i32, row_bytes: i64, stream: *mut core::ffi::c_void,
367    ) -> i32;
368}
369
370/// W4A8-MMQ DEFAULT-FLIP seam (2026-07-05): the vendored MMQ prefill suite is DEFAULT-ON — NVFP4
371/// takes the W4A8 MMQ tile (same int8 accuracy class as the int8 GEMM it replaces, all exactness
372/// gates hold, ~1.9x pp512; the rp tile-loader arm coexists with the A6 split-plane repack) and
373/// Q4_K/Q5_K take the vendored k-quant int8-MMA MMQ (also int8-class; gated with W4A8 in the same
374/// battery — the predecessor's `MEMRA_MMQ_W4A8=1` arm engaged BOTH, this flip preserves exactly
375/// that measured config). `MEMRA_MMQ_W4A8=0` = escape hatch back to the int8 GEMM prefill
376/// everywhere. `MEMRA_MMQ=1` additionally switches GGUF-layout NVFP4 to the W4A4 mxf4nvf4 tile
377/// (speed/accuracy tradeoff opt-in, unchanged).
378pub fn mmq_w4a8_enabled() -> bool {
379    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
380    *ON.get_or_init(|| {
381        std::env::var("MEMRA_MMQ_W4A8")
382            .map(|v| v != "0")
383            .unwrap_or(true)
384    })
385}
386
387/// Residual high-precision activation channels for the W4A4 MMQ prefill path.
388/// `MEMRA_MMQ_RESIDUAL_K=<k>` keeps the k largest-magnitude activation channels out of the e2m1
389/// quantized path and adds their exact f32 contribution back as a rank-k correction. k=0 (default)
390/// is off; the kernel clamps to MMQ_MAX_RESIDUAL_K (64).
391///
392/// Read LIVE per call, not OnceLock'd, for the same reason `MEMRA_MMQ` is: the W4A4 exactness gate
393/// sweeps arms inside ONE process against ONE set of loaded weights, and a cached first read would
394/// pin every later arm to whatever the first one saw.
395pub fn mmq_residual_k() -> i32 {
396    std::env::var("MEMRA_MMQ_RESIDUAL_K")
397        .ok()
398        .and_then(|v| v.parse::<i32>().ok())
399        .unwrap_or(0)
400        .clamp(0, 64)
401}
402
403/// Q8_0 MMQ prefill seam (lane/ppmmq lever 2, DEFAULT ON since 2026-07-09 — `MEMRA_PP_Q8MMQ=0`
404/// reverts): routes Q8_0 dense
405/// projections (m>=16) through the vendored int8-MMA MMQ (cu/mmq_q8_0.cu) instead of the hand-rolled
406/// `qmatvec_gemm_q8_0` tiling GEMM. Its own numeric config (MMA f32 reduction order != the tiling
407/// GEMM's) — gated with the full exactness battery. Default OFF until the battery is green.
408pub fn mmq_q8_enabled() -> bool {
409    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
410    // Promotion battery (2026-07-09): argmax MATCH on 35B p1/p2/p3 + 9B p2/p3 (p4-16k OOMs
411    // identically with and without the flag — pre-existing gate capacity limit, not this seam);
412    // kernel-check ALL GREEN; run-spec K=1..8 PASS on 9B+35B. 35B pp 2456->3069 free-clock.
413    *ON.get_or_init(|| {
414        std::env::var("MEMRA_PP_Q8MMQ")
415            .map(|v| v != "0")
416            .unwrap_or(true)
417    })
418}
419
420/// IQ4_XS dense-trunk MMQ prefill seam (lane/kquant-tile-loaders, 2026-08-02): routes
421/// NON-expert IQ4_XS 2-D projections (m>=16) through the vendored-machinery int8-MMA dense
422/// MMQ (cu/mmq_iq_experts.cu `mmq_iq4xs_dense_kernel`) instead of the per-column dp4a grid
423/// — the KAT-Coder prefill wall (0.169x vs llama; zero weight reuse across tokens,
424/// research/kat-anomaly-20260802 §6). Its own numeric config (MMA reduction order) — gated
425/// with the full exactness battery. m=1..15 decode/verify keep dp4a (dispatch parity).
426/// `MEMRA_PP_IQMMQ=0` reverts.
427pub fn mmq_iq4xs_enabled() -> bool {
428    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
429    *ON.get_or_init(|| {
430        std::env::var("MEMRA_PP_IQMMQ")
431            .map(|v| v != "0")
432            .unwrap_or(true)
433    })
434}
435
436/// Q4_0 MMQ prefill seam (gemma-4-12B lane, 2026-07-22): routes Q4_0 dense projections (m>=16)
437/// through the vendored int8-MMA MMQ (cu/mmq_q4_0.cu) instead of the hand-rolled
438/// `qmatvec_gemm_q4_0[_rp]` tiling GEMM (measured 77% of the 12B prime pass). Its own numeric
439/// config (MMA f32 reduction order != the tiling GEMM's) — gated with the full exactness battery
440/// before default-flip; `MEMRA_PP_Q4MMQ=0` reverts.
441pub fn mmq_q4_enabled() -> bool {
442    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
443    *ON.get_or_init(|| {
444        std::env::var("MEMRA_PP_Q4MMQ")
445            .map(|v| v != "0")
446            .unwrap_or(true)
447    })
448}
449
450impl Engine {
451    /// True if `w` should take a vendored MMQ GEMM under the current env policy (see
452    /// `mmq_w4a8_enabled`): NVFP4 needs in_f % 64 == 0, Q4_K/Q5_K need in_f % 256 == 0.
453    pub fn mmq_supports(&self, w: &crate::model::GpuTensor) -> bool {
454        use crate::model::GpuTensor;
455        if crate::portable_mma_gated() {
456            return false;
457        }
458        let mmq_opt_in = std::env::var("MEMRA_MMQ").is_ok();
459        match w {
460            // A6 split-plane repacked NVFP4: ONLY the W4A8 loader has an rp arm (pure address
461            // remap, bit-identical output — mmq_nvfp4_w4a8.cu load_tiles_nvfp4_w4a8<is_rp>).
462            // The W4A4 loader (mmq_fp4.cu load_tiles_nvfp4_nvfp4) reads 36B GGUF blocks only,
463            // so an rp weight with W4A8 disabled falls through to the rp-ported int8 GEMM.
464            // NVFP4 W4A8/W4A4 launchers use .kind::f8f6f4 / mxf4nvf4 tile MMA — sm_100a+/
465            // sm_120a-only. On every portable build (incl. the 90a Hopper-MMA lane) they are
466            // fail-closed link stubs (build.rs), so never offer them here.
467            GpuTensor::Quant { qtype, rp, .. } if *qtype == crate::QT_NVFP4 && *rp => {
468                !cfg!(memra_portable_cuda) && mmq_w4a8_enabled() && w.in_features() % 64 == 0
469            }
470            // GGUF-layout NVFP4 (MEMRA_RP=0): W4A8 (default-on) or the explicit W4A4 opt-in.
471            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4 => {
472                !cfg!(memra_portable_cuda) && (mmq_w4a8_enabled() || mmq_opt_in)
473                    && w.in_features() % 64 == 0
474            }
475            GpuTensor::Quant { qtype, .. }
476                if *qtype == crate::QT_Q4_K || *qtype == crate::QT_Q5_K =>
477            {
478                (mmq_w4a8_enabled() || mmq_opt_in) && w.in_features() % 256 == 0
479            }
480            // Q8_0 dense projections (35B attn/ssm/shexp): opt-in only (MEMRA_PP_Q8MMQ=1), its own
481            // numeric config vs qmatvec_gemm_q8_0. in_f % 256 == 0: MMQ_ITER_K=256 loads 8-block
482            // groups, so a non-multiple row would read a garbage weight tail (fp16 d bytes can be
483            // NaN-pattern, and NaN * 0-padded-activation = NaN — the 26B ffn_down lesson).
484            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q8_0 => {
485                mmq_q8_enabled() && w.in_features() % 256 == 0
486            }
487            // Q4_0 dense projections (gemma QAT ggufs): MEMRA_PP_Q4MMQ seam. Both weight layouts
488            // (raw 18B blocks and the MEMRA_Q4RP split-plane repack) have loader arms. Same
489            // in_f % 256 == 0 tail rule as Q8_0 (26B ffn_down in_f=2112 NaN'd on the %32 gate);
490            // non-multiples fall back to the hand-rolled qmatvec_gemm_q4_0[_rp].
491            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q4_0 => {
492                mmq_q4_enabled() && w.in_features() % 256 == 0
493            }
494            // IQ4_XS dense projections (KAT-Coder trunk): m>=16 prefill only — decode and
495            // spec-verify (m<16) keep the qmatvec_iq4_XS_dp4a per-column program (the
496            // kat-anomaly dispatch-parity law). Requires the dp4a fast path itself enabled:
497            // MEMRA_IQ_FAST=0 (the Stage-A oracle rollback) must also kill this arm so the
498            // rollback stays a full-path seam. in_f % 256: MMQ_ITER_K walks whole superblocks.
499            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_IQ4_XS => {
500                mmq_iq4xs_enabled() && Self::iq_fast_enabled() && w.in_features() % 256 == 0
501            }
502            _ => false,
503        }
504    }
505
506    /// Unified vendored-MMQ dispatch: routes to the NVFP4 or Q4_K/Q5_K launcher by qtype.
507    /// Caller MUST have checked `mmq_supports(w)`. `x` is the RAW f32 activation.
508    pub fn qmatvec_mmq(
509        &self,
510        w: &crate::model::GpuTensor,
511        x: &CudaSlice<f32>,
512        m: usize,
513    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
514        use crate::model::GpuTensor;
515        let (in_f, out_f) = (w.in_features(), w.out_features());
516        let GpuTensor::Quant {
517            bytes,
518            scale,
519            qtype,
520            rp,
521            ..
522        } = w
523        else {
524            return Err("qmatvec_mmq: not a Quant tensor".into());
525        };
526        // NVFP4 tile choice: W4A8 (accuracy-safe int8 pair, DEFAULT since the flip) vs W4A4
527        // (mxf4nvf4 mma, explicit MEMRA_MMQ=1 speed/accuracy tradeoff). An rp weight ALWAYS takes
528        // W4A8 — only its loader has the split-plane arm (pure address remap, bit-identical).
529        // Explicit MEMRA_MMQ_W4A8=1 still overrides a simultaneous MEMRA_MMQ=1 (predecessor rule).
530        let w4a8_explicit = std::env::var("MEMRA_MMQ_W4A8")
531            .map(|v| v != "0")
532            .unwrap_or(false);
533        let use_w4a8 =
534            *rp || w4a8_explicit || (mmq_w4a8_enabled() && std::env::var("MEMRA_MMQ").is_err());
535        match *qtype {
536            // STAGE 2: the accuracy-safe int8 W4A8 MMQ tile (weight FP4->int8 dequant + q8_1
537            // activation) — handles BOTH weight layouts (rp = A6 split-plane vs GGUF blocks).
538            q if q == crate::QT_NVFP4 && use_w4a8 => {
539                self.qmatvec_mmq_nvfp4_w4a8(bytes, x, m, in_f, out_f, *scale, *rp)
540            }
541            q if q == crate::QT_NVFP4 => self.qmatvec_mmq_nvfp4(bytes, x, m, in_f, out_f, *scale),
542            q if q == crate::QT_Q4_K || q == crate::QT_Q5_K => {
543                let mut y = self.qmatvec_mmq_q45k_raw(bytes, x, m, in_f, out_f, q)?;
544                if *scale != 1.0 {
545                    self.scale_inplace(&mut y, *scale, m * out_f)?;
546                }
547                Ok(y)
548            }
549            q if q == crate::QT_Q8_0 => {
550                // wgmma arm (sm_90a, task 8): OPT-IN via MEMRA_WGMMA=1 — v0 measured 3845
551                // vs MMQ 8692 tok/s pp512 (2026-07-26 N=5), so MMQ stays the default until
552                // the pipelined wgmma wins. Reads the rp4 split-plane mirror + the engine's
553                // q8_1 activation planes. Same numeric class as MMQ (exact s32 per 32-block,
554                // one f32 fold per block, ascending K) — kernel-check tolerance-gated.
555                if cfg!(memra_hopper_mma) && out_f % 64 == 0 && crate::wgmma_gemm_enabled() {
556                    if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
557                        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
558                        let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, &aq, &ad, m, in_f, out_f)?;
559                        if *scale != 1.0 {
560                            self.scale_inplace(&mut y, *scale, m * out_f)?;
561                        }
562                        return Ok(y);
563                    }
564                }
565                let mut y = self.qmatvec_mmq_q8_0_raw(bytes, x, m, in_f, out_f)?;
566                if *scale != 1.0 {
567                    self.scale_inplace(&mut y, *scale, m * out_f)?;
568                }
569                Ok(y)
570            }
571            q if q == crate::QT_Q4_0 => {
572                let mut y = self.qmatvec_mmq_q4_0_raw(bytes, x, m, in_f, out_f, *rp)?;
573                if *scale != 1.0 {
574                    self.scale_inplace(&mut y, *scale, m * out_f)?;
575                }
576                Ok(y)
577            }
578            q if q == crate::QT_IQ4_XS => {
579                let GpuTensor::Quant { row_bytes, .. } = w else { unreachable!() };
580                let mut y = self.qmatvec_mmq_iq4xs_raw(bytes, x, m, in_f, out_f, *row_bytes)?;
581                if *scale != 1.0 {
582                    self.scale_inplace(&mut y, *scale, m * out_f)?;
583                }
584                Ok(y)
585            }
586            q => Err(format!("qmatvec_mmq: unsupported qtype {q}").into()),
587        }
588    }
589
590    /// Bare IQ4_XS dense MMQ launch (no macro-scale) — also the kernel_check gate entry.
591    pub fn qmatvec_mmq_iq4xs_raw(
592        &self,
593        bytes: &CudaSlice<u8>,
594        x: &CudaSlice<f32>,
595        m: usize,
596        in_f: usize,
597        out_f: usize,
598        row_bytes: usize,
599    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
600        assert!(
601            in_f % 256 == 0,
602            "MMQ IQ4_XS requires in_f % 256 == 0, got {in_f}"
603        );
604        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, m as i32) };
605        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
606        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
607        {
608            let stream = self.gpu.stream();
609            let (w_p, _gw) = bytes.device_ptr(&stream);
610            let (x_p, _gx) = x.device_ptr(&stream);
611            let (y_p, _gy) = y.device_ptr_mut(&stream);
612            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
613            let rc = unsafe {
614                memra_mmq_iq4xs_dense(
615                    w_p as *const core::ffi::c_void,
616                    x_p as *const f32,
617                    y_p as *mut f32,
618                    in_f as i32,
619                    out_f as i32,
620                    m as i32,
621                    row_bytes as i64,
622                    s_p as *mut core::ffi::c_void,
623                    stream.cu_stream() as *mut core::ffi::c_void,
624                )
625            };
626            if rc != 0 {
627                return Err(format!("memra_mmq_iq4xs_dense rc={rc}").into());
628            }
629        }
630        Ok(y)
631    }
632
633    /// Bare Q4_K/Q5_K MMQ launch (no macro-scale) — also the kernel_check accuracy-gate entry.
634    /// Conventional xy-tiling only (the vendored stream-K arm — MEMRA_MMQ_STREAMK — was removed
635    /// 2026-07-08: 1.11x per-GEMM but its k-split f32 reorder flipped the model argmax gate;
636    /// rig5090.jsonl 2026-07-03 has the record).
637    pub fn qmatvec_mmq_q45k_raw(
638        &self,
639        bytes: &CudaSlice<u8>,
640        x: &CudaSlice<f32>,
641        m: usize,
642        in_f: usize,
643        out_f: usize,
644        qtype: i32,
645    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
646        assert!(
647            in_f % 256 == 0,
648            "MMQ Q4_K/Q5_K requires in_f % 256 == 0, got {in_f}"
649        );
650        let act_bytes = unsafe { memra_mmq_q45k_act_bytes(in_f as i32, m as i32) };
651        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
652        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
653        {
654            let stream = self.gpu.stream();
655            let (w_p, _gw) = bytes.device_ptr(&stream);
656            let (x_p, _gx) = x.device_ptr(&stream);
657            let (y_p, _gy) = y.device_ptr_mut(&stream);
658            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
659            let launcher = if qtype == crate::QT_Q4_K {
660                memra_mmq_q4_K
661            } else {
662                memra_mmq_q5_K
663            };
664            let rc = unsafe {
665                launcher(
666                    w_p as *const core::ffi::c_void,
667                    x_p as *const f32,
668                    y_p as *mut f32,
669                    in_f as i32,
670                    out_f as i32,
671                    m as i32,
672                    s_p as *mut core::ffi::c_void,
673                    stream.cu_stream() as *mut core::ffi::c_void,
674                )
675            };
676            if rc != 0 {
677                return Err(format!("memra_mmq_q45k(qtype={qtype}) rc={rc}").into());
678            }
679        }
680        Ok(y)
681    }
682
683    /// Bare Q8_0 int8-MMA MMQ launch (no macro-scale) — the kernel_check accuracy-gate entry and
684    /// the `qmatvec_mmq` dispatch body. Conventional xy-tiling only (no stream-K / fixup scratch).
685    pub fn qmatvec_mmq_q8_0_raw(
686        &self,
687        bytes: &CudaSlice<u8>,
688        x: &CudaSlice<f32>,
689        m: usize,
690        in_f: usize,
691        out_f: usize,
692    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
693        assert!(
694            in_f % 32 == 0,
695            "MMQ Q8_0 requires in_f % 32 == 0, got {in_f}"
696        );
697        let act_bytes = unsafe { memra_mmq_q8_0_act_bytes(in_f as i32, m as i32) };
698        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
699        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
700        {
701            let stream = self.gpu.stream();
702            let (w_p, _gw) = bytes.device_ptr(&stream);
703            let (x_p, _gx) = x.device_ptr(&stream);
704            let (y_p, _gy) = y.device_ptr_mut(&stream);
705            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
706            let rc = unsafe {
707                memra_mmq_q8_0(
708                    w_p as *const core::ffi::c_void,
709                    x_p as *const f32,
710                    y_p as *mut f32,
711                    in_f as i32,
712                    out_f as i32,
713                    m as i32,
714                    s_p as *mut core::ffi::c_void,
715                    stream.cu_stream() as *mut core::ffi::c_void,
716                )
717            };
718            if rc != 0 {
719                return Err(format!("memra_mmq_q8_0 rc={rc}").into());
720            }
721        }
722        Ok(y)
723    }
724
725    /// Open a quantize-once sharing window for the NEXT activation (quantize-once seam): sibling
726    /// Q4_0 MMQ matmuls on the SAME input (q/k/v; gate/up) quantize its D4 scratch once. Safe by
727    /// construction: a hit requires the same window epoch AND the same (ptr, m, in_f) — the caller
728    /// opens a window while it holds the shared input alive, so its address can neither change nor
729    /// be recycled inside the window. Paths that never call this never hit the cache.
730    pub fn mmq_act_begin(&self) {
731        use std::sync::atomic::Ordering;
732        MMQ_ACT_EPOCH.fetch_add(1, Ordering::Relaxed);
733        *MMQ_ACT_SLOT.lock().unwrap() = None;
734    }
735
736    /// Bare Q4_0 int8-MMA MMQ launch (no macro-scale) — the kernel_check accuracy-gate entry and
737    /// the `qmatvec_mmq` dispatch body. `rp` selects the weight layout (MEMRA_Q4RP split-plane vs
738    /// raw ggml 18B blocks) — pure address remap, bit-identical output.
739    pub fn qmatvec_mmq_q4_0_raw(
740        &self,
741        bytes: &CudaSlice<u8>,
742        x: &CudaSlice<f32>,
743        m: usize,
744        in_f: usize,
745        out_f: usize,
746        rp: bool,
747    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
748        use std::sync::atomic::Ordering;
749        assert!(
750            in_f % 32 == 0,
751            "MMQ Q4_0 requires in_f % 32 == 0, got {in_f}"
752        );
753        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
754        let stream = self.gpu.stream();
755        let (x_p, _gx) = x.device_ptr(&stream);
756        let epoch = MMQ_ACT_EPOCH.load(Ordering::Relaxed);
757        // quantize-once: reuse the window's scratch when the SAME activation comes back.
758        let mut slot = MMQ_ACT_SLOT.lock().unwrap();
759        let hit = matches!(&*slot,
760            Some((e, p, mm, inf, _)) if *e == epoch && *p == x_p as u64 && *mm == m && *inf == in_f);
761        if !hit {
762            let act_bytes = unsafe { memra_mmq_q4_0_act_bytes(in_f as i32, m as i32) };
763            let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
764            {
765                let (s_p, _gs) = scratch.device_ptr_mut(&stream);
766                let rc = unsafe {
767                    memra_mmq_q4_0_quant_act(
768                        x_p as *const f32,
769                        s_p as *mut core::ffi::c_void,
770                        in_f as i32,
771                        m as i32,
772                        stream.cu_stream() as *mut core::ffi::c_void,
773                    )
774                };
775                if rc != 0 {
776                    return Err(
777                        format!("memra_mmq_q4_0_quant_act(in_f={in_f}, m={m}) rc={rc}").into()
778                    );
779                }
780            }
781            *slot = Some((epoch, x_p as u64, m, in_f, scratch));
782        }
783        let scratch = &slot.as_ref().unwrap().4;
784        {
785            let (w_p, _gw) = bytes.device_ptr(&stream);
786            let (y_p, _gy) = y.device_ptr_mut(&stream);
787            let (s_p, _gs) = scratch.device_ptr(&stream);
788            // Stream-k arm (DEFAULT since 2026-07-23; MEMRA_MMQ_SK=0 reverts to xy-tiling):
789            // small-batch tail-wave fix — the sk entry itself falls back to (bit-identical)
790            // tiling at >=90% wave efficiency. Band-class fold order below that. Gate: 12B
791            // pp512 +3.3% (1.005x vs llama), pp1736 +1.0%; 31B +0.5%; D512 sentinel MATCH.
792            //
793            // SPEC-SERVING FLIP (2026-07-27, the f16pv/wkv acceptance-law pattern): with
794            // MEMRA_DRAFT set the default is OFF. Two mechanisms, both measured: (1) sk's
795            // fold order shifts PRIME numerics in the drafter's sub-argmax logit space
796            // (26B d1736 accept 0.846 w/o sk vs 0.826 — bisect row 0.861 vs 0.405-era);
797            // (2) the shape-keyed AUTOTUNE re-times both forms per process, so knife-edge
798            // shapes pick DIFFERENT kernels run-to-run — the 12B depth cell was BIMODAL
799            // (205 @ 0.756 / 260 @ 0.943 across identical invocations; sk-off x6 = stable
800            // 263-269 @ 0.953). Explicit MEMRA_MMQ_SK always wins; plain serving keeps sk.
801            // HOPPER DEFAULT OFF (2026-07-31, #23): on sm_90a the SK arm computes WRONG
802            // values for the 26B a4b's non-rp Q4_0 shapes once the autotune m-bucket
803            // crosses 256 (prefill argmax garbage, maxdiff ~10; MEMRA_MMQ_SK=0 -> MATCH,
804            // one-variable kill x confirmed on-box). The winner is timing-picked and the
805            // SK split/fixup are SM-count dependent (132 vs 170) — until the kernel is
806            // fixed for that class, Hopper fails CLOSED to the bit-identical xy-tiling
807            // (cost on the healthy models: g12 -1.4%, g31 -0.6% prefill, N=3 on-box).
808            // sm_120a keeps SK on (rig-divergence law). MEMRA_MMQ_SK=1 still forces.
809            static SK_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
810            let sk = match crate::MMQ_SK_FORCE.load(std::sync::atomic::Ordering::Relaxed) {
811                0 => false,
812                1 => true,
813                _ => *SK_ON.get_or_init(|| std::env::var("MEMRA_MMQ_SK")
814                        .map(|v| v != "0").unwrap_or(!cfg!(memra_hopper_mma))),
815            };
816            let rc = if sk {
817                let mut fx = MMQ_FIXUP_SLOT.lock().unwrap();
818                if fx.is_none() {
819                    let nb = unsafe { memra_mmq_q4_0_fixup_bytes() };
820                    *fx = Some(self.alloc_uninit::<u8>(nb)?);
821                }
822                let (f_p, _gf) = fx.as_mut().unwrap().device_ptr_mut(&stream);
823                unsafe {
824                    memra_mmq_q4_0_gemm_sk(
825                        w_p as *const core::ffi::c_void,
826                        s_p as *const core::ffi::c_void,
827                        y_p as *mut f32,
828                        f_p as *mut core::ffi::c_void,
829                        in_f as i32,
830                        out_f as i32,
831                        m as i32,
832                        stream.cu_stream() as *mut core::ffi::c_void,
833                        rp as i32,
834                    )
835                }
836            } else { unsafe {
837                memra_mmq_q4_0_gemm(
838                    w_p as *const core::ffi::c_void,
839                    s_p as *const core::ffi::c_void,
840                    y_p as *mut f32,
841                    in_f as i32,
842                    out_f as i32,
843                    m as i32,
844                    stream.cu_stream() as *mut core::ffi::c_void,
845                    rp as i32,
846                )
847            } };
848            if rc != 0 {
849                return Err(format!(
850                    "memra_mmq_q4_0_gemm(rp={rp}, in_f={in_f}, out_f={out_f}, m={m}, wbytes={}) rc={rc}",
851                    bytes.len()
852                )
853                .into());
854            }
855        }
856        Ok(y)
857    }
858
859    /// Run the vendored NVFP4 MMQ prefill GEMM from raw weight bytes + f32 activation.
860    /// y[m, out_f] = x[m, in_f] @ W^T. The per-tensor NVFP4 macro-scale is FOLDED into the MMQ
861    /// write-back epilogue (was a separate scale_inplace launch + full y round-trip per matmul).
862    /// Same elementwise multiply -> bit-identical to the two-launch form.
863    /// `x` is the RAW f32 activation (the launcher quantizes it to block_fp4_mmq internally).
864    pub fn qmatvec_mmq_nvfp4(
865        &self,
866        bytes: &CudaSlice<u8>,
867        x: &CudaSlice<f32>,
868        m: usize,
869        in_f: usize,
870        out_f: usize,
871        scale: f32,
872    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
873        self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, scale)
874    }
875
876    /// Bare MMQ launch (no macro-scale) — for the kernel_check accuracy gate.
877    pub fn qmatvec_mmq_nvfp4_raw(
878        &self,
879        bytes: &CudaSlice<u8>,
880        x: &CudaSlice<f32>,
881        m: usize,
882        in_f: usize,
883        out_f: usize,
884    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
885        self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, 1.0)
886    }
887
888    /// Bare MMQ launch on the PRE-PORT activation quantizer (per-sub-block UE4M3 scale only, no
889    /// per-token row amax). The numeric oracle for the two-level quantizer: kernel-check runs both
890    /// and reports the accuracy delta, so the port's value is measured rather than asserted.
891    pub fn qmatvec_mmq_nvfp4_raw_v1(
892        &self,
893        bytes: &CudaSlice<u8>,
894        x: &CudaSlice<f32>,
895        m: usize,
896        in_f: usize,
897        out_f: usize,
898    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
899        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, false, 0)
900    }
901
902    /// Bare MMQ launch with an explicit residual-channel count — for the kernel-check k sweep.
903    pub fn qmatvec_mmq_nvfp4_raw_res(
904        &self,
905        bytes: &CudaSlice<u8>,
906        x: &CudaSlice<f32>,
907        m: usize,
908        in_f: usize,
909        out_f: usize,
910        residual_k: i32,
911    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
912        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, true, residual_k)
913    }
914
915    fn qmatvec_mmq_nvfp4_scaled(
916        &self,
917        bytes: &CudaSlice<u8>,
918        x: &CudaSlice<f32>,
919        m: usize,
920        in_f: usize,
921        out_f: usize,
922        scale: f32,
923    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
924        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, scale, true, mmq_residual_k())
925    }
926
927    fn qmatvec_mmq_nvfp4_inner(
928        &self,
929        bytes: &CudaSlice<u8>,
930        x: &CudaSlice<f32>,
931        m: usize,
932        in_f: usize,
933        out_f: usize,
934        scale: f32,
935        per_token_scale: bool,
936        residual_k: i32,
937    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
938        assert!(
939            in_f % 64 == 0,
940            "MMQ NVFP4 requires in_f % 64 == 0, got {in_f}"
941        );
942        let act_bytes = unsafe { memra_mmq_nvfp4_act_bytes(in_f as i32, m as i32) };
943        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
944        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
945        {
946            let stream = self.gpu.stream();
947            let (w_p, _gw) = bytes.device_ptr(&stream);
948            let (x_p, _gx) = x.device_ptr(&stream);
949            let (y_p, _gy) = y.device_ptr_mut(&stream);
950            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
951            let rc = unsafe {
952                memra_mmq_nvfp4_ex2(
953                    w_p as *const core::ffi::c_void,
954                    x_p as *const f32,
955                    y_p as *mut f32,
956                    in_f as i32,
957                    out_f as i32,
958                    m as i32,
959                    s_p as *mut core::ffi::c_void,
960                    stream.cu_stream() as *mut core::ffi::c_void,
961                    scale,
962                    per_token_scale as i32,
963                    residual_k,
964                )
965            };
966            if rc != 0 {
967                return Err(format!("memra_mmq_nvfp4_ex2 rc={rc}").into());
968            }
969        }
970        Ok(y)
971    }
972
973    /// STAGE 2 W4A8 MMQ NVFP4: same tile as the W4A4 path, but weight FP4 is LUT-dequantized to
974    /// int8 at tile-load and the activation stays q8_1 int8 — the accuracy-safe rung. Macro-scale
975    /// folded into the write-back epilogue (bit-identical to a post-matmul scale_inplace).
976    /// `rp` selects the weight layout (A6 split-plane vs GGUF blocks) — bit-identical output.
977    pub fn qmatvec_mmq_nvfp4_w4a8(
978        &self,
979        bytes: &CudaSlice<u8>,
980        x: &CudaSlice<f32>,
981        m: usize,
982        in_f: usize,
983        out_f: usize,
984        scale: f32,
985        rp: bool,
986    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
987        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, scale, rp)
988    }
989
990    /// Bare W4A8 MMQ launch (no macro-scale, GGUF layout) — for the kernel_check accuracy gate.
991    pub fn qmatvec_mmq_nvfp4_w4a8_raw(
992        &self,
993        bytes: &CudaSlice<u8>,
994        x: &CudaSlice<f32>,
995        m: usize,
996        in_f: usize,
997        out_f: usize,
998    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
999        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, false)
1000    }
1001
1002    /// Bare W4A8 MMQ launch on an A6 split-plane repacked weight — the rp-loader bit-identity gate
1003    /// compares this against `qmatvec_mmq_nvfp4_w4a8_raw` on the same weight.
1004    pub fn qmatvec_mmq_nvfp4_w4a8_raw_rp(
1005        &self,
1006        bytes: &CudaSlice<u8>,
1007        x: &CudaSlice<f32>,
1008        m: usize,
1009        in_f: usize,
1010        out_f: usize,
1011    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1012        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, true)
1013    }
1014
1015    fn qmatvec_mmq_nvfp4_w4a8_scaled(
1016        &self,
1017        bytes: &CudaSlice<u8>,
1018        x: &CudaSlice<f32>,
1019        m: usize,
1020        in_f: usize,
1021        out_f: usize,
1022        scale: f32,
1023        rp: bool,
1024    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1025        assert!(
1026            in_f % 64 == 0,
1027            "MMQ NVFP4 W4A8 requires in_f % 64 == 0, got {in_f}"
1028        );
1029        let act_bytes = unsafe { memra_mmq_nvfp4_w4a8_act_bytes(in_f as i32, m as i32) };
1030        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1031        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1032        {
1033            let stream = self.gpu.stream();
1034            let (w_p, _gw) = bytes.device_ptr(&stream);
1035            let (x_p, _gx) = x.device_ptr(&stream);
1036            let (y_p, _gy) = y.device_ptr_mut(&stream);
1037            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1038            // MEMRA_MMQ_F8F4=1: the R-B W4A8-FP8 tile (own numeric config; battery-gated seam).
1039            // Scratch layouts are footprint-identical, so only the entry point swaps.
1040            static F8F4: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1041            let f8f4 = *F8F4.get_or_init(|| std::env::var("MEMRA_MMQ_F8F4").as_deref() == Ok("1"));
1042            let rc = unsafe {
1043                if f8f4 {
1044                    memra_mmq_nvfp4_f8f4(
1045                        w_p as *const core::ffi::c_void,
1046                        x_p as *const f32,
1047                        y_p as *mut f32,
1048                        in_f as i32,
1049                        out_f as i32,
1050                        m as i32,
1051                        s_p as *mut core::ffi::c_void,
1052                        stream.cu_stream() as *mut core::ffi::c_void,
1053                        scale,
1054                        rp as i32,
1055                    )
1056                } else {
1057                    memra_mmq_nvfp4_w4a8(
1058                        w_p as *const core::ffi::c_void,
1059                        x_p as *const f32,
1060                        y_p as *mut f32,
1061                        in_f as i32,
1062                        out_f as i32,
1063                        m as i32,
1064                        s_p as *mut core::ffi::c_void,
1065                        stream.cu_stream() as *mut core::ffi::c_void,
1066                        scale,
1067                        rp as i32,
1068                    )
1069                }
1070            };
1071            if rc != 0 {
1072                return Err(format!("memra_mmq_nvfp4_w4a8(f8f4={f8f4}) rc={rc}").into());
1073            }
1074        }
1075        Ok(y)
1076    }
1077
1078    /// PER-BLOCK FP8 MMQ prefill GEMM (cu/mmq_fp8_blk.cu). `w_e4m3` is the raw checkpoint e4m3
1079    /// plane [out_f x in_f] and `blk_scales` the device f32 grid [ceil(out_f/128) x
1080    /// ceil(in_f/128)] — no re-quantization of either.
1081    pub fn qmatvec_mmq_fp8_blk(
1082        &self,
1083        w_e4m3: &CudaSlice<u8>,
1084        blk_scales: &CudaSlice<f32>,
1085        x: &CudaSlice<f32>,
1086        m: usize,
1087        in_f: usize,
1088        out_f: usize,
1089    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1090        self.qmatvec_mmq_fp8_blk_scaled(w_e4m3, blk_scales, x, m, in_f, out_f, 1.0)
1091    }
1092
1093    pub fn qmatvec_mmq_fp8_blk_scaled(
1094        &self,
1095        w_e4m3: &CudaSlice<u8>,
1096        blk_scales: &CudaSlice<f32>,
1097        x: &CudaSlice<f32>,
1098        m: usize,
1099        in_f: usize,
1100        out_f: usize,
1101        scale: f32,
1102    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1103        assert!(
1104            in_f % 16 == 0,
1105            "per-block FP8 MMQ requires in_f % 16 == 0, got {in_f}"
1106        );
1107        let want_scales = ((out_f + 127) / 128) * ((in_f + 127) / 128);
1108        assert!(
1109            blk_scales.len() >= want_scales,
1110            "blk_scales too small: {} < {want_scales}",
1111            blk_scales.len()
1112        );
1113        assert!(
1114            w_e4m3.len() >= out_f * in_f,
1115            "e4m3 plane too small: {} < {}",
1116            w_e4m3.len(),
1117            out_f * in_f
1118        );
1119        let act_bytes = unsafe { memra_mmq_fp8_blk_act_bytes(in_f as i32, m as i32) };
1120        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1121        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1122        {
1123            let stream = self.gpu.stream();
1124            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
1125            let (sc_p, _gsc) = blk_scales.device_ptr(&stream);
1126            let (x_p, _gx) = x.device_ptr(&stream);
1127            let (y_p, _gy) = y.device_ptr_mut(&stream);
1128            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1129            let rc = unsafe {
1130                memra_mmq_fp8_blk(
1131                    w_p as *const core::ffi::c_void,
1132                    sc_p as *const f32,
1133                    x_p as *const f32,
1134                    y_p as *mut f32,
1135                    in_f as i32,
1136                    out_f as i32,
1137                    m as i32,
1138                    s_p as *mut core::ffi::c_void,
1139                    stream.cu_stream() as *mut core::ffi::c_void,
1140                    scale,
1141                )
1142            };
1143            if rc != 0 {
1144                return Err(format!("memra_mmq_fp8_blk rc={rc}").into());
1145            }
1146        }
1147        Ok(y)
1148    }
1149
1150    /// Count e4m3 NaN codes (magnitude 0x7F) in a device e4m3 plane. 0 is the precondition for
1151    /// routing that tensor through `qmatvec_mmq_fp8_blk` (hardware decodes them to NaN, the
1152    /// host/ARM B' reference to 0.0).
1153    pub fn fp8_blk_nan_count(
1154        &self,
1155        w_e4m3: &CudaSlice<u8>,
1156    ) -> Result<u32, Box<dyn std::error::Error>> {
1157        let mut cnt = self.htod_u32_v(&[0u32])?;
1158        let n = w_e4m3.len();
1159        {
1160            let stream = self.gpu.stream();
1161            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
1162            let (c_p, _gc) = cnt.device_ptr_mut(&stream);
1163            let rc = unsafe {
1164                memra_fp8_blk_count_nan(
1165                    w_p as *const core::ffi::c_void,
1166                    n,
1167                    c_p as *mut u32,
1168                    stream.cu_stream() as *mut core::ffi::c_void,
1169                )
1170            };
1171            if rc != 0 {
1172                return Err(format!("memra_fp8_blk_count_nan rc={rc}").into());
1173            }
1174        }
1175        Ok(self.dtoh_u32(&cnt)?[0])
1176    }
1177
1178    /// Quantize token-major f32 activation [n_tokens, in_f] to the block_q8_1_mmq (D4) scratch the
1179    /// IQ expert-MMA kernel consumes. Returns the scratch buffer (one per proj input per layer).
1180    pub fn mmq_iq_quantize_act(
1181        &self,
1182        x: &CudaSlice<f32>,
1183        in_f: usize,
1184        n_tokens: usize,
1185    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1186        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
1187        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1188        {
1189            let stream = self.gpu.stream();
1190            let (x_p, _gx) = x.device_ptr(&stream);
1191            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1192            let rc = unsafe {
1193                memra_mmq_iq_quantize_act(
1194                    x_p as *const f32,
1195                    s_p as *mut core::ffi::c_void,
1196                    in_f as i32,
1197                    n_tokens as i32,
1198                    stream.cu_stream() as *mut core::ffi::c_void,
1199                )
1200            };
1201            if rc != 0 {
1202                return Err(format!("memra_mmq_iq_quantize_act rc={rc}").into());
1203            }
1204        }
1205        Ok(scratch)
1206    }
1207
1208    /// Fused act-epilogue (research lever #3): silu/gelu(gate)*up + D4 quantize in one launch —
1209    /// replaces moe_pairs_{silu,gelu}_mul + mmq_iq_quantize_act without materializing the f32 act
1210    /// buffer (saves one full write + one full read pass over [n_pairs x n_ff]). Scratch bytes are
1211    /// BYTE-IDENTICAL to the two-pass path (kernel-check `iq fused act+quant` gates it).
1212    /// `act_kind`: 0 = silu*mul (qwen35moe), 1 = gelu_tanh*mul (gemma4).
1213    pub fn mmq_iq_fused_act_quant(
1214        &self,
1215        gate: &CudaSlice<f32>,
1216        up: &CudaSlice<f32>,
1217        in_f: usize,
1218        n_tokens: usize,
1219        act_kind: i32,
1220    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1221        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
1222        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1223        {
1224            let stream = self.gpu.stream();
1225            let (g_p, _gg) = gate.device_ptr(&stream);
1226            let (u_p, _gu) = up.device_ptr(&stream);
1227            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1228            let rc = unsafe {
1229                memra_mmq_iq_fused_act_quant(
1230                    g_p as *const f32,
1231                    u_p as *const f32,
1232                    s_p as *mut core::ffi::c_void,
1233                    in_f as i32,
1234                    n_tokens as i32,
1235                    act_kind,
1236                    stream.cu_stream() as *mut core::ffi::c_void,
1237                )
1238            };
1239            if rc != 0 {
1240                return Err(format!("memra_mmq_iq_fused_act_quant rc={rc}").into());
1241            }
1242        }
1243        Ok(scratch)
1244    }
1245
1246    /// Expert-segmented IQ3_S/IQ4_XS int8-MMA MMQ (the m16n8k16.s8 analog of moe_pairs_matvec_q8_dec).
1247    /// Same CSR inputs (table/ex_ids/ex_off/ex_pairs/pair_tok) + a pre-quantized q8_1_mmq activation
1248    /// scratch (from `mmq_iq_quantize_act` over n_tokens). y = [n_pairs, out_f] pair-major.
1249    #[allow(clippy::too_many_arguments)]
1250    pub fn mmq_iq_experts(
1251        &self,
1252        table: &CudaSlice<u64>,
1253        proj: i32,
1254        n_expert: usize,
1255        ex_ids: &CudaSlice<i32>,
1256        ex_off: &CudaSlice<i32>,
1257        ex_pairs: &CudaSlice<i32>,
1258        pair_tok: &CudaSlice<i32>,
1259        act_scratch: &CudaSlice<u8>,
1260        in_f: usize,
1261        out_f: usize,
1262        n_active: usize,
1263        n_pairs: usize,
1264        n_tokens: usize,
1265        qtype: i32,
1266        row_bytes: usize,
1267    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1268        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1269        {
1270            let stream = self.gpu.stream();
1271            let (tab_p, _g0) = table.device_ptr(&stream);
1272            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1273            let (eo_p, _g2) = ex_off.device_ptr(&stream);
1274            let (ep_p, _g3) = ex_pairs.device_ptr(&stream);
1275            let (pt_p, _g4) = pair_tok.device_ptr(&stream);
1276            let (as_p, _g5) = act_scratch.device_ptr(&stream);
1277            let (y_p, _g6) = y.device_ptr_mut(&stream);
1278            let rc = unsafe {
1279                memra_mmq_iq_experts(
1280                    tab_p as *const u64,
1281                    proj,
1282                    n_expert as i32,
1283                    ei_p as *const i32,
1284                    eo_p as *const i32,
1285                    ep_p as *const i32,
1286                    pt_p as *const i32,
1287                    as_p as *const core::ffi::c_void,
1288                    y_p as *mut f32,
1289                    in_f as i32,
1290                    out_f as i32,
1291                    n_active as i32,
1292                    n_tokens as i32,
1293                    qtype,
1294                    row_bytes as i64,
1295                    stream.cu_stream() as *mut core::ffi::c_void,
1296                )
1297            };
1298            if rc != 0 {
1299                return Err(format!("memra_mmq_iq_experts rc={rc}").into());
1300            }
1301        }
1302        Ok(y)
1303    }
1304
1305    /// Gather+convert the activation to f16 pair-major [n_pairs, in_f] for the grouped
1306    /// GEMM, normalized per row by its amax (raw f16 overflows on gemma's activation
1307    /// spikes — round 46 NaN find). Returns (act_f16, row_scales) — the scales fold back
1308    /// into the GEMM output. `pair_tok` = None when the input is already pair-major.
1309    pub fn moe_f16g_act(
1310        &self,
1311        x: &CudaSlice<f32>,
1312        pair_tok: Option<&CudaSlice<i32>>,
1313        in_f: usize,
1314        n_pairs: usize,
1315    ) -> Result<(CudaSlice<u8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1316        let mut act = self.alloc_uninit::<u8>(n_pairs * in_f * 2)?;
1317        let mut scales = self.alloc_uninit::<f32>(n_pairs)?;
1318        {
1319            let stream = self.gpu.stream();
1320            let (x_p, _gx) = x.device_ptr(&stream);
1321            let pt_p = match pair_tok {
1322                Some(pt) => { let (p, _g) = pt.device_ptr(&stream); p as *const i32 }
1323                None => std::ptr::null(),
1324            };
1325            let (a_p, _ga) = act.device_ptr_mut(&stream);
1326            let (s_p, _gs) = scales.device_ptr_mut(&stream);
1327            let rc = unsafe {
1328                memra_moe_f16g_gather_act(x_p as *const f32, pt_p,
1329                    a_p as *mut core::ffi::c_void, s_p as *mut f32,
1330                    in_f as i32, n_pairs as i32,
1331                    stream.cu_stream() as *mut core::ffi::c_void)
1332            };
1333            if rc != 0 { return Err(format!("memra_moe_f16g_gather_act rc={rc}").into()); }
1334        }
1335        Ok((act, scales))
1336    }
1337
1338    /// One projection through the grouped f16 lane: dequant the active experts' rows to an
1339    /// f16 workspace, then ONE grouped GEMM over the CSR groups (variable m per expert).
1340    /// y = f32 [n_pairs, out_f] pair-major — same layout as mmq_iq_experts.
1341    /// MEMRA_MOE_F16G=1: cublasGemmGroupedBatchedEx (+ h2f pass + per-projection sync — the
1342    /// grouped API runs on internal streams unordered with ours, round-47 ledger).
1343    /// MEMRA_MOE_F16G=2: single-kernel grouped GEMM on the engine stream (round 49) — the
1344    /// row scale folds into the kernel epilogue; no f16 C, no h2f, NO sync (ordered by
1345    /// construction). f16-MIRROR numeric class either way (argmax/spec gated, not
1346    /// byte-identity). Errors on unsupported qtype (caller keeps the MMQ arm as fallback).
1347    #[allow(clippy::too_many_arguments)]
1348    pub fn moe_f16_grouped(
1349        &self,
1350        table: &CudaSlice<u64>,
1351        proj: i32,
1352        n_expert: usize,
1353        ex_ids: &CudaSlice<i32>,
1354        ex_off_host: &[i32],
1355        ex_off_dev: &CudaSlice<i32>,
1356        act_f16: &CudaSlice<u8>,
1357        act_scale: &CudaSlice<f32>,
1358        in_f: usize,
1359        out_f: usize,
1360        n_active: usize,
1361        n_pairs: usize,
1362        qtype: i32,
1363        row_bytes: usize,
1364    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1365        let sk = crate::moe_f16g_mode() >= 2 && in_f % 32 == 0;
1366        // DIRECT-FROM-QUANT lane (lane/kquant-tile-loaders + lane/iq-direct-loaders, default
1367        // ON — MEMRA_F16G_DIRECT=0 is the rollback seam): Q4_K/Q6_K/IQ4_XS/IQ3_S expert
1368        // projections skip the dequant-workspace pass entirely; the sk visitor forms dequant
1369        // B tiles in-register from the superblocks. Bit-identical to the workspace path by
1370        // construction (kernel-check "f16g-kq-direct") — this is a pure data-movement change,
1371        // not a numeric-class change. Admission mirrors the C-side guards; the grid-scan
1372        // rollback arm (MEMRA_F16G_SK=0) keeps the workspace.
1373        let (shape_sel, cross) = crate::moe_f16g_sk_params();
1374        if sk && shape_sel >= 0 && crate::moe_f16g_direct_on(qtype)
1375            && (qtype == crate::QT_Q4_K || qtype == crate::QT_Q6_K
1376                || qtype == crate::QT_IQ4_XS || qtype == crate::QT_IQ3_S)
1377            && in_f % 256 == 0 && n_active <= 512 && n_active > 0
1378        {
1379            let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1380            let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1381            {
1382                let stream = self.gpu.stream();
1383                let (tab_p, _g0) = table.device_ptr(&stream);
1384                let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1385                let (a_p, _g2) = act_f16.device_ptr(&stream);
1386                let (s_p, _g3) = act_scale.device_ptr(&stream);
1387                let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
1388                let (y_p, _g5) = y.device_ptr_mut(&stream);
1389                let rc = unsafe {
1390                    memra_moe_kq_gemm_sk(tab_p as *const u64, proj, n_expert as i32,
1391                        ei_p as *const i32, a_p as *const core::ffi::c_void, y_p as *mut f32,
1392                        s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1393                        n_active as i32, max_m, in_f as i32, out_f as i32, qtype, cross,
1394                        crate::moe_f16g_tail_on() as i32, row_bytes as i64,
1395                        stream.cu_stream() as *mut core::ffi::c_void)
1396                };
1397                if rc != 0 { return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into()); }
1398            }
1399            return Ok(y);
1400        }
1401        // one-time cublas grouped init (algo heuristics + module load cost ~10% of a cold
1402        // g26 prime when paid inside the first projection): a tiny dummy grouped GEMM at
1403        // first use, synced, so the real prime runs warm. The =2 path never touches cublas.
1404        if !sk {
1405            static WARM: std::sync::Once = std::sync::Once::new();
1406            let mut warm_err = None;
1407            WARM.call_once(|| {
1408                let r = (|| -> Result<(), Box<dyn std::error::Error>> {
1409                    let w = self.alloc_uninit::<u8>(2 * 32 * 64 * 2)?;
1410                    let a = self.alloc_uninit::<u8>(4 * 64 * 2)?;
1411                    let mut yw = self.alloc_uninit::<u8>(4 * 32 * 2)?;
1412                    let off = [0i32, 2, 4];
1413                    let stream = self.gpu.stream();
1414                    let (w_p, _a1) = w.device_ptr(&stream);
1415                    let (a_p, _a2) = a.device_ptr(&stream);
1416                    let (y_p, _a3) = yw.device_ptr_mut(&stream);
1417                    let rc = unsafe {
1418                        memra_moe_f16g_gemm(w_p as *const core::ffi::c_void,
1419                            a_p as *const core::ffi::c_void, y_p as *mut core::ffi::c_void,
1420                            off.as_ptr(), 2, 64, 32,
1421                            stream.cu_stream() as *mut core::ffi::c_void)
1422                    };
1423                    if rc != 0 { return Err(format!("f16g warmup rc={rc}").into()); }
1424                    self.gpu.stream().synchronize()?;
1425                    Ok(())
1426                })();
1427                if let Err(e) = r { warm_err = Some(e.to_string()); }
1428            });
1429            if let Some(we) = warm_err { return Err(we.into()); }
1430        }
1431        let w_bytes = n_active * out_f * in_f * 2;
1432        let mut w_f16 = self.alloc_uninit::<u8>(w_bytes)?;
1433        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1434        {
1435            let stream = self.gpu.stream();
1436            let (tab_p, _g0) = table.device_ptr(&stream);
1437            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1438            let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
1439            let rc = unsafe {
1440                memra_moe_f16g_dequant(tab_p as *const u64, proj, n_expert as i32,
1441                    ei_p as *const i32, w_p as *mut core::ffi::c_void,
1442                    in_f as i32, out_f as i32, n_active as i32, qtype, row_bytes as i64,
1443                    stream.cu_stream() as *mut core::ffi::c_void)
1444            };
1445            if rc != 0 { return Err(format!("memra_moe_f16g_dequant rc={rc}").into()); }
1446            let (a_p, _g3) = act_f16.device_ptr(&stream);
1447            let (s_p, _g6) = act_scale.device_ptr(&stream);
1448            let (y_p, _g5) = y.device_ptr_mut(&stream);
1449            if sk {
1450                let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1451                let (off_p, _g7) = ex_off_dev.device_ptr(&stream);
1452                let (shape_sel, cross) = crate::moe_f16g_sk_params();
1453                let rc = unsafe {
1454                    memra_moe_f16g_gemm_sk(w_p as *const core::ffi::c_void,
1455                        a_p as *const core::ffi::c_void, y_p as *mut f32,
1456                        s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1457                        n_active as i32, max_m, in_f as i32, out_f as i32, shape_sel, cross,
1458                        crate::moe_f16g_tail_on() as i32,
1459                        stream.cu_stream() as *mut core::ffi::c_void)
1460                };
1461                if rc != 0 { return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into()); }
1462            } else {
1463                let mut y16 = self.alloc_uninit::<u8>(n_pairs * out_f * 2)?;
1464                let (y16_p, _g4) = y16.device_ptr_mut(&stream);
1465                let rc = unsafe {
1466                    memra_moe_f16g_gemm(w_p as *const core::ffi::c_void,
1467                        a_p as *const core::ffi::c_void, y16_p as *mut core::ffi::c_void,
1468                        ex_off_host.as_ptr(), n_active as i32, in_f as i32, out_f as i32,
1469                        stream.cu_stream() as *mut core::ffi::c_void)
1470                };
1471                if rc != 0 { return Err(format!("memra_moe_f16g_gemm rc={rc}").into()); }
1472                let rc = unsafe {
1473                    memra_moe_f16g_h2f_scaled(y16_p as *const core::ffi::c_void, y_p as *mut f32,
1474                        s_p as *const f32, out_f as i32, n_pairs as i32,
1475                        stream.cu_stream() as *mut core::ffi::c_void)
1476                };
1477                if rc != 0 { return Err(format!("memra_moe_f16g_h2f_scaled rc={rc}").into()); }
1478            }
1479        }
1480        // MODE 1 ONLY: cublasGemmGroupedBatchedEx issues through internal streams NOT ordered
1481        // with ours (round 46: NaN race, clean under sync — 205=205 MATCH). Full sync per
1482        // projection. Mode 2 (single kernel, our stream) is ordered by construction — no sync,
1483        // that is the point of this arc.
1484        if !sk {
1485            self.gpu.stream().synchronize()?;
1486        }
1487        if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
1488            // FULL NaN/Inf scan of w, act (through h2f) and y — localizes the corrupt stage.
1489            let wn = n_active * out_f * in_f;
1490            let an = n_pairs * in_f;
1491            let mut wf = self.alloc_uninit::<f32>(wn)?;
1492            let mut af = self.alloc_uninit::<f32>(an)?;
1493            {
1494                let stream = self.gpu.stream();
1495                let (w_p, _a) = w_f16.device_ptr(&stream);
1496                let (a_p, _b) = act_f16.device_ptr(&stream);
1497                let (wf_p, _c) = wf.device_ptr_mut(&stream);
1498                let (af_p, _d) = af.device_ptr_mut(&stream);
1499                unsafe {
1500                    memra_moe_f16g_h2f(w_p as *const core::ffi::c_void, wf_p as *mut f32, wn,
1501                        stream.cu_stream() as *mut core::ffi::c_void);
1502                    memra_moe_f16g_h2f(a_p as *const core::ffi::c_void, af_p as *mut f32, an,
1503                        stream.cu_stream() as *mut core::ffi::c_void);
1504                }
1505            }
1506            let (wh, ah, yh) = (self.dtoh(&wf)?, self.dtoh(&af)?, self.dtoh(&y)?);
1507            let scan = |v: &[f32]| -> (usize, f32) {
1508                let bad = v.iter().filter(|x| !x.is_finite()).count();
1509                let mx = v.iter().filter(|x| x.is_finite()).fold(0.0f32, |m, x| m.max(x.abs()));
1510                (bad, mx)
1511            };
1512            let (wb, wm) = scan(&wh); let (ab, am) = scan(&ah); let (yb, ym) = scan(&yh);
1513            eprintln!("[f16g-debug] proj={proj} w: bad={wb} max={wm:.3e} | act: bad={ab} \
1514                       max={am:.3e} | y: bad={yb} max={ym:.3e} (na={n_active} np={n_pairs} \
1515                       in={in_f} out={out_f})");
1516        }
1517        Ok(y)
1518    }
1519
1520    /// Raw sk grouped-GEMM entry for kernel-check ("f16g-sk" section): explicit shape/cross
1521    /// instead of the env policy. shape_sel < 0 = the round-49 grid-scan rollback arm; else
1522    /// the round-51 problem-visitor split at `cross` (1 forces all-128, i32::MAX all-32).
1523    /// tail: 1 = the deep tail (32x64x64 3-stage, lane/sk-tail-form) on sub-cross groups,
1524    /// 0 = the round-51 2-stage 32x64x32 tail.
1525    /// w_f16 = [n_active][out_f][in_f] f16 bytes, act_f16 = [n_pairs][in_f] f16 bytes.
1526    #[allow(clippy::too_many_arguments)]
1527    pub fn moe_f16g_gemm_sk_raw(&self, w_f16: &CudaSlice<u8>, act_f16: &CudaSlice<u8>,
1528        row_scale: &CudaSlice<f32>, ex_off_host: &[i32], ex_off_dev: &CudaSlice<i32>,
1529        in_f: usize, out_f: usize, n_pairs: usize, shape_sel: i32, cross: i32, tail: i32)
1530        -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1531        let n_active = ex_off_host.len() - 1;
1532        let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1533        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1534        {
1535            let stream = self.gpu.stream();
1536            let (w_p, _g0) = w_f16.device_ptr(&stream);
1537            let (a_p, _g1) = act_f16.device_ptr(&stream);
1538            let (s_p, _g2) = row_scale.device_ptr(&stream);
1539            let (off_p, _g3) = ex_off_dev.device_ptr(&stream);
1540            let (y_p, _g4) = y.device_ptr_mut(&stream);
1541            let rc = unsafe {
1542                memra_moe_f16g_gemm_sk(w_p as *const core::ffi::c_void,
1543                    a_p as *const core::ffi::c_void, y_p as *mut f32,
1544                    s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1545                    n_active as i32, max_m, in_f as i32, out_f as i32, shape_sel, cross,
1546                    tail, stream.cu_stream() as *mut core::ffi::c_void)
1547            };
1548            if rc != 0 { return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into()); }
1549        }
1550        Ok(y)
1551    }
1552
1553    /// Raw direct-from-quant sk grouped-GEMM entry for kernel-check ("f16g-kq-direct"):
1554    /// explicit cross/tail instead of the env policy. `table` = device u64 pointer table
1555    /// (proj-major, [n_proj][n_expert] — same contract as moe_f16_grouped), `ex_ids` =
1556    /// active-expert ids (device). Visitor forms only (the C side rejects anything else).
1557    #[allow(clippy::too_many_arguments)]
1558    pub fn moe_kq_gemm_sk_raw(&self, table: &CudaSlice<u64>, proj: i32, n_expert: usize,
1559        ex_ids: &CudaSlice<i32>, act_f16: &CudaSlice<u8>, row_scale: &CudaSlice<f32>,
1560        ex_off_host: &[i32], ex_off_dev: &CudaSlice<i32>,
1561        in_f: usize, out_f: usize, n_pairs: usize, qtype: i32, row_bytes: usize, cross: i32,
1562        tail: i32)
1563        -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1564        let n_active = ex_off_host.len() - 1;
1565        let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1566        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1567        {
1568            let stream = self.gpu.stream();
1569            let (tab_p, _g0) = table.device_ptr(&stream);
1570            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1571            let (a_p, _g2) = act_f16.device_ptr(&stream);
1572            let (s_p, _g3) = row_scale.device_ptr(&stream);
1573            let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
1574            let (y_p, _g5) = y.device_ptr_mut(&stream);
1575            let rc = unsafe {
1576                memra_moe_kq_gemm_sk(tab_p as *const u64, proj, n_expert as i32,
1577                    ei_p as *const i32, a_p as *const core::ffi::c_void, y_p as *mut f32,
1578                    s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1579                    n_active as i32, max_m, in_f as i32, out_f as i32, qtype, cross,
1580                    tail, row_bytes as i64,
1581                    stream.cu_stream() as *mut core::ffi::c_void)
1582            };
1583            if rc != 0 { return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into()); }
1584        }
1585        Ok(y)
1586    }
1587
1588    /// Raw dequant-workspace entry for kernel-check: dequant the active experts' rows to a
1589    /// fresh f16 workspace via the same kernel `moe_f16_grouped` uses (the direct loaders'
1590    /// bitwise reference).
1591    pub fn moe_f16g_dequant_raw(&self, table: &CudaSlice<u64>, proj: i32, n_expert: usize,
1592        ex_ids: &CudaSlice<i32>, in_f: usize, out_f: usize, n_active: usize, qtype: i32,
1593        row_bytes: usize)
1594        -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1595        let mut w_f16 = self.alloc_uninit::<u8>(n_active * out_f * in_f * 2)?;
1596        {
1597            let stream = self.gpu.stream();
1598            let (tab_p, _g0) = table.device_ptr(&stream);
1599            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1600            let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
1601            let rc = unsafe {
1602                memra_moe_f16g_dequant(tab_p as *const u64, proj, n_expert as i32,
1603                    ei_p as *const i32, w_p as *mut core::ffi::c_void,
1604                    in_f as i32, out_f as i32, n_active as i32, qtype, row_bytes as i64,
1605                    stream.cu_stream() as *mut core::ffi::c_void)
1606            };
1607            if rc != 0 { return Err(format!("memra_moe_f16g_dequant rc={rc}").into()); }
1608        }
1609        Ok(w_f16)
1610    }
1611}