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    // ---- Q1 accumulator instrument (cu/mmq_q8_0_f32acc.cu, lane/fp8-v3-gate) ----
199    // The Q8_0 MMQ floor's GEMM with the accumulator as its ONE free variable: arm S32 is the
200    // floor's `mma...s32.s8.s8.s32`, arm F32 is the same m16n8k32 shape and the same A/B/D fragment
201    // ABI with `mma...kind::f8f6f4...f32.e4m3.e4m3.f32` — the op cu/mmq_fp8_blk.cu accumulates in.
202    // Both take a PRE-QUANTIZED block_q8_1_mmq activation buffer, so the measurement is GEMM-only
203    // and cannot differ by a quantizer. Research instrument only: no dispatch seam, and neither arm's
204    // output is a numeric claim (see the TU header).
205    /// Activation-scratch bytes for the accumulator instrument (same padding rule as the floor).
206    pub fn memra_accprobe_act_bytes(in_f: i32, n_tokens: i32) -> usize;
207    /// ARM S32 — the floor's GEMM verbatim, s32 accumulate. Returns 0, 1, or 1000+cudaError.
208    pub fn memra_accprobe_gemm_s32(
209        w_q8_0_blocks: *const core::ffi::c_void,
210        act_q: *const core::ffi::c_void,
211        y: *mut f32,
212        in_f: i32,
213        out_f: i32,
214        n_tokens: i32,
215        stream: *mut core::ffi::c_void,
216    ) -> i32;
217    /// ARM F32 — byte-identical kernel, f32 accumulate over the e4m3 reading of the same bytes.
218    pub fn memra_accprobe_gemm_f32(
219        w_q8_0_blocks: *const core::ffi::c_void,
220        act_q: *const core::ffi::c_void,
221        y: *mut f32,
222        in_f: i32,
223        out_f: i32,
224        n_tokens: i32,
225        stream: *mut core::ffi::c_void,
226    ) -> i32;
227
228    /// Bytes needed for the block_q8_1_mmq (D4) activation scratch for the Q4_0 MMQ path.
229    pub fn memra_mmq_q4_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
230    /// Run the Q4_0 int8-MMA MMQ prefill GEMM (MEMRA_PP_Q4MMQ). Nibbles dequant to int8 at
231    /// tile-load (the -8 zero-point folds into the quants, D4 epilogue — same accuracy class as
232    /// the Q8_0 MMQ). `rp`: 0 = raw ggml 18B blocks, 1 = MEMRA_Q4RP split-plane repack (qs plane +
233    /// fp16 d plane) — pure address remap, bit-identical output either way. Requires
234    /// in_f % 32 == 0. Returns 0 or (1000 + cudaError).
235    pub fn memra_mmq_q4_0(
236        w_q4_0: *const core::ffi::c_void,
237        act_f32: *const f32,
238        y: *mut f32,
239        in_f: i32,
240        out_f: i32,
241        n_tokens: i32,
242        act_scratch: *mut core::ffi::c_void,
243        stream: *mut core::ffi::c_void,
244        rp: i32,
245    ) -> i32;
246    /// Quantize-only entry (quantize-once seam): f32 activation -> block_q8_1_mmq scratch.
247    pub fn memra_mmq_q4_0_quant_act(
248        act_f32: *const f32,
249        act_scratch: *mut core::ffi::c_void,
250        in_f: i32,
251        n_tokens: i32,
252        stream: *mut core::ffi::c_void,
253    ) -> i32;
254    /// GEMM-only entry: consumes a pre-quantized scratch (from memra_mmq_q4_0_quant_act).
255    pub fn memra_mmq_q4_0_gemm(
256        w_q4_0: *const core::ffi::c_void,
257        act_scratch: *const core::ffi::c_void,
258        y: *mut f32,
259        in_f: i32,
260        out_f: i32,
261        n_tokens: i32,
262        stream: *mut core::ffi::c_void,
263        rp: i32,
264    ) -> i32;
265    /// Stream-k fixup scratch bytes (one [MMQ_X x MMQ_Y] f32 slot per SM).
266    pub fn memra_mmq_q4_0_fixup_bytes() -> usize;
267    /// Force the CLC work-stealing arm: 1 = on, 0 = off (static grid), -1 = MEMRA_MMQ_CLC env
268    /// default. Schedule-only swap of the xy-tiling kernel — bit-identical output by
269    /// construction (perf-frontier lever #1). Returns 1 when the CLC kernel is compiled in
270    /// (SM_100+ gencode), 0 on sm_89/90a builds (force is a no-op there; static grid always).
271    pub fn memra_mmq_q4_0_set_clc(force: i32) -> i32;
272    /// Stream-k GEMM entry: deterministic form selection, with the SK form itself
273    /// falling back to tiling when wave efficiency is at least 90%.
274    pub fn memra_mmq_q4_0_gemm_sk(
275        w_q4_0: *const core::ffi::c_void,
276        act_scratch: *const core::ffi::c_void,
277        y: *mut f32,
278        fixup_scratch: *mut core::ffi::c_void,
279        in_f: i32,
280        out_f: i32,
281        n_tokens: i32,
282        stream: *mut core::ffi::c_void,
283        rp: i32,
284    ) -> i32;
285
286    // ---- IQ3_S / IQ4_XS expert-segmented int8-MMA MMQ (cu/mmq_iq_experts.cu, MEMRA_MOE_MMA) ----
287    /// Bytes for the token-major block_q8_1_mmq activation scratch (in_f, n_tokens).
288    pub fn memra_mmq_iq_experts_act_bytes(in_f: i32, n_tokens: i32) -> usize;
289    /// Quantize token-major f32 activation [n_tokens, in_f] -> block_q8_1_mmq (D4). Returns 0 or 1000+err.
290    pub fn memra_mmq_iq_quantize_act(
291        act_f32: *const f32,
292        act_scratch: *mut core::ffi::c_void,
293        in_f: i32,
294        n_tokens: i32,
295        stream: *mut core::ffi::c_void,
296    ) -> i32;
297    /// Fused act-epilogue: silu/gelu(gate)*up + q8_1_mmq (D4) quantize in ONE launch — no f32 act
298    /// buffer. gate/up pair-major [n_tokens, in_f]; scratch identical to memra_mmq_iq_quantize_act.
299    /// act_kind: 0=silu*mul, 1=gelu_tanh*mul. Byte-identical to the two-pass path (kernel-check gated).
300    pub fn memra_mmq_iq_fused_act_quant(
301        gate: *const f32,
302        up: *const f32,
303        act_scratch: *mut core::ffi::c_void,
304        in_f: i32,
305        n_tokens: i32,
306        act_kind: i32,
307        stream: *mut core::ffi::c_void,
308    ) -> i32;
309    /// Expert-segmented IQ MMA MMQ. Same CSR shape as moe_pairs_matvec_q8_dec: `table` = [3,n_expert]
310    /// device slab ptrs, CSR ex_ids/ex_off/ex_pairs group pairs by expert, pair_tok gathers the
311    /// activation row. y = [n_pairs, out_f] pair-major. `act_scratch` pre-quantized over n_tokens.
312    /// qtype: 5=IQ4_XS, 6=IQ3_S. Returns 0 or 1000+cudaError.
313    /// Dense-trunk IQ4_XS MMQ (lane/kquant-tile-loaders): the dense analog of the expert
314    /// kernel for non-expert IQ4_XS 2-D matmuls (the KAT-Coder trunk class). Quantizes the
315    /// f32 activation to D4 q8_1_mmq internally; `act_scratch` sized by
316    /// `memra_mmq_iq_experts_act_bytes`. Requires in_f % 256 == 0.
317    pub fn memra_mmq_iq4xs_dense(
318        w_blocks: *const core::ffi::c_void,
319        act_f32: *const f32,
320        y: *mut f32,
321        in_f: i32,
322        out_f: i32,
323        n_tokens: i32,
324        row_bytes: i64,
325        act_scratch: *mut core::ffi::c_void,
326        stream: *mut core::ffi::c_void,
327    ) -> i32;
328    pub fn memra_mmq_iq_experts(
329        table: *const u64,
330        proj: i32,
331        n_expert: i32,
332        ex_ids: *const i32,
333        ex_off: *const i32,
334        ex_pairs: *const i32,
335        pair_tok: *const i32,
336        act_scratch: *const core::ffi::c_void,
337        y: *mut f32,
338        in_f: i32,
339        out_f: i32,
340        n_active: i32,
341        n_tokens: i32,
342        qtype: i32,
343        row_bytes: i64,
344        stream: *mut core::ffi::c_void,
345    ) -> i32;
346
347    // ---- MoE grouped f16 GEMM (cu/moe_f16_grouped.cu, round 46 arc 2) ----
348    pub fn memra_moe_f16g_dequant(
349        table: *const u64,
350        proj: i32,
351        n_expert: i32,
352        ex_ids: *const i32,
353        w_f16: *mut core::ffi::c_void,
354        in_f: i32,
355        out_f: i32,
356        n_active: i32,
357        qtype: i32,
358        row_bytes: i64,
359        stream: *mut core::ffi::c_void,
360    ) -> i32;
361    pub fn memra_moe_f16g_gather_act(
362        x: *const f32,
363        pair_tok_or_null: *const i32,
364        act_f16: *mut core::ffi::c_void,
365        row_scale: *mut f32,
366        in_f: i32,
367        n_pairs: i32,
368        stream: *mut core::ffi::c_void,
369    ) -> i32;
370    pub fn memra_moe_f16g_h2f_scaled(
371        src_f16: *const core::ffi::c_void,
372        dst: *mut f32,
373        row_scale: *const f32,
374        ncols: i32,
375        nrows: i32,
376        stream: *mut core::ffi::c_void,
377    ) -> i32;
378    pub fn memra_moe_f16g_gemm(
379        w_f16: *const core::ffi::c_void,
380        act_f16: *const core::ffi::c_void,
381        y_f16: *mut core::ffi::c_void,
382        ex_off_host: *const i32,
383        n_active: i32,
384        in_f: i32,
385        out_f: i32,
386        stream: *mut core::ffi::c_void,
387    ) -> i32;
388    pub fn memra_moe_f16g_h2f(
389        src_f16: *const core::ffi::c_void,
390        dst: *mut f32,
391        n: usize,
392        stream: *mut core::ffi::c_void,
393    ) -> i32;
394    // Single-kernel grouped GEMM (MEMRA_MOE_F16G=2, rounds 49+51): on OUR stream, f32 C with
395    // the act row-scale folded in — no cublas internal-stream race, no sync. Round 51 runs it
396    // as a persistent problem-visitor over the real tiles with two tile forms (32x64 tail
397    // / 128x64x64 3-stage): shape_sel < 0 = the round-49 grid-scan kernel (rollback
398    // arm); else groups with m_e >= cross ride the 128 form. ex_off_host sizes the visitor
399    // grids host-side (the offsets are already there at the call site — no extra transfer).
400    // tail != 0 (lane/sk-tail-form): sub-cross groups ride the DEEP tail (32x64x64 3-stage);
401    // 0 = the round-51 2-stage 32x64x32 (MEMRA_F16G_TAIL=0 rollback). Byte-identical arms.
402    pub fn memra_moe_f16g_gemm_sk(
403        w_f16: *const core::ffi::c_void,
404        act_f16: *const core::ffi::c_void,
405        y_f32: *mut f32,
406        row_scale: *const f32,
407        ex_off_dev: *const i32,
408        ex_off_host: *const i32,
409        n_active: i32,
410        max_m: i32,
411        in_f: i32,
412        out_f: i32,
413        shape_sel: i32,
414        cross: i32,
415        tail: i32,
416        stream: *mut core::ffi::c_void,
417    ) -> i32;
418    // DIRECT-FROM-QUANT sk visitor grouped GEMM (lane/kquant-tile-loaders + iq-direct-loaders):
419    // the visitor forms with the B (weight) tiles dequanted in-register from the expert
420    // superblocks — no f16 dequant workspace pass. Bit-identical to the workspace path by
421    // construction (kernel-check "f16g-kq-direct"). qtype: QT_Q4_K | QT_Q6_K | QT_IQ4_XS |
422    // QT_IQ3_S; rc=2 = not admitted here (caller keeps the dequant-workspace path).
423    // tail: as memra_moe_f16g_gemm_sk.
424    pub fn memra_moe_kq_gemm_sk(
425        table: *const u64,
426        proj: i32,
427        n_expert: i32,
428        ex_ids: *const i32,
429        act_f16: *const core::ffi::c_void,
430        y_f32: *mut f32,
431        row_scale: *const f32,
432        ex_off_dev: *const i32,
433        ex_off_host: *const i32,
434        n_active: i32,
435        max_m: i32,
436        in_f: i32,
437        out_f: i32,
438        qtype: i32,
439        cross: i32,
440        tail: i32,
441        row_bytes: i64,
442        stream: *mut core::ffi::c_void,
443    ) -> i32;
444}
445
446/// W4A8-MMQ DEFAULT-FLIP seam (2026-07-05): the vendored MMQ prefill suite is DEFAULT-ON — NVFP4
447/// takes the W4A8 MMQ tile (same int8 accuracy class as the int8 GEMM it replaces, all exactness
448/// gates hold, ~1.9x pp512; the rp tile-loader arm coexists with the A6 split-plane repack) and
449/// Q4_K/Q5_K take the vendored k-quant int8-MMA MMQ (also int8-class; gated with W4A8 in the same
450/// battery — the predecessor's `MEMRA_MMQ_W4A8=1` arm engaged BOTH, this flip preserves exactly
451/// that measured config). `MEMRA_MMQ_W4A8=0` = escape hatch back to the int8 GEMM prefill
452/// everywhere. `MEMRA_MMQ=1` additionally switches GGUF-layout NVFP4 to the W4A4 mxf4nvf4 tile
453/// (speed/accuracy tradeoff opt-in, unchanged).
454pub fn mmq_w4a8_enabled() -> bool {
455    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
456    *ON.get_or_init(|| {
457        std::env::var("MEMRA_MMQ_W4A8")
458            .map(|v| v != "0")
459            .unwrap_or(true)
460    })
461}
462
463/// Residual high-precision activation channels for the W4A4 MMQ prefill path.
464/// `MEMRA_MMQ_RESIDUAL_K=<k>` keeps the k largest-magnitude activation channels out of the e2m1
465/// quantized path and adds their exact f32 contribution back as a rank-k correction. k=0 (default)
466/// is off; the kernel clamps to MMQ_MAX_RESIDUAL_K (64).
467///
468/// Read LIVE per call, not OnceLock'd, for the same reason `MEMRA_MMQ` is: the W4A4 exactness gate
469/// sweeps arms inside ONE process against ONE set of loaded weights, and a cached first read would
470/// pin every later arm to whatever the first one saw.
471pub fn mmq_residual_k() -> i32 {
472    std::env::var("MEMRA_MMQ_RESIDUAL_K")
473        .ok()
474        .and_then(|v| v.parse::<i32>().ok())
475        .unwrap_or(0)
476        .clamp(0, 64)
477}
478
479/// Q8_0 MMQ prefill seam (lane/ppmmq lever 2, DEFAULT ON since 2026-07-09 — `MEMRA_PP_Q8MMQ=0`
480/// reverts): routes Q8_0 dense
481/// projections (m>=16) through the vendored int8-MMA MMQ (cu/mmq_q8_0.cu) instead of the hand-rolled
482/// `qmatvec_gemm_q8_0` tiling GEMM. Its own numeric config (MMA f32 reduction order != the tiling
483/// GEMM's) — gated with the full exactness battery. Default OFF until the battery is green.
484pub fn mmq_q8_enabled() -> bool {
485    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
486    // Promotion battery (2026-07-09): argmax MATCH on 35B p1/p2/p3 + 9B p2/p3 (p4-16k OOMs
487    // identically with and without the flag — pre-existing gate capacity limit, not this seam);
488    // kernel-check ALL GREEN; run-spec K=1..8 PASS on 9B+35B. 35B pp 2456->3069 free-clock.
489    *ON.get_or_init(|| {
490        std::env::var("MEMRA_PP_Q8MMQ")
491            .map(|v| v != "0")
492            .unwrap_or(true)
493    })
494}
495
496/// IQ4_XS dense-trunk MMQ prefill seam (lane/kquant-tile-loaders, 2026-08-02): routes
497/// NON-expert IQ4_XS 2-D projections (m>=16) through the vendored-machinery int8-MMA dense
498/// MMQ (cu/mmq_iq_experts.cu `mmq_iq4xs_dense_kernel`) instead of the per-column dp4a grid
499/// — the KAT-Coder prefill wall (0.169x vs llama; zero weight reuse across tokens,
500/// research/kat-anomaly-20260802 §6). Its own numeric config (MMA reduction order) — gated
501/// with the full exactness battery. m=1..15 decode/verify keep dp4a (dispatch parity).
502/// `MEMRA_PP_IQMMQ=0` reverts.
503pub fn mmq_iq4xs_enabled() -> bool {
504    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
505    *ON.get_or_init(|| {
506        std::env::var("MEMRA_PP_IQMMQ")
507            .map(|v| v != "0")
508            .unwrap_or(true)
509    })
510}
511
512/// Q4_0 MMQ prefill seam (gemma-4-12B lane, 2026-07-22): routes Q4_0 dense projections (m>=16)
513/// through the vendored int8-MMA MMQ (cu/mmq_q4_0.cu) instead of the hand-rolled
514/// `qmatvec_gemm_q4_0[_rp]` tiling GEMM (measured 77% of the 12B prime pass). Its own numeric
515/// config (MMA f32 reduction order != the tiling GEMM's) — gated with the full exactness battery
516/// before default-flip; `MEMRA_PP_Q4MMQ=0` reverts.
517pub fn mmq_q4_enabled() -> bool {
518    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
519    *ON.get_or_init(|| {
520        std::env::var("MEMRA_PP_Q4MMQ")
521            .map(|v| v != "0")
522            .unwrap_or(true)
523    })
524}
525
526impl Engine {
527    /// True if `w` should take a vendored MMQ GEMM under the current env policy (see
528    /// `mmq_w4a8_enabled`): NVFP4 needs in_f % 64 == 0, Q4_K/Q5_K need in_f % 256 == 0.
529    pub fn mmq_supports(&self, w: &crate::model::GpuTensor) -> bool {
530        use crate::model::GpuTensor;
531        if crate::portable_mma_gated() {
532            return false;
533        }
534        let mmq_opt_in = std::env::var("MEMRA_MMQ").is_ok();
535        match w {
536            // A6 split-plane repacked NVFP4: ONLY the W4A8 loader has an rp arm (pure address
537            // remap, bit-identical output — mmq_nvfp4_w4a8.cu load_tiles_nvfp4_w4a8<is_rp>).
538            // The W4A4 loader (mmq_fp4.cu load_tiles_nvfp4_nvfp4) reads 36B GGUF blocks only,
539            // so an rp weight with W4A8 disabled falls through to the rp-ported int8 GEMM.
540            // NVFP4 W4A8/W4A4 launchers use .kind::f8f6f4 / mxf4nvf4 tile MMA — sm_100a+/
541            // sm_120a-only. On every portable build (incl. the 90a Hopper-MMA lane) they are
542            // fail-closed link stubs (build.rs), so never offer them here.
543            GpuTensor::Quant { qtype, rp, .. } if *qtype == crate::QT_NVFP4 && *rp => {
544                !cfg!(memra_portable_cuda) && mmq_w4a8_enabled() && w.in_features() % 64 == 0
545            }
546            // GGUF-layout NVFP4 (MEMRA_RP=0): W4A8 (default-on) or the explicit W4A4 opt-in.
547            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4 => {
548                !cfg!(memra_portable_cuda)
549                    && (mmq_w4a8_enabled() || mmq_opt_in)
550                    && w.in_features() % 64 == 0
551            }
552            GpuTensor::Quant { qtype, .. }
553                if *qtype == crate::QT_Q4_K || *qtype == crate::QT_Q5_K =>
554            {
555                (mmq_w4a8_enabled() || mmq_opt_in) && w.in_features() % 256 == 0
556            }
557            // Q8_0 dense projections (35B attn/ssm/shexp): opt-in only (MEMRA_PP_Q8MMQ=1), its own
558            // numeric config vs qmatvec_gemm_q8_0. in_f % 256 == 0: MMQ_ITER_K=256 loads 8-block
559            // groups, so a non-multiple row would read a garbage weight tail (fp16 d bytes can be
560            // NaN-pattern, and NaN * 0-padded-activation = NaN — the 26B ffn_down lesson).
561            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q8_0 => {
562                mmq_q8_enabled() && w.in_features() % 256 == 0
563            }
564            // Q4_0 dense projections (gemma QAT ggufs): MEMRA_PP_Q4MMQ seam. Both weight layouts
565            // (raw 18B blocks and the MEMRA_Q4RP split-plane repack) have loader arms. Same
566            // in_f % 256 == 0 tail rule as Q8_0 (26B ffn_down in_f=2112 NaN'd on the %32 gate);
567            // non-multiples fall back to the hand-rolled qmatvec_gemm_q4_0[_rp].
568            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q4_0 => {
569                mmq_q4_enabled() && w.in_features() % 256 == 0
570            }
571            // IQ4_XS dense projections (KAT-Coder trunk): m>=16 prefill only — decode and
572            // spec-verify (m<16) keep the qmatvec_iq4_XS_dp4a per-column program (the
573            // kat-anomaly dispatch-parity law). Requires the dp4a fast path itself enabled:
574            // MEMRA_IQ_FAST=0 (the Stage-A oracle rollback) must also kill this arm so the
575            // rollback stays a full-path seam. in_f % 256: MMQ_ITER_K walks whole superblocks.
576            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_IQ4_XS => {
577                mmq_iq4xs_enabled() && Self::iq_fast_enabled() && w.in_features() % 256 == 0
578            }
579            _ => false,
580        }
581    }
582
583    /// Unified vendored-MMQ dispatch: routes to the NVFP4 or Q4_K/Q5_K launcher by qtype.
584    /// Caller MUST have checked `mmq_supports(w)`. `x` is the RAW f32 activation.
585    pub fn qmatvec_mmq(
586        &self,
587        w: &crate::model::GpuTensor,
588        x: &CudaSlice<f32>,
589        m: usize,
590    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
591        use crate::model::GpuTensor;
592        let (in_f, out_f) = (w.in_features(), w.out_features());
593        let GpuTensor::Quant {
594            bytes,
595            scale,
596            qtype,
597            rp,
598            ..
599        } = w
600        else {
601            return Err("qmatvec_mmq: not a Quant tensor".into());
602        };
603        // NVFP4 tile choice: W4A8 (accuracy-safe int8 pair, DEFAULT since the flip) vs W4A4
604        // (mxf4nvf4 mma, explicit MEMRA_MMQ=1 speed/accuracy tradeoff). An rp weight ALWAYS takes
605        // W4A8 — only its loader has the split-plane arm (pure address remap, bit-identical).
606        // Explicit MEMRA_MMQ_W4A8=1 still overrides a simultaneous MEMRA_MMQ=1 (predecessor rule).
607        let w4a8_explicit = std::env::var("MEMRA_MMQ_W4A8")
608            .map(|v| v != "0")
609            .unwrap_or(false);
610        let use_w4a8 =
611            *rp || w4a8_explicit || (mmq_w4a8_enabled() && std::env::var("MEMRA_MMQ").is_err());
612        match *qtype {
613            // STAGE 2: the accuracy-safe int8 W4A8 MMQ tile (weight FP4->int8 dequant + q8_1
614            // activation) — handles BOTH weight layouts (rp = A6 split-plane vs GGUF blocks).
615            q if q == crate::QT_NVFP4 && use_w4a8 => {
616                self.qmatvec_mmq_nvfp4_w4a8(bytes, x, m, in_f, out_f, *scale, *rp)
617            }
618            q if q == crate::QT_NVFP4 => self.qmatvec_mmq_nvfp4(bytes, x, m, in_f, out_f, *scale),
619            q if q == crate::QT_Q4_K || q == crate::QT_Q5_K => {
620                let mut y = self.qmatvec_mmq_q45k_raw(bytes, x, m, in_f, out_f, q)?;
621                if *scale != 1.0 {
622                    self.scale_inplace(&mut y, *scale, m * out_f)?;
623                }
624                Ok(y)
625            }
626            q if q == crate::QT_Q8_0 => {
627                // wgmma arm (sm_90a, task 8): OPT-IN via MEMRA_WGMMA=1 — v0 measured 3845
628                // vs MMQ 8692 tok/s pp512 (2026-07-26 N=5), so MMQ stays the default until
629                // the pipelined wgmma wins. Reads the rp4 split-plane mirror + the engine's
630                // q8_1 activation planes. Same numeric class as MMQ (exact s32 per 32-block,
631                // one f32 fold per block, ascending K) — kernel-check tolerance-gated.
632                if cfg!(memra_hopper_mma) && out_f % 64 == 0 && crate::wgmma_gemm_enabled() {
633                    if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
634                        let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
635                        let mut y =
636                            self.qmatvec_gemm_q8_0_wgmma_raw(m4, &aq, &ad, m, in_f, out_f)?;
637                        if *scale != 1.0 {
638                            self.scale_inplace(&mut y, *scale, m * out_f)?;
639                        }
640                        return Ok(y);
641                    }
642                }
643                let mut y = self.qmatvec_mmq_q8_0_raw(bytes, x, m, in_f, out_f)?;
644                if *scale != 1.0 {
645                    self.scale_inplace(&mut y, *scale, m * out_f)?;
646                }
647                Ok(y)
648            }
649            q if q == crate::QT_Q4_0 => {
650                let mut y = self.qmatvec_mmq_q4_0_raw(bytes, x, m, in_f, out_f, *rp)?;
651                if *scale != 1.0 {
652                    self.scale_inplace(&mut y, *scale, m * out_f)?;
653                }
654                Ok(y)
655            }
656            q if q == crate::QT_IQ4_XS => {
657                let GpuTensor::Quant { row_bytes, .. } = w else {
658                    unreachable!()
659                };
660                let mut y = self.qmatvec_mmq_iq4xs_raw(bytes, x, m, in_f, out_f, *row_bytes)?;
661                if *scale != 1.0 {
662                    self.scale_inplace(&mut y, *scale, m * out_f)?;
663                }
664                Ok(y)
665            }
666            q => Err(format!("qmatvec_mmq: unsupported qtype {q}").into()),
667        }
668    }
669
670    /// Bare IQ4_XS dense MMQ launch (no macro-scale) — also the kernel_check gate entry.
671    pub fn qmatvec_mmq_iq4xs_raw(
672        &self,
673        bytes: &CudaSlice<u8>,
674        x: &CudaSlice<f32>,
675        m: usize,
676        in_f: usize,
677        out_f: usize,
678        row_bytes: usize,
679    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
680        assert!(
681            in_f % 256 == 0,
682            "MMQ IQ4_XS requires in_f % 256 == 0, got {in_f}"
683        );
684        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, m as i32) };
685        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
686        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
687        {
688            let stream = self.gpu.stream();
689            let (w_p, _gw) = bytes.device_ptr(&stream);
690            let (x_p, _gx) = x.device_ptr(&stream);
691            let (y_p, _gy) = y.device_ptr_mut(&stream);
692            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
693            let rc = unsafe {
694                memra_mmq_iq4xs_dense(
695                    w_p as *const core::ffi::c_void,
696                    x_p as *const f32,
697                    y_p as *mut f32,
698                    in_f as i32,
699                    out_f as i32,
700                    m as i32,
701                    row_bytes as i64,
702                    s_p as *mut core::ffi::c_void,
703                    stream.cu_stream() as *mut core::ffi::c_void,
704                )
705            };
706            if rc != 0 {
707                return Err(format!("memra_mmq_iq4xs_dense rc={rc}").into());
708            }
709        }
710        Ok(y)
711    }
712
713    /// Bare Q4_K/Q5_K MMQ launch (no macro-scale) — also the kernel_check accuracy-gate entry.
714    /// Conventional xy-tiling only (the vendored stream-K arm — MEMRA_MMQ_STREAMK — was removed
715    /// 2026-07-08: 1.11x per-GEMM but its k-split f32 reorder flipped the model argmax gate;
716    /// rig5090.jsonl 2026-07-03 has the record).
717    pub fn qmatvec_mmq_q45k_raw(
718        &self,
719        bytes: &CudaSlice<u8>,
720        x: &CudaSlice<f32>,
721        m: usize,
722        in_f: usize,
723        out_f: usize,
724        qtype: i32,
725    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
726        assert!(
727            in_f % 256 == 0,
728            "MMQ Q4_K/Q5_K requires in_f % 256 == 0, got {in_f}"
729        );
730        let act_bytes = unsafe { memra_mmq_q45k_act_bytes(in_f as i32, m as i32) };
731        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
732        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
733        {
734            let stream = self.gpu.stream();
735            let (w_p, _gw) = bytes.device_ptr(&stream);
736            let (x_p, _gx) = x.device_ptr(&stream);
737            let (y_p, _gy) = y.device_ptr_mut(&stream);
738            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
739            let launcher = if qtype == crate::QT_Q4_K {
740                memra_mmq_q4_K
741            } else {
742                memra_mmq_q5_K
743            };
744            let rc = unsafe {
745                launcher(
746                    w_p as *const core::ffi::c_void,
747                    x_p as *const f32,
748                    y_p as *mut f32,
749                    in_f as i32,
750                    out_f as i32,
751                    m as i32,
752                    s_p as *mut core::ffi::c_void,
753                    stream.cu_stream() as *mut core::ffi::c_void,
754                )
755            };
756            if rc != 0 {
757                return Err(format!("memra_mmq_q45k(qtype={qtype}) rc={rc}").into());
758            }
759        }
760        Ok(y)
761    }
762
763    /// Bare Q8_0 int8-MMA MMQ launch (no macro-scale) — the kernel_check accuracy-gate entry and
764    /// the `qmatvec_mmq` dispatch body. Conventional xy-tiling only (no stream-K / fixup scratch).
765    pub fn qmatvec_mmq_q8_0_raw(
766        &self,
767        bytes: &CudaSlice<u8>,
768        x: &CudaSlice<f32>,
769        m: usize,
770        in_f: usize,
771        out_f: usize,
772    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
773        assert!(
774            in_f % 32 == 0,
775            "MMQ Q8_0 requires in_f % 32 == 0, got {in_f}"
776        );
777        let act_bytes = unsafe { memra_mmq_q8_0_act_bytes(in_f as i32, m as i32) };
778        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
779        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
780        {
781            let stream = self.gpu.stream();
782            let (w_p, _gw) = bytes.device_ptr(&stream);
783            let (x_p, _gx) = x.device_ptr(&stream);
784            let (y_p, _gy) = y.device_ptr_mut(&stream);
785            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
786            let rc = unsafe {
787                memra_mmq_q8_0(
788                    w_p as *const core::ffi::c_void,
789                    x_p as *const f32,
790                    y_p as *mut f32,
791                    in_f as i32,
792                    out_f as i32,
793                    m as i32,
794                    s_p as *mut core::ffi::c_void,
795                    stream.cu_stream() as *mut core::ffi::c_void,
796                )
797            };
798            if rc != 0 {
799                return Err(format!("memra_mmq_q8_0 rc={rc}").into());
800            }
801        }
802        Ok(y)
803    }
804
805    /// Accumulator-instrument bytes for a pre-quantized block_q8_1_mmq activation buffer
806    /// (cu/mmq_q8_0_f32acc.cu). The caller synthesizes that buffer itself — see `accprobe_gemm`.
807    pub fn accprobe_act_bytes(&self, in_f: usize, m: usize) -> usize {
808        unsafe { memra_accprobe_act_bytes(in_f as i32, m as i32) }
809    }
810
811    /// Run one arm of the Q1 accumulator instrument. `f32acc=false` is the Q8_0 MMQ floor's GEMM
812    /// verbatim (s32 accumulate); `f32acc=true` is the byte-identical kernel with the f8f6f4 f32
813    /// accumulate. `act_q` is a PRE-QUANTIZED block_q8_1_mmq buffer of at least
814    /// `accprobe_act_bytes(in_f, m)` bytes — keeping the quantizer out of the timed region is the
815    /// point, so this wrapper does not build it. Research instrument: the output is not a numeric
816    /// claim.
817    pub fn accprobe_gemm(
818        &self,
819        w_q8_0: &CudaSlice<u8>,
820        act_q: &CudaSlice<u8>,
821        m: usize,
822        in_f: usize,
823        out_f: usize,
824        f32acc: bool,
825    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
826        assert!(
827            in_f % 32 == 0,
828            "accprobe requires in_f % 32 == 0, got {in_f}"
829        );
830        assert!(
831            act_q.len() >= self.accprobe_act_bytes(in_f, m),
832            "accprobe act_q too small: {} < {}",
833            act_q.len(),
834            self.accprobe_act_bytes(in_f, m)
835        );
836        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
837        {
838            let stream = self.gpu.stream();
839            let (w_p, _gw) = w_q8_0.device_ptr(&stream);
840            let (a_p, _ga) = act_q.device_ptr(&stream);
841            let (y_p, _gy) = y.device_ptr_mut(&stream);
842            let f = if f32acc {
843                memra_accprobe_gemm_f32
844            } else {
845                memra_accprobe_gemm_s32
846            };
847            let rc = unsafe {
848                f(
849                    w_p as *const core::ffi::c_void,
850                    a_p as *const core::ffi::c_void,
851                    y_p as *mut f32,
852                    in_f as i32,
853                    out_f as i32,
854                    m as i32,
855                    stream.cu_stream() as *mut core::ffi::c_void,
856                )
857            };
858            if rc != 0 {
859                let arm = if f32acc { "f32" } else { "s32" };
860                return Err(format!("memra_accprobe_gemm_{arm} rc={rc}").into());
861            }
862        }
863        Ok(y)
864    }
865
866    /// Open a quantize-once sharing window for the NEXT activation (quantize-once seam): sibling
867    /// Q4_0 MMQ matmuls on the SAME input (q/k/v; gate/up) quantize its D4 scratch once. Safe by
868    /// construction: a hit requires the same window epoch AND the same (ptr, m, in_f) — the caller
869    /// opens a window while it holds the shared input alive, so its address can neither change nor
870    /// be recycled inside the window. Paths that never call this never hit the cache.
871    pub fn mmq_act_begin(&self) {
872        use std::sync::atomic::Ordering;
873        MMQ_ACT_EPOCH.fetch_add(1, Ordering::Relaxed);
874        *MMQ_ACT_SLOT.lock().unwrap() = None;
875    }
876
877    /// Bare Q4_0 int8-MMA MMQ launch (no macro-scale) — the kernel_check accuracy-gate entry and
878    /// the `qmatvec_mmq` dispatch body. `rp` selects the weight layout (MEMRA_Q4RP split-plane vs
879    /// raw ggml 18B blocks) — pure address remap, bit-identical output.
880    pub fn qmatvec_mmq_q4_0_raw(
881        &self,
882        bytes: &CudaSlice<u8>,
883        x: &CudaSlice<f32>,
884        m: usize,
885        in_f: usize,
886        out_f: usize,
887        rp: bool,
888    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
889        use std::sync::atomic::Ordering;
890        assert!(
891            in_f % 32 == 0,
892            "MMQ Q4_0 requires in_f % 32 == 0, got {in_f}"
893        );
894        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
895        let stream = self.gpu.stream();
896        let (x_p, _gx) = x.device_ptr(&stream);
897        let epoch = MMQ_ACT_EPOCH.load(Ordering::Relaxed);
898        // quantize-once: reuse the window's scratch when the SAME activation comes back.
899        let mut slot = MMQ_ACT_SLOT.lock().unwrap();
900        let hit = matches!(&*slot,
901            Some((e, p, mm, inf, _)) if *e == epoch && *p == x_p as u64 && *mm == m && *inf == in_f);
902        if !hit {
903            let act_bytes = unsafe { memra_mmq_q4_0_act_bytes(in_f as i32, m as i32) };
904            let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
905            {
906                let (s_p, _gs) = scratch.device_ptr_mut(&stream);
907                let rc = unsafe {
908                    memra_mmq_q4_0_quant_act(
909                        x_p as *const f32,
910                        s_p as *mut core::ffi::c_void,
911                        in_f as i32,
912                        m as i32,
913                        stream.cu_stream() as *mut core::ffi::c_void,
914                    )
915                };
916                if rc != 0 {
917                    return Err(
918                        format!("memra_mmq_q4_0_quant_act(in_f={in_f}, m={m}) rc={rc}").into(),
919                    );
920                }
921            }
922            *slot = Some((epoch, x_p as u64, m, in_f, scratch));
923        }
924        let scratch = &slot.as_ref().unwrap().4;
925        {
926            let (w_p, _gw) = bytes.device_ptr(&stream);
927            let (y_p, _gy) = y.device_ptr_mut(&stream);
928            let (s_p, _gs) = scratch.device_ptr(&stream);
929            // Stream-k arm (DEFAULT since 2026-07-23; MEMRA_MMQ_SK=0 reverts to xy-tiling):
930            // small-batch tail-wave fix — the sk entry itself falls back to (bit-identical)
931            // tiling at >=90% wave efficiency. Band-class fold order below that. Gate: 12B
932            // pp512 +3.3% (1.005x vs llama), pp1736 +1.0%; 31B +0.5%; D512 sentinel MATCH.
933            //
934            // SPEC-SERVING FLIP (2026-07-27, the f16pv/wkv acceptance-law pattern): with
935            // MEMRA_DRAFT set, big dense models force tiling while MoE/small models defer
936            // to the fail-closed TILE form. The former shape-timing autotune was removed 2026-08-14:
937            // its per-process timing coin selected different fold orders on independent
938            // boots. On the measured 82-SM 5090, TILE is both faster and higher-acceptance
939            // for the 26B depth cell. Every other hardware class requires its own gate
940            // before selecting SK without an explicit form override.
941            // MEMRA_MMQ_SK controls entry and MEMRA_MMQ_SK_FORM pins the numerical form.
942            // HOPPER DEFAULT OFF (2026-07-31, #23): on sm_90a the SK arm computes WRONG
943            // values for the 26B a4b's non-rp Q4_0 shapes once the prefill width crosses
944            // 256 (prefill argmax garbage, maxdiff ~10; MEMRA_MMQ_SK=0 -> MATCH,
945            // one-variable kill x confirmed on-box). The SK split/fixup is SM-count
946            // dependent (132 vs 170) — until the kernel is
947            // fixed for that class, Hopper fails CLOSED to the bit-identical xy-tiling
948            // (cost on the healthy models: g12 -1.4%, g31 -0.6% prefill, N=3 on-box).
949            // sm_120a keeps the SK entry on (rig-divergence law). MEMRA_MMQ_SK=1 forces
950            // entry; MEMRA_MMQ_SK_FORM=sk forces the actual SK numerical form.
951            static SK_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
952            let sk = match crate::MMQ_SK_FORCE.load(std::sync::atomic::Ordering::Relaxed) {
953                0 => false,
954                1 => true,
955                _ => *SK_ON.get_or_init(|| {
956                    std::env::var("MEMRA_MMQ_SK")
957                        .map(|v| v != "0")
958                        .unwrap_or(!cfg!(memra_hopper_mma))
959                }),
960            };
961            let rc = if sk {
962                let mut fx = MMQ_FIXUP_SLOT.lock().unwrap();
963                if fx.is_none() {
964                    let nb = unsafe { memra_mmq_q4_0_fixup_bytes() };
965                    *fx = Some(self.alloc_uninit::<u8>(nb)?);
966                }
967                let (f_p, _gf) = fx.as_mut().unwrap().device_ptr_mut(&stream);
968                unsafe {
969                    memra_mmq_q4_0_gemm_sk(
970                        w_p as *const core::ffi::c_void,
971                        s_p as *const core::ffi::c_void,
972                        y_p as *mut f32,
973                        f_p as *mut core::ffi::c_void,
974                        in_f as i32,
975                        out_f as i32,
976                        m as i32,
977                        stream.cu_stream() as *mut core::ffi::c_void,
978                        rp as i32,
979                    )
980                }
981            } else {
982                unsafe {
983                    memra_mmq_q4_0_gemm(
984                        w_p as *const core::ffi::c_void,
985                        s_p as *const core::ffi::c_void,
986                        y_p as *mut f32,
987                        in_f as i32,
988                        out_f as i32,
989                        m as i32,
990                        stream.cu_stream() as *mut core::ffi::c_void,
991                        rp as i32,
992                    )
993                }
994            };
995            if rc != 0 {
996                return Err(format!(
997                    "memra_mmq_q4_0_gemm(rp={rp}, in_f={in_f}, out_f={out_f}, m={m}, wbytes={}) rc={rc}",
998                    bytes.len()
999                )
1000                .into());
1001            }
1002        }
1003        Ok(y)
1004    }
1005
1006    /// Run the vendored NVFP4 MMQ prefill GEMM from raw weight bytes + f32 activation.
1007    /// y[m, out_f] = x[m, in_f] @ W^T. The per-tensor NVFP4 macro-scale is FOLDED into the MMQ
1008    /// write-back epilogue (was a separate scale_inplace launch + full y round-trip per matmul).
1009    /// Same elementwise multiply -> bit-identical to the two-launch form.
1010    /// `x` is the RAW f32 activation (the launcher quantizes it to block_fp4_mmq internally).
1011    pub fn qmatvec_mmq_nvfp4(
1012        &self,
1013        bytes: &CudaSlice<u8>,
1014        x: &CudaSlice<f32>,
1015        m: usize,
1016        in_f: usize,
1017        out_f: usize,
1018        scale: f32,
1019    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1020        self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, scale)
1021    }
1022
1023    /// Bare MMQ launch (no macro-scale) — for the kernel_check accuracy gate.
1024    pub fn qmatvec_mmq_nvfp4_raw(
1025        &self,
1026        bytes: &CudaSlice<u8>,
1027        x: &CudaSlice<f32>,
1028        m: usize,
1029        in_f: usize,
1030        out_f: usize,
1031    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1032        self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, 1.0)
1033    }
1034
1035    /// Bare MMQ launch on the PRE-PORT activation quantizer (per-sub-block UE4M3 scale only, no
1036    /// per-token row amax). The numeric oracle for the two-level quantizer: kernel-check runs both
1037    /// and reports the accuracy delta, so the port's value is measured rather than asserted.
1038    pub fn qmatvec_mmq_nvfp4_raw_v1(
1039        &self,
1040        bytes: &CudaSlice<u8>,
1041        x: &CudaSlice<f32>,
1042        m: usize,
1043        in_f: usize,
1044        out_f: usize,
1045    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1046        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, false, 0)
1047    }
1048
1049    /// Bare MMQ launch with an explicit residual-channel count — for the kernel-check k sweep.
1050    pub fn qmatvec_mmq_nvfp4_raw_res(
1051        &self,
1052        bytes: &CudaSlice<u8>,
1053        x: &CudaSlice<f32>,
1054        m: usize,
1055        in_f: usize,
1056        out_f: usize,
1057        residual_k: i32,
1058    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1059        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, true, residual_k)
1060    }
1061
1062    fn qmatvec_mmq_nvfp4_scaled(
1063        &self,
1064        bytes: &CudaSlice<u8>,
1065        x: &CudaSlice<f32>,
1066        m: usize,
1067        in_f: usize,
1068        out_f: usize,
1069        scale: f32,
1070    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1071        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, scale, true, mmq_residual_k())
1072    }
1073
1074    fn qmatvec_mmq_nvfp4_inner(
1075        &self,
1076        bytes: &CudaSlice<u8>,
1077        x: &CudaSlice<f32>,
1078        m: usize,
1079        in_f: usize,
1080        out_f: usize,
1081        scale: f32,
1082        per_token_scale: bool,
1083        residual_k: i32,
1084    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1085        assert!(
1086            in_f % 64 == 0,
1087            "MMQ NVFP4 requires in_f % 64 == 0, got {in_f}"
1088        );
1089        let act_bytes = unsafe { memra_mmq_nvfp4_act_bytes(in_f as i32, m as i32) };
1090        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1091        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1092        {
1093            let stream = self.gpu.stream();
1094            let (w_p, _gw) = bytes.device_ptr(&stream);
1095            let (x_p, _gx) = x.device_ptr(&stream);
1096            let (y_p, _gy) = y.device_ptr_mut(&stream);
1097            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1098            let rc = unsafe {
1099                memra_mmq_nvfp4_ex2(
1100                    w_p as *const core::ffi::c_void,
1101                    x_p as *const f32,
1102                    y_p as *mut f32,
1103                    in_f as i32,
1104                    out_f as i32,
1105                    m as i32,
1106                    s_p as *mut core::ffi::c_void,
1107                    stream.cu_stream() as *mut core::ffi::c_void,
1108                    scale,
1109                    per_token_scale as i32,
1110                    residual_k,
1111                )
1112            };
1113            if rc != 0 {
1114                return Err(format!("memra_mmq_nvfp4_ex2 rc={rc}").into());
1115            }
1116        }
1117        Ok(y)
1118    }
1119
1120    /// STAGE 2 W4A8 MMQ NVFP4: same tile as the W4A4 path, but weight FP4 is LUT-dequantized to
1121    /// int8 at tile-load and the activation stays q8_1 int8 — the accuracy-safe rung. Macro-scale
1122    /// folded into the write-back epilogue (bit-identical to a post-matmul scale_inplace).
1123    /// `rp` selects the weight layout (A6 split-plane vs GGUF blocks) — bit-identical output.
1124    pub fn qmatvec_mmq_nvfp4_w4a8(
1125        &self,
1126        bytes: &CudaSlice<u8>,
1127        x: &CudaSlice<f32>,
1128        m: usize,
1129        in_f: usize,
1130        out_f: usize,
1131        scale: f32,
1132        rp: bool,
1133    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1134        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, scale, rp)
1135    }
1136
1137    /// Bare W4A8 MMQ launch (no macro-scale, GGUF layout) — for the kernel_check accuracy gate.
1138    pub fn qmatvec_mmq_nvfp4_w4a8_raw(
1139        &self,
1140        bytes: &CudaSlice<u8>,
1141        x: &CudaSlice<f32>,
1142        m: usize,
1143        in_f: usize,
1144        out_f: usize,
1145    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1146        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, false)
1147    }
1148
1149    /// Bare W4A8 MMQ launch on an A6 split-plane repacked weight — the rp-loader bit-identity gate
1150    /// compares this against `qmatvec_mmq_nvfp4_w4a8_raw` on the same weight.
1151    pub fn qmatvec_mmq_nvfp4_w4a8_raw_rp(
1152        &self,
1153        bytes: &CudaSlice<u8>,
1154        x: &CudaSlice<f32>,
1155        m: usize,
1156        in_f: usize,
1157        out_f: usize,
1158    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1159        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, true)
1160    }
1161
1162    fn qmatvec_mmq_nvfp4_w4a8_scaled(
1163        &self,
1164        bytes: &CudaSlice<u8>,
1165        x: &CudaSlice<f32>,
1166        m: usize,
1167        in_f: usize,
1168        out_f: usize,
1169        scale: f32,
1170        rp: bool,
1171    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1172        assert!(
1173            in_f % 64 == 0,
1174            "MMQ NVFP4 W4A8 requires in_f % 64 == 0, got {in_f}"
1175        );
1176        let act_bytes = unsafe { memra_mmq_nvfp4_w4a8_act_bytes(in_f as i32, m as i32) };
1177        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1178        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1179        {
1180            let stream = self.gpu.stream();
1181            let (w_p, _gw) = bytes.device_ptr(&stream);
1182            let (x_p, _gx) = x.device_ptr(&stream);
1183            let (y_p, _gy) = y.device_ptr_mut(&stream);
1184            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1185            // MEMRA_MMQ_F8F4=1: the R-B W4A8-FP8 tile (own numeric config; battery-gated seam).
1186            // Scratch layouts are footprint-identical, so only the entry point swaps.
1187            static F8F4: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1188            let f8f4 = *F8F4.get_or_init(|| std::env::var("MEMRA_MMQ_F8F4").as_deref() == Ok("1"));
1189            let rc = unsafe {
1190                if f8f4 {
1191                    memra_mmq_nvfp4_f8f4(
1192                        w_p as *const core::ffi::c_void,
1193                        x_p as *const f32,
1194                        y_p as *mut f32,
1195                        in_f as i32,
1196                        out_f as i32,
1197                        m as i32,
1198                        s_p as *mut core::ffi::c_void,
1199                        stream.cu_stream() as *mut core::ffi::c_void,
1200                        scale,
1201                        rp as i32,
1202                    )
1203                } else {
1204                    memra_mmq_nvfp4_w4a8(
1205                        w_p as *const core::ffi::c_void,
1206                        x_p as *const f32,
1207                        y_p as *mut f32,
1208                        in_f as i32,
1209                        out_f as i32,
1210                        m as i32,
1211                        s_p as *mut core::ffi::c_void,
1212                        stream.cu_stream() as *mut core::ffi::c_void,
1213                        scale,
1214                        rp as i32,
1215                    )
1216                }
1217            };
1218            if rc != 0 {
1219                return Err(format!("memra_mmq_nvfp4_w4a8(f8f4={f8f4}) rc={rc}").into());
1220            }
1221        }
1222        Ok(y)
1223    }
1224
1225    /// PER-BLOCK FP8 MMQ prefill GEMM (cu/mmq_fp8_blk.cu). `w_e4m3` is the raw checkpoint e4m3
1226    /// plane [out_f x in_f] and `blk_scales` the device f32 grid [ceil(out_f/128) x
1227    /// ceil(in_f/128)] — no re-quantization of either.
1228    pub fn qmatvec_mmq_fp8_blk(
1229        &self,
1230        w_e4m3: &CudaSlice<u8>,
1231        blk_scales: &CudaSlice<f32>,
1232        x: &CudaSlice<f32>,
1233        m: usize,
1234        in_f: usize,
1235        out_f: usize,
1236    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1237        self.qmatvec_mmq_fp8_blk_scaled(w_e4m3, blk_scales, x, m, in_f, out_f, 1.0)
1238    }
1239
1240    pub fn qmatvec_mmq_fp8_blk_scaled(
1241        &self,
1242        w_e4m3: &CudaSlice<u8>,
1243        blk_scales: &CudaSlice<f32>,
1244        x: &CudaSlice<f32>,
1245        m: usize,
1246        in_f: usize,
1247        out_f: usize,
1248        scale: f32,
1249    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1250        assert!(
1251            in_f % 16 == 0,
1252            "per-block FP8 MMQ requires in_f % 16 == 0, got {in_f}"
1253        );
1254        let want_scales = ((out_f + 127) / 128) * ((in_f + 127) / 128);
1255        assert!(
1256            blk_scales.len() >= want_scales,
1257            "blk_scales too small: {} < {want_scales}",
1258            blk_scales.len()
1259        );
1260        assert!(
1261            w_e4m3.len() >= out_f * in_f,
1262            "e4m3 plane too small: {} < {}",
1263            w_e4m3.len(),
1264            out_f * in_f
1265        );
1266        let act_bytes = unsafe { memra_mmq_fp8_blk_act_bytes(in_f as i32, m as i32) };
1267        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1268        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1269        {
1270            let stream = self.gpu.stream();
1271            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
1272            let (sc_p, _gsc) = blk_scales.device_ptr(&stream);
1273            let (x_p, _gx) = x.device_ptr(&stream);
1274            let (y_p, _gy) = y.device_ptr_mut(&stream);
1275            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1276            let rc = unsafe {
1277                memra_mmq_fp8_blk(
1278                    w_p as *const core::ffi::c_void,
1279                    sc_p as *const f32,
1280                    x_p as *const f32,
1281                    y_p as *mut f32,
1282                    in_f as i32,
1283                    out_f as i32,
1284                    m as i32,
1285                    s_p as *mut core::ffi::c_void,
1286                    stream.cu_stream() as *mut core::ffi::c_void,
1287                    scale,
1288                )
1289            };
1290            if rc != 0 {
1291                return Err(format!("memra_mmq_fp8_blk rc={rc}").into());
1292            }
1293        }
1294        Ok(y)
1295    }
1296
1297    /// Count e4m3 NaN codes (magnitude 0x7F) in a device e4m3 plane. 0 is the precondition for
1298    /// routing that tensor through `qmatvec_mmq_fp8_blk` (hardware decodes them to NaN, the
1299    /// host/ARM B' reference to 0.0).
1300    pub fn fp8_blk_nan_count(
1301        &self,
1302        w_e4m3: &CudaSlice<u8>,
1303    ) -> Result<u32, Box<dyn std::error::Error>> {
1304        let mut cnt = self.htod_u32_v(&[0u32])?;
1305        let n = w_e4m3.len();
1306        {
1307            let stream = self.gpu.stream();
1308            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
1309            let (c_p, _gc) = cnt.device_ptr_mut(&stream);
1310            let rc = unsafe {
1311                memra_fp8_blk_count_nan(
1312                    w_p as *const core::ffi::c_void,
1313                    n,
1314                    c_p as *mut u32,
1315                    stream.cu_stream() as *mut core::ffi::c_void,
1316                )
1317            };
1318            if rc != 0 {
1319                return Err(format!("memra_fp8_blk_count_nan rc={rc}").into());
1320            }
1321        }
1322        Ok(self.dtoh_u32(&cnt)?[0])
1323    }
1324
1325    /// Quantize token-major f32 activation [n_tokens, in_f] to the block_q8_1_mmq (D4) scratch the
1326    /// IQ expert-MMA kernel consumes. Returns the scratch buffer (one per proj input per layer).
1327    pub fn mmq_iq_quantize_act(
1328        &self,
1329        x: &CudaSlice<f32>,
1330        in_f: usize,
1331        n_tokens: usize,
1332    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1333        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
1334        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1335        {
1336            let stream = self.gpu.stream();
1337            let (x_p, _gx) = x.device_ptr(&stream);
1338            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1339            let rc = unsafe {
1340                memra_mmq_iq_quantize_act(
1341                    x_p as *const f32,
1342                    s_p as *mut core::ffi::c_void,
1343                    in_f as i32,
1344                    n_tokens as i32,
1345                    stream.cu_stream() as *mut core::ffi::c_void,
1346                )
1347            };
1348            if rc != 0 {
1349                return Err(format!("memra_mmq_iq_quantize_act rc={rc}").into());
1350            }
1351        }
1352        Ok(scratch)
1353    }
1354
1355    /// Fused act-epilogue (research lever #3): silu/gelu(gate)*up + D4 quantize in one launch —
1356    /// replaces moe_pairs_{silu,gelu}_mul + mmq_iq_quantize_act without materializing the f32 act
1357    /// buffer (saves one full write + one full read pass over [n_pairs x n_ff]). Scratch bytes are
1358    /// BYTE-IDENTICAL to the two-pass path (kernel-check `iq fused act+quant` gates it).
1359    /// `act_kind`: 0 = silu*mul (qwen35moe), 1 = gelu_tanh*mul (gemma4).
1360    pub fn mmq_iq_fused_act_quant(
1361        &self,
1362        gate: &CudaSlice<f32>,
1363        up: &CudaSlice<f32>,
1364        in_f: usize,
1365        n_tokens: usize,
1366        act_kind: i32,
1367    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1368        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
1369        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1370        {
1371            let stream = self.gpu.stream();
1372            let (g_p, _gg) = gate.device_ptr(&stream);
1373            let (u_p, _gu) = up.device_ptr(&stream);
1374            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1375            let rc = unsafe {
1376                memra_mmq_iq_fused_act_quant(
1377                    g_p as *const f32,
1378                    u_p as *const f32,
1379                    s_p as *mut core::ffi::c_void,
1380                    in_f as i32,
1381                    n_tokens as i32,
1382                    act_kind,
1383                    stream.cu_stream() as *mut core::ffi::c_void,
1384                )
1385            };
1386            if rc != 0 {
1387                return Err(format!("memra_mmq_iq_fused_act_quant rc={rc}").into());
1388            }
1389        }
1390        Ok(scratch)
1391    }
1392
1393    /// Expert-segmented IQ3_S/IQ4_XS int8-MMA MMQ (the m16n8k16.s8 analog of moe_pairs_matvec_q8_dec).
1394    /// Same CSR inputs (table/ex_ids/ex_off/ex_pairs/pair_tok) + a pre-quantized q8_1_mmq activation
1395    /// scratch (from `mmq_iq_quantize_act` over n_tokens). y = [n_pairs, out_f] pair-major.
1396    #[allow(clippy::too_many_arguments)]
1397    pub fn mmq_iq_experts(
1398        &self,
1399        table: &CudaSlice<u64>,
1400        proj: i32,
1401        n_expert: usize,
1402        ex_ids: &CudaSlice<i32>,
1403        ex_off: &CudaSlice<i32>,
1404        ex_pairs: &CudaSlice<i32>,
1405        pair_tok: &CudaSlice<i32>,
1406        act_scratch: &CudaSlice<u8>,
1407        in_f: usize,
1408        out_f: usize,
1409        n_active: usize,
1410        n_pairs: usize,
1411        n_tokens: usize,
1412        qtype: i32,
1413        row_bytes: usize,
1414    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1415        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1416        {
1417            let stream = self.gpu.stream();
1418            let (tab_p, _g0) = table.device_ptr(&stream);
1419            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1420            let (eo_p, _g2) = ex_off.device_ptr(&stream);
1421            let (ep_p, _g3) = ex_pairs.device_ptr(&stream);
1422            let (pt_p, _g4) = pair_tok.device_ptr(&stream);
1423            let (as_p, _g5) = act_scratch.device_ptr(&stream);
1424            let (y_p, _g6) = y.device_ptr_mut(&stream);
1425            let rc = unsafe {
1426                memra_mmq_iq_experts(
1427                    tab_p as *const u64,
1428                    proj,
1429                    n_expert as i32,
1430                    ei_p as *const i32,
1431                    eo_p as *const i32,
1432                    ep_p as *const i32,
1433                    pt_p as *const i32,
1434                    as_p as *const core::ffi::c_void,
1435                    y_p as *mut f32,
1436                    in_f as i32,
1437                    out_f as i32,
1438                    n_active as i32,
1439                    n_tokens as i32,
1440                    qtype,
1441                    row_bytes as i64,
1442                    stream.cu_stream() as *mut core::ffi::c_void,
1443                )
1444            };
1445            if rc != 0 {
1446                return Err(format!("memra_mmq_iq_experts rc={rc}").into());
1447            }
1448        }
1449        Ok(y)
1450    }
1451
1452    /// Gather+convert the activation to f16 pair-major [n_pairs, in_f] for the grouped
1453    /// GEMM, normalized per row by its amax (raw f16 overflows on gemma's activation
1454    /// spikes — round 46 NaN find). Returns (act_f16, row_scales) — the scales fold back
1455    /// into the GEMM output. `pair_tok` = None when the input is already pair-major.
1456    pub fn moe_f16g_act(
1457        &self,
1458        x: &CudaSlice<f32>,
1459        pair_tok: Option<&CudaSlice<i32>>,
1460        in_f: usize,
1461        n_pairs: usize,
1462    ) -> Result<(CudaSlice<u8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1463        let mut act = self.alloc_uninit::<u8>(n_pairs * in_f * 2)?;
1464        let mut scales = self.alloc_uninit::<f32>(n_pairs)?;
1465        {
1466            let stream = self.gpu.stream();
1467            let (x_p, _gx) = x.device_ptr(&stream);
1468            let pt_p = match pair_tok {
1469                Some(pt) => {
1470                    let (p, _g) = pt.device_ptr(&stream);
1471                    p as *const i32
1472                }
1473                None => std::ptr::null(),
1474            };
1475            let (a_p, _ga) = act.device_ptr_mut(&stream);
1476            let (s_p, _gs) = scales.device_ptr_mut(&stream);
1477            let rc = unsafe {
1478                memra_moe_f16g_gather_act(
1479                    x_p as *const f32,
1480                    pt_p,
1481                    a_p as *mut core::ffi::c_void,
1482                    s_p as *mut f32,
1483                    in_f as i32,
1484                    n_pairs as i32,
1485                    stream.cu_stream() as *mut core::ffi::c_void,
1486                )
1487            };
1488            if rc != 0 {
1489                return Err(format!("memra_moe_f16g_gather_act rc={rc}").into());
1490            }
1491        }
1492        Ok((act, scales))
1493    }
1494
1495    /// One projection through the grouped f16 lane: dequant the active experts' rows to an
1496    /// f16 workspace, then ONE grouped GEMM over the CSR groups (variable m per expert).
1497    /// y = f32 [n_pairs, out_f] pair-major — same layout as mmq_iq_experts.
1498    /// MEMRA_MOE_F16G=1: cublasGemmGroupedBatchedEx (+ h2f pass + per-projection sync — the
1499    /// grouped API runs on internal streams unordered with ours, round-47 ledger).
1500    /// MEMRA_MOE_F16G=2: single-kernel grouped GEMM on the engine stream (round 49) — the
1501    /// row scale folds into the kernel epilogue; no f16 C, no h2f, NO sync (ordered by
1502    /// construction). f16-MIRROR numeric class either way (argmax/spec gated, not
1503    /// byte-identity). Errors on unsupported qtype (caller keeps the MMQ arm as fallback).
1504    #[allow(clippy::too_many_arguments)]
1505    pub fn moe_f16_grouped(
1506        &self,
1507        table: &CudaSlice<u64>,
1508        proj: i32,
1509        n_expert: usize,
1510        ex_ids: &CudaSlice<i32>,
1511        ex_off_host: &[i32],
1512        ex_off_dev: &CudaSlice<i32>,
1513        act_f16: &CudaSlice<u8>,
1514        act_scale: &CudaSlice<f32>,
1515        in_f: usize,
1516        out_f: usize,
1517        n_active: usize,
1518        n_pairs: usize,
1519        qtype: i32,
1520        row_bytes: usize,
1521    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1522        let sk = crate::moe_f16g_mode() >= 2 && in_f % 32 == 0;
1523        // DIRECT-FROM-QUANT lane (lane/kquant-tile-loaders + lane/iq-direct-loaders, default
1524        // ON — MEMRA_F16G_DIRECT=0 is the rollback seam): Q4_K/Q6_K/IQ4_XS/IQ3_S expert
1525        // projections skip the dequant-workspace pass entirely; the sk visitor forms dequant
1526        // B tiles in-register from the superblocks. Bit-identical to the workspace path by
1527        // construction (kernel-check "f16g-kq-direct") — this is a pure data-movement change,
1528        // not a numeric-class change. Admission mirrors the C-side guards; the grid-scan
1529        // rollback arm (MEMRA_F16G_SK=0) keeps the workspace.
1530        let (shape_sel, cross) = crate::moe_f16g_sk_params();
1531        if sk
1532            && shape_sel >= 0
1533            && crate::moe_f16g_direct_on(qtype)
1534            && (qtype == crate::QT_Q4_K
1535                || qtype == crate::QT_Q6_K
1536                || qtype == crate::QT_IQ4_XS
1537                || qtype == crate::QT_IQ3_S)
1538            && in_f % 256 == 0
1539            && n_active <= 512
1540            && n_active > 0
1541        {
1542            let max_m = ex_off_host
1543                .windows(2)
1544                .map(|w| w[1] - w[0])
1545                .max()
1546                .unwrap_or(0);
1547            let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1548            {
1549                let stream = self.gpu.stream();
1550                let (tab_p, _g0) = table.device_ptr(&stream);
1551                let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1552                let (a_p, _g2) = act_f16.device_ptr(&stream);
1553                let (s_p, _g3) = act_scale.device_ptr(&stream);
1554                let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
1555                let (y_p, _g5) = y.device_ptr_mut(&stream);
1556                let rc = unsafe {
1557                    memra_moe_kq_gemm_sk(
1558                        tab_p as *const u64,
1559                        proj,
1560                        n_expert as i32,
1561                        ei_p as *const i32,
1562                        a_p as *const core::ffi::c_void,
1563                        y_p as *mut f32,
1564                        s_p as *const f32,
1565                        off_p as *const i32,
1566                        ex_off_host.as_ptr(),
1567                        n_active as i32,
1568                        max_m,
1569                        in_f as i32,
1570                        out_f as i32,
1571                        qtype,
1572                        cross,
1573                        crate::moe_f16g_tail_on() as i32,
1574                        row_bytes as i64,
1575                        stream.cu_stream() as *mut core::ffi::c_void,
1576                    )
1577                };
1578                if rc != 0 {
1579                    return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into());
1580                }
1581            }
1582            return Ok(y);
1583        }
1584        // one-time cublas grouped init (algo heuristics + module load cost ~10% of a cold
1585        // g26 prime when paid inside the first projection): a tiny dummy grouped GEMM at
1586        // first use, synced, so the real prime runs warm. The =2 path never touches cublas.
1587        if !sk {
1588            static WARM: std::sync::Once = std::sync::Once::new();
1589            let mut warm_err = None;
1590            WARM.call_once(|| {
1591                let r = (|| -> Result<(), Box<dyn std::error::Error>> {
1592                    let w = self.alloc_uninit::<u8>(2 * 32 * 64 * 2)?;
1593                    let a = self.alloc_uninit::<u8>(4 * 64 * 2)?;
1594                    let mut yw = self.alloc_uninit::<u8>(4 * 32 * 2)?;
1595                    let off = [0i32, 2, 4];
1596                    let stream = self.gpu.stream();
1597                    let (w_p, _a1) = w.device_ptr(&stream);
1598                    let (a_p, _a2) = a.device_ptr(&stream);
1599                    let (y_p, _a3) = yw.device_ptr_mut(&stream);
1600                    let rc = unsafe {
1601                        memra_moe_f16g_gemm(
1602                            w_p as *const core::ffi::c_void,
1603                            a_p as *const core::ffi::c_void,
1604                            y_p as *mut core::ffi::c_void,
1605                            off.as_ptr(),
1606                            2,
1607                            64,
1608                            32,
1609                            stream.cu_stream() as *mut core::ffi::c_void,
1610                        )
1611                    };
1612                    if rc != 0 {
1613                        return Err(format!("f16g warmup rc={rc}").into());
1614                    }
1615                    self.gpu.stream().synchronize()?;
1616                    Ok(())
1617                })();
1618                if let Err(e) = r {
1619                    warm_err = Some(e.to_string());
1620                }
1621            });
1622            if let Some(we) = warm_err {
1623                return Err(we.into());
1624            }
1625        }
1626        let w_bytes = n_active * out_f * in_f * 2;
1627        let mut w_f16 = self.alloc_uninit::<u8>(w_bytes)?;
1628        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1629        {
1630            let stream = self.gpu.stream();
1631            let (tab_p, _g0) = table.device_ptr(&stream);
1632            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1633            let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
1634            let rc = unsafe {
1635                memra_moe_f16g_dequant(
1636                    tab_p as *const u64,
1637                    proj,
1638                    n_expert as i32,
1639                    ei_p as *const i32,
1640                    w_p as *mut core::ffi::c_void,
1641                    in_f as i32,
1642                    out_f as i32,
1643                    n_active as i32,
1644                    qtype,
1645                    row_bytes as i64,
1646                    stream.cu_stream() as *mut core::ffi::c_void,
1647                )
1648            };
1649            if rc != 0 {
1650                return Err(format!("memra_moe_f16g_dequant rc={rc}").into());
1651            }
1652            let (a_p, _g3) = act_f16.device_ptr(&stream);
1653            let (s_p, _g6) = act_scale.device_ptr(&stream);
1654            let (y_p, _g5) = y.device_ptr_mut(&stream);
1655            if sk {
1656                let max_m = ex_off_host
1657                    .windows(2)
1658                    .map(|w| w[1] - w[0])
1659                    .max()
1660                    .unwrap_or(0);
1661                let (off_p, _g7) = ex_off_dev.device_ptr(&stream);
1662                let (shape_sel, cross) = crate::moe_f16g_sk_params();
1663                let rc = unsafe {
1664                    memra_moe_f16g_gemm_sk(
1665                        w_p as *const core::ffi::c_void,
1666                        a_p as *const core::ffi::c_void,
1667                        y_p as *mut f32,
1668                        s_p as *const f32,
1669                        off_p as *const i32,
1670                        ex_off_host.as_ptr(),
1671                        n_active as i32,
1672                        max_m,
1673                        in_f as i32,
1674                        out_f as i32,
1675                        shape_sel,
1676                        cross,
1677                        crate::moe_f16g_tail_on() as i32,
1678                        stream.cu_stream() as *mut core::ffi::c_void,
1679                    )
1680                };
1681                if rc != 0 {
1682                    return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into());
1683                }
1684            } else {
1685                let mut y16 = self.alloc_uninit::<u8>(n_pairs * out_f * 2)?;
1686                let (y16_p, _g4) = y16.device_ptr_mut(&stream);
1687                let rc = unsafe {
1688                    memra_moe_f16g_gemm(
1689                        w_p as *const core::ffi::c_void,
1690                        a_p as *const core::ffi::c_void,
1691                        y16_p as *mut core::ffi::c_void,
1692                        ex_off_host.as_ptr(),
1693                        n_active as i32,
1694                        in_f as i32,
1695                        out_f as i32,
1696                        stream.cu_stream() as *mut core::ffi::c_void,
1697                    )
1698                };
1699                if rc != 0 {
1700                    return Err(format!("memra_moe_f16g_gemm rc={rc}").into());
1701                }
1702                let rc = unsafe {
1703                    memra_moe_f16g_h2f_scaled(
1704                        y16_p as *const core::ffi::c_void,
1705                        y_p as *mut f32,
1706                        s_p as *const f32,
1707                        out_f as i32,
1708                        n_pairs as i32,
1709                        stream.cu_stream() as *mut core::ffi::c_void,
1710                    )
1711                };
1712                if rc != 0 {
1713                    return Err(format!("memra_moe_f16g_h2f_scaled rc={rc}").into());
1714                }
1715            }
1716        }
1717        // MODE 1 ONLY: cublasGemmGroupedBatchedEx issues through internal streams NOT ordered
1718        // with ours (round 46: NaN race, clean under sync — 205=205 MATCH). Full sync per
1719        // projection. Mode 2 (single kernel, our stream) is ordered by construction — no sync,
1720        // that is the point of this arc.
1721        if !sk {
1722            self.gpu.stream().synchronize()?;
1723        }
1724        if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
1725            // FULL NaN/Inf scan of w, act (through h2f) and y — localizes the corrupt stage.
1726            let wn = n_active * out_f * in_f;
1727            let an = n_pairs * in_f;
1728            let mut wf = self.alloc_uninit::<f32>(wn)?;
1729            let mut af = self.alloc_uninit::<f32>(an)?;
1730            {
1731                let stream = self.gpu.stream();
1732                let (w_p, _a) = w_f16.device_ptr(&stream);
1733                let (a_p, _b) = act_f16.device_ptr(&stream);
1734                let (wf_p, _c) = wf.device_ptr_mut(&stream);
1735                let (af_p, _d) = af.device_ptr_mut(&stream);
1736                unsafe {
1737                    memra_moe_f16g_h2f(
1738                        w_p as *const core::ffi::c_void,
1739                        wf_p as *mut f32,
1740                        wn,
1741                        stream.cu_stream() as *mut core::ffi::c_void,
1742                    );
1743                    memra_moe_f16g_h2f(
1744                        a_p as *const core::ffi::c_void,
1745                        af_p as *mut f32,
1746                        an,
1747                        stream.cu_stream() as *mut core::ffi::c_void,
1748                    );
1749                }
1750            }
1751            let (wh, ah, yh) = (self.dtoh(&wf)?, self.dtoh(&af)?, self.dtoh(&y)?);
1752            let scan = |v: &[f32]| -> (usize, f32) {
1753                let bad = v.iter().filter(|x| !x.is_finite()).count();
1754                let mx = v
1755                    .iter()
1756                    .filter(|x| x.is_finite())
1757                    .fold(0.0f32, |m, x| m.max(x.abs()));
1758                (bad, mx)
1759            };
1760            let (wb, wm) = scan(&wh);
1761            let (ab, am) = scan(&ah);
1762            let (yb, ym) = scan(&yh);
1763            eprintln!(
1764                "[f16g-debug] proj={proj} w: bad={wb} max={wm:.3e} | act: bad={ab} \
1765                       max={am:.3e} | y: bad={yb} max={ym:.3e} (na={n_active} np={n_pairs} \
1766                       in={in_f} out={out_f})"
1767            );
1768        }
1769        Ok(y)
1770    }
1771
1772    /// Raw sk grouped-GEMM entry for kernel-check ("f16g-sk" section): explicit shape/cross
1773    /// instead of the env policy. shape_sel < 0 = the round-49 grid-scan rollback arm; else
1774    /// the round-51 problem-visitor split at `cross` (1 forces all-128, i32::MAX all-32).
1775    /// tail: 1 = the deep tail (32x64x64 3-stage, lane/sk-tail-form) on sub-cross groups,
1776    /// 0 = the round-51 2-stage 32x64x32 tail.
1777    /// w_f16 = [n_active][out_f][in_f] f16 bytes, act_f16 = [n_pairs][in_f] f16 bytes.
1778    #[allow(clippy::too_many_arguments)]
1779    pub fn moe_f16g_gemm_sk_raw(
1780        &self,
1781        w_f16: &CudaSlice<u8>,
1782        act_f16: &CudaSlice<u8>,
1783        row_scale: &CudaSlice<f32>,
1784        ex_off_host: &[i32],
1785        ex_off_dev: &CudaSlice<i32>,
1786        in_f: usize,
1787        out_f: usize,
1788        n_pairs: usize,
1789        shape_sel: i32,
1790        cross: i32,
1791        tail: i32,
1792    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1793        let n_active = ex_off_host.len() - 1;
1794        let max_m = ex_off_host
1795            .windows(2)
1796            .map(|w| w[1] - w[0])
1797            .max()
1798            .unwrap_or(0);
1799        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1800        {
1801            let stream = self.gpu.stream();
1802            let (w_p, _g0) = w_f16.device_ptr(&stream);
1803            let (a_p, _g1) = act_f16.device_ptr(&stream);
1804            let (s_p, _g2) = row_scale.device_ptr(&stream);
1805            let (off_p, _g3) = ex_off_dev.device_ptr(&stream);
1806            let (y_p, _g4) = y.device_ptr_mut(&stream);
1807            let rc = unsafe {
1808                memra_moe_f16g_gemm_sk(
1809                    w_p as *const core::ffi::c_void,
1810                    a_p as *const core::ffi::c_void,
1811                    y_p as *mut f32,
1812                    s_p as *const f32,
1813                    off_p as *const i32,
1814                    ex_off_host.as_ptr(),
1815                    n_active as i32,
1816                    max_m,
1817                    in_f as i32,
1818                    out_f as i32,
1819                    shape_sel,
1820                    cross,
1821                    tail,
1822                    stream.cu_stream() as *mut core::ffi::c_void,
1823                )
1824            };
1825            if rc != 0 {
1826                return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into());
1827            }
1828        }
1829        Ok(y)
1830    }
1831
1832    /// Raw direct-from-quant sk grouped-GEMM entry for kernel-check ("f16g-kq-direct"):
1833    /// explicit cross/tail instead of the env policy. `table` = device u64 pointer table
1834    /// (proj-major, [n_proj][n_expert] — same contract as moe_f16_grouped), `ex_ids` =
1835    /// active-expert ids (device). Visitor forms only (the C side rejects anything else).
1836    #[allow(clippy::too_many_arguments)]
1837    pub fn moe_kq_gemm_sk_raw(
1838        &self,
1839        table: &CudaSlice<u64>,
1840        proj: i32,
1841        n_expert: usize,
1842        ex_ids: &CudaSlice<i32>,
1843        act_f16: &CudaSlice<u8>,
1844        row_scale: &CudaSlice<f32>,
1845        ex_off_host: &[i32],
1846        ex_off_dev: &CudaSlice<i32>,
1847        in_f: usize,
1848        out_f: usize,
1849        n_pairs: usize,
1850        qtype: i32,
1851        row_bytes: usize,
1852        cross: i32,
1853        tail: i32,
1854    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1855        let n_active = ex_off_host.len() - 1;
1856        let max_m = ex_off_host
1857            .windows(2)
1858            .map(|w| w[1] - w[0])
1859            .max()
1860            .unwrap_or(0);
1861        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1862        {
1863            let stream = self.gpu.stream();
1864            let (tab_p, _g0) = table.device_ptr(&stream);
1865            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1866            let (a_p, _g2) = act_f16.device_ptr(&stream);
1867            let (s_p, _g3) = row_scale.device_ptr(&stream);
1868            let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
1869            let (y_p, _g5) = y.device_ptr_mut(&stream);
1870            let rc = unsafe {
1871                memra_moe_kq_gemm_sk(
1872                    tab_p as *const u64,
1873                    proj,
1874                    n_expert as i32,
1875                    ei_p as *const i32,
1876                    a_p as *const core::ffi::c_void,
1877                    y_p as *mut f32,
1878                    s_p as *const f32,
1879                    off_p as *const i32,
1880                    ex_off_host.as_ptr(),
1881                    n_active as i32,
1882                    max_m,
1883                    in_f as i32,
1884                    out_f as i32,
1885                    qtype,
1886                    cross,
1887                    tail,
1888                    row_bytes as i64,
1889                    stream.cu_stream() as *mut core::ffi::c_void,
1890                )
1891            };
1892            if rc != 0 {
1893                return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into());
1894            }
1895        }
1896        Ok(y)
1897    }
1898
1899    /// Raw dequant-workspace entry for kernel-check: dequant the active experts' rows to a
1900    /// fresh f16 workspace via the same kernel `moe_f16_grouped` uses (the direct loaders'
1901    /// bitwise reference).
1902    pub fn moe_f16g_dequant_raw(
1903        &self,
1904        table: &CudaSlice<u64>,
1905        proj: i32,
1906        n_expert: usize,
1907        ex_ids: &CudaSlice<i32>,
1908        in_f: usize,
1909        out_f: usize,
1910        n_active: usize,
1911        qtype: i32,
1912        row_bytes: usize,
1913    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1914        let mut w_f16 = self.alloc_uninit::<u8>(n_active * out_f * in_f * 2)?;
1915        {
1916            let stream = self.gpu.stream();
1917            let (tab_p, _g0) = table.device_ptr(&stream);
1918            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1919            let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
1920            let rc = unsafe {
1921                memra_moe_f16g_dequant(
1922                    tab_p as *const u64,
1923                    proj,
1924                    n_expert as i32,
1925                    ei_p as *const i32,
1926                    w_p as *mut core::ffi::c_void,
1927                    in_f as i32,
1928                    out_f as i32,
1929                    n_active as i32,
1930                    qtype,
1931                    row_bytes as i64,
1932                    stream.cu_stream() as *mut core::ffi::c_void,
1933                )
1934            };
1935            if rc != 0 {
1936                return Err(format!("memra_moe_f16g_dequant rc={rc}").into());
1937            }
1938        }
1939        Ok(w_f16)
1940    }
1941}