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, CudaView, 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
28/// Model-agnostic expert-major CSR used by grouped projection backends.
29///
30/// `ex_pairs` is a permutation of pair-major output rows. `pair_tok[pair]` selects the activation
31/// row consumed by that pair. Model adapters supply route choices; this type builds, validates,
32/// owns, and uploads the expert-major schedule.
33pub struct ExpertCsr {
34    ex_ids: Vec<i32>,
35    ex_off: Vec<i32>,
36    ex_pairs: Vec<i32>,
37    pair_tok: Vec<i32>,
38    n_expert: usize,
39    n_tokens: usize,
40}
41
42impl ExpertCsr {
43    /// Build an expert-major schedule from token-major top-k route choices.
44    pub fn from_token_routes(
45        n_expert: usize,
46        n_tokens: usize,
47        experts_per_token: usize,
48        selected: &[usize],
49    ) -> Result<Self, String> {
50        if experts_per_token == 0 {
51            return Err("grouped expert CSR experts/token must be nonzero".into());
52        }
53        let n_pairs = n_tokens
54            .checked_mul(experts_per_token)
55            .ok_or("grouped expert CSR route count overflow")?;
56        if selected.len() != n_pairs {
57            return Err(format!(
58                "grouped expert CSR selected routes {} != {n_tokens}x{experts_per_token} \
59                 ({n_pairs})",
60                selected.len()
61            ));
62        }
63        let pair_tok = (0..n_pairs)
64            .map(|pair| pair / experts_per_token)
65            .collect::<Vec<_>>();
66        Self::from_pair_rows(n_expert, n_tokens, selected, &pair_tok)
67    }
68
69    /// Build an expert-major schedule with an explicit activation row for every output pair.
70    ///
71    /// This is used by chained grouped projections: gate/up pairs select token rows, while the
72    /// down projection selects the corresponding pair-major activation row.
73    pub fn from_pair_rows(
74        n_expert: usize,
75        n_tokens: usize,
76        selected: &[usize],
77        pair_tok: &[usize],
78    ) -> Result<Self, String> {
79        if n_expert == 0 || n_tokens == 0 || selected.is_empty() {
80            return Err("grouped expert CSR requires non-empty experts, tokens, and pairs".into());
81        }
82        if n_expert > i32::MAX as usize
83            || n_tokens > i32::MAX as usize
84            || selected.len() > i32::MAX as usize
85        {
86            return Err("grouped expert CSR dimensions exceed the i32 kernel ABI".into());
87        }
88        if pair_tok.len() != selected.len() {
89            return Err(format!(
90                "grouped expert CSR pair rows {} != selected routes {}",
91                pair_tok.len(),
92                selected.len()
93            ));
94        }
95
96        let mut counts = vec![0usize; n_expert];
97        for &expert in selected {
98            let count = counts.get_mut(expert).ok_or_else(|| {
99                format!("grouped expert CSR expert {expert} outside 0..{n_expert}")
100            })?;
101            *count += 1;
102        }
103        if let Some(&token) = pair_tok.iter().find(|&&token| token >= n_tokens) {
104            return Err(format!(
105                "grouped expert CSR token {token} outside 0..{n_tokens}"
106            ));
107        }
108
109        let mut prefix = vec![0usize; n_expert + 1];
110        for expert in 0..n_expert {
111            prefix[expert + 1] = prefix[expert] + counts[expert];
112        }
113        let mut ex_ids = Vec::with_capacity(n_expert.min(selected.len()));
114        let mut ex_off = Vec::with_capacity(ex_ids.capacity() + 1);
115        for expert in 0..n_expert {
116            if counts[expert] != 0 {
117                ex_ids.push(expert as i32);
118                ex_off.push(prefix[expert] as i32);
119            }
120        }
121        ex_off.push(selected.len() as i32);
122
123        let mut cursor = prefix[..n_expert].to_vec();
124        let mut ex_pairs = vec![0i32; selected.len()];
125        for (pair, &expert) in selected.iter().enumerate() {
126            ex_pairs[cursor[expert]] = pair as i32;
127            cursor[expert] += 1;
128        }
129        let pair_tok = pair_tok.iter().map(|&token| token as i32).collect();
130        Self::from_parts(n_expert, n_tokens, ex_ids, ex_off, ex_pairs, pair_tok)
131    }
132
133    fn from_parts(
134        n_expert: usize,
135        n_tokens: usize,
136        ex_ids: Vec<i32>,
137        ex_off: Vec<i32>,
138        ex_pairs: Vec<i32>,
139        pair_tok: Vec<i32>,
140    ) -> Result<Self, String> {
141        if n_expert == 0
142            || n_tokens == 0
143            || ex_ids.is_empty()
144            || ex_pairs.is_empty()
145            || n_expert > i32::MAX as usize
146            || n_tokens > i32::MAX as usize
147            || ex_pairs.len() > i32::MAX as usize
148        {
149            return Err("grouped expert CSR requires non-empty experts, tokens, and pairs".into());
150        }
151        if ex_off.len() != ex_ids.len() + 1 || ex_off.first() != Some(&0) {
152            return Err(format!(
153                "grouped expert CSR offsets {} != active experts {} + 1 or do not start at zero",
154                ex_off.len(),
155                ex_ids.len()
156            ));
157        }
158        let n_pairs = i32::try_from(ex_pairs.len())
159            .map_err(|_| "grouped expert CSR pair count exceeds i32")?;
160        if pair_tok.len() != ex_pairs.len() || ex_off.last().copied() != Some(n_pairs) {
161            return Err(format!(
162                "grouped expert CSR pair lengths offsets_end={:?} pairs={} pair_tok={}",
163                ex_off.last(),
164                ex_pairs.len(),
165                pair_tok.len()
166            ));
167        }
168        for pair in ex_ids.windows(2) {
169            if pair[0] >= pair[1] {
170                return Err("grouped expert CSR expert ids must be strictly increasing".into());
171            }
172        }
173        if ex_ids
174            .iter()
175            .any(|&expert| expert < 0 || expert as usize >= n_expert)
176        {
177            return Err(format!(
178                "grouped expert CSR expert id outside 0..{n_expert}: {ex_ids:?}"
179            ));
180        }
181        let mut seen = vec![false; ex_pairs.len()];
182        for &pair in &ex_pairs {
183            if pair < 0 || pair as usize >= ex_pairs.len() {
184                return Err(format!(
185                    "grouped expert CSR pair {pair} outside 0..{}",
186                    ex_pairs.len()
187                ));
188            }
189            if std::mem::replace(&mut seen[pair as usize], true) {
190                return Err(format!(
191                    "grouped expert CSR pair {pair} appears more than once"
192                ));
193            }
194            let token = pair_tok[pair as usize];
195            if token < 0 || token as usize >= n_tokens {
196                return Err(format!(
197                    "grouped expert CSR token {token} outside 0..{n_tokens}"
198                ));
199            }
200        }
201        for offsets in ex_off.windows(2) {
202            if offsets[0] >= offsets[1] {
203                return Err("grouped expert CSR segments must be non-empty and increasing".into());
204            }
205        }
206        Ok(Self {
207            ex_ids,
208            ex_off,
209            ex_pairs,
210            pair_tok,
211            n_expert,
212            n_tokens,
213        })
214    }
215
216    pub fn upload(&self, engine: &Engine) -> Result<DeviceExpertCsr, Box<dyn std::error::Error>> {
217        Ok(DeviceExpertCsr {
218            ex_ids: engine.htod_i32(&self.ex_ids)?,
219            ex_off: engine.htod_i32(&self.ex_off)?,
220            ex_pairs: engine.htod_i32(&self.ex_pairs)?,
221            pair_tok: engine.htod_i32(&self.pair_tok)?,
222            n_expert: self.n_expert,
223            active_experts: self.ex_ids.len(),
224            n_tokens: self.n_tokens,
225            n_pairs: self.ex_pairs.len(),
226            max_tokens: self.n_tokens,
227            max_pairs: self.ex_pairs.len(),
228        })
229    }
230}
231
232pub struct DeviceExpertCsr {
233    ex_ids: CudaSlice<i32>,
234    ex_off: CudaSlice<i32>,
235    ex_pairs: CudaSlice<i32>,
236    pair_tok: CudaSlice<i32>,
237    n_expert: usize,
238    active_experts: usize,
239    n_tokens: usize,
240    n_pairs: usize,
241    max_tokens: usize,
242    max_pairs: usize,
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246struct DeviceExpertCsrCapacity {
247    n_expert: usize,
248    max_active_experts: usize,
249    max_tokens: usize,
250    max_pairs: usize,
251}
252
253fn validate_device_expert_csr_capacity(
254    n_expert: usize,
255    max_tokens: usize,
256    max_pairs: usize,
257) -> Result<DeviceExpertCsrCapacity, String> {
258    if n_expert == 0
259        || max_tokens == 0
260        || max_pairs == 0
261        || n_expert > i32::MAX as usize
262        || max_tokens > i32::MAX as usize
263        || max_pairs > i32::MAX as usize
264    {
265        return Err(format!(
266            "invalid device expert CSR capacity experts={n_expert} tokens={max_tokens} \
267             pairs={max_pairs}"
268        ));
269    }
270    Ok(DeviceExpertCsrCapacity {
271        n_expert,
272        max_active_experts: n_expert.min(max_pairs),
273        max_tokens,
274        max_pairs,
275    })
276}
277
278fn validate_device_expert_csr_refresh(
279    capacity: DeviceExpertCsrCapacity,
280    n_expert: usize,
281    active_experts: usize,
282    n_tokens: usize,
283    n_pairs: usize,
284) -> Result<(), String> {
285    if n_expert != capacity.n_expert {
286        return Err(format!(
287            "device expert CSR expert count changed {n_expert} != {}",
288            capacity.n_expert
289        ));
290    }
291    if active_experts == 0
292        || n_tokens == 0
293        || n_pairs == 0
294        || active_experts > capacity.max_active_experts
295        || n_tokens > capacity.max_tokens
296        || n_pairs > capacity.max_pairs
297    {
298        return Err(format!(
299            "device expert CSR active shape experts={active_experts} tokens={n_tokens} \
300             pairs={n_pairs} exceeds capacity experts={} tokens={} pairs={}",
301            capacity.max_active_experts, capacity.max_tokens, capacity.max_pairs
302        ));
303    }
304    Ok(())
305}
306
307impl DeviceExpertCsr {
308    /// Allocate stable device storage for schedules up to the supplied logical maxima.
309    ///
310    /// `refresh` fills prefixes of these buffers. The grouped kernel receives only the active
311    /// lengths, so route changes do not change any device pointer or allocate in the hot path.
312    pub fn with_capacity(
313        engine: &Engine,
314        n_expert: usize,
315        max_tokens: usize,
316        max_pairs: usize,
317    ) -> Result<Self, Box<dyn std::error::Error>> {
318        let capacity = validate_device_expert_csr_capacity(n_expert, max_tokens, max_pairs)?;
319        Ok(Self {
320            ex_ids: engine.htod_i32(&vec![0; capacity.max_active_experts])?,
321            ex_off: engine.htod_i32(&vec![0; capacity.max_active_experts + 1])?,
322            ex_pairs: engine.htod_i32(&vec![0; capacity.max_pairs])?,
323            pair_tok: engine.htod_i32(&vec![0; capacity.max_pairs])?,
324            n_expert,
325            active_experts: 0,
326            n_tokens: 0,
327            n_pairs: 0,
328            max_tokens,
329            max_pairs,
330        })
331    }
332
333    pub fn refresh(
334        &mut self,
335        engine: &Engine,
336        csr: &ExpertCsr,
337    ) -> Result<(), Box<dyn std::error::Error>> {
338        let capacity =
339            validate_device_expert_csr_capacity(self.n_expert, self.max_tokens, self.max_pairs)?;
340        validate_device_expert_csr_refresh(
341            capacity,
342            csr.n_expert,
343            csr.ex_ids.len(),
344            csr.n_tokens,
345            csr.ex_pairs.len(),
346        )?;
347        let device = engine.ctx().ordinal();
348        if self.ex_ids.ordinal() != device
349            || self.ex_off.ordinal() != device
350            || self.ex_pairs.ordinal() != device
351            || self.pair_tok.ordinal() != device
352        {
353            return Err(
354                format!("device expert CSR capacity is not resident on device {device}").into(),
355            );
356        }
357        engine.htod_i32_into(&mut self.ex_ids, &csr.ex_ids)?;
358        engine.htod_i32_into(&mut self.ex_off, &csr.ex_off)?;
359        engine.htod_i32_into(&mut self.ex_pairs, &csr.ex_pairs)?;
360        engine.htod_i32_into(&mut self.pair_tok, &csr.pair_tok)?;
361        self.active_experts = csr.ex_ids.len();
362        self.n_tokens = csr.n_tokens;
363        self.n_pairs = csr.ex_pairs.len();
364        Ok(())
365    }
366
367    pub fn clear(&mut self) {
368        self.active_experts = 0;
369        self.n_tokens = 0;
370        self.n_pairs = 0;
371    }
372}
373
374#[derive(Debug, Clone, Copy, PartialEq, Eq)]
375struct GroupedFp8WorkspaceShape {
376    activation_len: usize,
377    output_len: usize,
378}
379
380fn validate_grouped_fp8_workspace_shape(
381    in_features: usize,
382    out_features: usize,
383    n_tokens: usize,
384    n_pairs: usize,
385) -> Result<GroupedFp8WorkspaceShape, String> {
386    if in_features == 0
387        || out_features == 0
388        || n_tokens == 0
389        || n_pairs == 0
390        || !in_features.is_multiple_of(16)
391        || in_features > i32::MAX as usize
392        || out_features > i32::MAX as usize
393        || n_tokens > i32::MAX as usize
394        || n_pairs > i32::MAX as usize
395    {
396        return Err(format!(
397            "invalid grouped FP8 workspace in={in_features} out={out_features} \
398             tokens={n_tokens} pairs={n_pairs}"
399        ));
400    }
401    let activation_len = n_tokens
402        .checked_mul(in_features)
403        .ok_or("grouped FP8 activation length overflow")?;
404    let output_len = n_pairs
405        .checked_mul(out_features)
406        .ok_or("grouped FP8 output length overflow")?;
407    Ok(GroupedFp8WorkspaceShape {
408        activation_len,
409        output_len,
410    })
411}
412
413fn validate_grouped_fp8_workspace_active_shape(
414    in_features: usize,
415    out_features: usize,
416    max_tokens: usize,
417    max_pairs: usize,
418    n_tokens: usize,
419    n_pairs: usize,
420) -> Result<GroupedFp8WorkspaceShape, String> {
421    validate_grouped_fp8_workspace_shape(in_features, out_features, max_tokens, max_pairs)?;
422    let active =
423        validate_grouped_fp8_workspace_shape(in_features, out_features, n_tokens, n_pairs)?;
424    if n_tokens > max_tokens || n_pairs > max_pairs {
425        return Err(format!(
426            "grouped FP8 active shape tokens={n_tokens} pairs={n_pairs} exceeds capacity \
427             tokens={max_tokens} pairs={max_pairs}"
428        ));
429    }
430    Ok(active)
431}
432
433/// Caller-owned persistent buffers for grouped block-E4M3 projections.
434///
435/// Allocation and routing-plan upload happen outside the hot projection path. `quantize` and
436/// `project` overwrite their complete buffers and therefore introduce no per-call allocations.
437pub struct Fp8GroupedWorkspace {
438    act_scratch: CudaSlice<u8>,
439    output: CudaSlice<f32>,
440    in_features: usize,
441    out_features: usize,
442    n_tokens: usize,
443    n_pairs: usize,
444    max_tokens: usize,
445    max_pairs: usize,
446}
447
448impl Fp8GroupedWorkspace {
449    pub fn new(
450        engine: &Engine,
451        in_features: usize,
452        out_features: usize,
453        n_tokens: usize,
454        n_pairs: usize,
455    ) -> Result<Self, Box<dyn std::error::Error>> {
456        let shape =
457            validate_grouped_fp8_workspace_shape(in_features, out_features, n_tokens, n_pairs)?;
458        let act_bytes = unsafe { memra_mmq_fp8_blk_act_bytes(in_features as i32, n_tokens as i32) };
459        if act_bytes == 0 {
460            return Err("grouped FP8 activation scratch size is zero".into());
461        }
462        Ok(Self {
463            act_scratch: engine.alloc_u8_uninit(act_bytes)?,
464            output: engine.uninit(shape.output_len)?,
465            in_features,
466            out_features,
467            n_tokens,
468            n_pairs,
469            max_tokens: n_tokens,
470            max_pairs: n_pairs,
471        })
472    }
473
474    pub fn quantize(
475        &mut self,
476        engine: &Engine,
477        activations: &CudaSlice<f32>,
478    ) -> Result<(), Box<dyn std::error::Error>> {
479        self.quantize_for_shape(engine, activations, self.n_tokens, self.n_pairs)
480    }
481
482    /// Quantize an active prefix while retaining the workspace's stable capacity pointers.
483    pub fn quantize_for_shape(
484        &mut self,
485        engine: &Engine,
486        activations: &CudaSlice<f32>,
487        n_tokens: usize,
488        n_pairs: usize,
489    ) -> Result<(), Box<dyn std::error::Error>> {
490        let shape = validate_grouped_fp8_workspace_active_shape(
491            self.in_features,
492            self.out_features,
493            self.max_tokens,
494            self.max_pairs,
495            n_tokens,
496            n_pairs,
497        )?;
498        let device = engine.ctx().ordinal();
499        if activations.len() < shape.activation_len
500            || activations.ordinal() != device
501            || self.act_scratch.ordinal() != device
502        {
503            return Err(format!(
504                "grouped FP8 activation len/device {}/{} does not cover {}x{} on device {}",
505                activations.len(),
506                activations.ordinal(),
507                n_tokens,
508                self.in_features,
509                device,
510            )
511            .into());
512        }
513        let stream = engine.gpu.stream();
514        let (x_p, _gx) = activations.device_ptr(&stream);
515        let (scratch_p, _gs) = self.act_scratch.device_ptr_mut(&stream);
516        let rc = unsafe {
517            memra_mmq_fp8_blk_quantize_act(
518                x_p as *const f32,
519                scratch_p as *mut core::ffi::c_void,
520                self.in_features as i32,
521                n_tokens as i32,
522                stream.cu_stream() as *mut core::ffi::c_void,
523            )
524        };
525        if rc != 0 {
526            return Err(format!("memra_mmq_fp8_blk_quantize_act rc={rc}").into());
527        }
528        self.n_tokens = n_tokens;
529        self.n_pairs = n_pairs;
530        Ok(())
531    }
532
533    #[allow(clippy::too_many_arguments)]
534    pub fn project(
535        &mut self,
536        engine: &Engine,
537        bank_codes: &CudaSlice<u8>,
538        bank_scales: &CudaSlice<f32>,
539        csr: &DeviceExpertCsr,
540        code_stride: usize,
541        scale_stride: usize,
542        out_scale: f32,
543    ) -> Result<(), Box<dyn std::error::Error>> {
544        if csr.n_tokens != self.n_tokens || csr.n_pairs != self.n_pairs {
545            return Err(format!(
546                "grouped FP8 CSR/workspace mismatch tokens {} != {}, pairs {} != {}",
547                csr.n_tokens, self.n_tokens, csr.n_pairs, self.n_pairs
548            )
549            .into());
550        }
551        let want_code_stride = self
552            .in_features
553            .checked_mul(self.out_features)
554            .ok_or("grouped FP8 code stride overflow")?;
555        let want_scale_stride = self.in_features.div_ceil(128) * self.out_features.div_ceil(128);
556        if code_stride < want_code_stride || scale_stride < want_scale_stride {
557            return Err(format!(
558                "grouped FP8 expert strides codes {code_stride} < {want_code_stride}, \
559                 scales {scale_stride} < {want_scale_stride}"
560            )
561            .into());
562        }
563        let code_count = csr
564            .n_expert
565            .checked_mul(code_stride)
566            .ok_or("grouped FP8 expert code count overflow")?;
567        let scale_count = csr
568            .n_expert
569            .checked_mul(scale_stride)
570            .ok_or("grouped FP8 expert scale count overflow")?;
571        if bank_codes.len() < code_count || bank_scales.len() < scale_count {
572            return Err(format!(
573                "grouped FP8 expert bank too small codes {} < {}, scales {} < {}",
574                bank_codes.len(),
575                code_count,
576                bank_scales.len(),
577                scale_count,
578            )
579            .into());
580        }
581        if !out_scale.is_finite() {
582            return Err(format!("grouped FP8 output scale is not finite: {out_scale}").into());
583        }
584        let device = engine.ctx().ordinal();
585        if bank_codes.ordinal() != device
586            || bank_scales.ordinal() != device
587            || csr.ex_ids.ordinal() != device
588            || csr.ex_off.ordinal() != device
589            || csr.ex_pairs.ordinal() != device
590            || csr.pair_tok.ordinal() != device
591            || self.act_scratch.ordinal() != device
592            || self.output.ordinal() != device
593        {
594            return Err(format!(
595                "grouped FP8 bank, CSR, and workspace must all reside on device {device}"
596            )
597            .into());
598        }
599        let stream = engine.gpu.stream();
600        let (codes_p, _gc) = bank_codes.device_ptr(&stream);
601        let (scales_p, _gs) = bank_scales.device_ptr(&stream);
602        let (ids_p, _gi) = csr.ex_ids.device_ptr(&stream);
603        let (off_p, _go) = csr.ex_off.device_ptr(&stream);
604        let (pairs_p, _gp) = csr.ex_pairs.device_ptr(&stream);
605        let (tok_p, _gt) = csr.pair_tok.device_ptr(&stream);
606        let (act_p, _ga) = self.act_scratch.device_ptr(&stream);
607        let (output_p, _gy) = self.output.device_ptr_mut(&stream);
608        let rc = unsafe {
609            memra_mmq_fp8_blk_grouped(
610                codes_p as *const core::ffi::c_void,
611                scales_p as *const f32,
612                ids_p as *const i32,
613                off_p as *const i32,
614                pairs_p as *const i32,
615                tok_p as *const i32,
616                act_p as *const core::ffi::c_void,
617                output_p as *mut f32,
618                self.in_features as i32,
619                self.out_features as i32,
620                csr.n_expert as i32,
621                csr.active_experts as i32,
622                csr.n_pairs as i32,
623                csr.n_tokens as i32,
624                code_stride,
625                scale_stride,
626                stream.cu_stream() as *mut core::ffi::c_void,
627                out_scale,
628            )
629        };
630        if rc != 0 {
631            return Err(format!("memra_mmq_fp8_blk_grouped rc={rc}").into());
632        }
633        Ok(())
634    }
635
636    pub fn output(&self) -> &CudaSlice<f32> {
637        &self.output
638    }
639
640    pub fn output_len(&self) -> usize {
641        self.n_pairs * self.out_features
642    }
643}
644
645#[cfg(test)]
646mod grouped_fp8_tests {
647    use super::{
648        DeviceExpertCsrCapacity, ExpertCsr, GroupedFp8WorkspaceShape,
649        validate_device_expert_csr_capacity, validate_device_expert_csr_refresh,
650        validate_grouped_fp8_workspace_active_shape, validate_grouped_fp8_workspace_shape,
651    };
652
653    #[test]
654    fn token_routes_build_stable_expert_major_csr() {
655        let csr = ExpertCsr::from_token_routes(4, 2, 3, &[2, 0, 2, 1, 0, 3]).unwrap();
656        assert_eq!(csr.ex_ids, vec![0, 1, 2, 3]);
657        assert_eq!(csr.ex_off, vec![0, 2, 3, 5, 6]);
658        assert_eq!(csr.ex_pairs, vec![1, 4, 3, 0, 2, 5]);
659        assert_eq!(csr.pair_tok, vec![0, 0, 0, 1, 1, 1]);
660    }
661
662    #[test]
663    fn explicit_pair_rows_remain_indexed_by_pair_id() {
664        let csr = ExpertCsr::from_pair_rows(2, 3, &[1, 0, 1], &[2, 0, 1]).unwrap();
665        assert_eq!(csr.ex_ids, vec![0, 1]);
666        assert_eq!(csr.ex_off, vec![0, 1, 3]);
667        assert_eq!(csr.ex_pairs, vec![1, 0, 2]);
668        assert_eq!(csr.pair_tok, vec![2, 0, 1]);
669    }
670
671    #[test]
672    fn csr_validation_rejects_bad_routes_and_parts() {
673        assert!(ExpertCsr::from_token_routes(4, 2, 3, &[0, 1]).is_err());
674        assert!(ExpertCsr::from_pair_rows(2, 1, &[2], &[0]).is_err());
675        assert!(ExpertCsr::from_pair_rows(2, 1, &[0], &[1]).is_err());
676        assert!(
677            ExpertCsr::from_parts(2, 2, vec![0, 1], vec![0, 1, 2], vec![0, 0], vec![0, 1]).is_err()
678        );
679        assert!(
680            ExpertCsr::from_parts(2, 2, vec![1, 0], vec![0, 1, 2], vec![0, 1], vec![0, 1]).is_err()
681        );
682    }
683
684    #[test]
685    fn csr_segments_are_not_limited_to_one_kernel_tile() {
686        let selected = vec![0usize; 17];
687        let rows = (0..17).collect::<Vec<_>>();
688        let csr = ExpertCsr::from_pair_rows(1, 17, &selected, &rows).unwrap();
689        assert_eq!(csr.ex_off, vec![0, 17]);
690        assert_eq!(csr.ex_pairs, (0..17).collect::<Vec<i32>>());
691    }
692
693    #[test]
694    fn workspace_shape_validation_is_pure_and_checked() {
695        assert_eq!(
696            validate_grouped_fp8_workspace_shape(4096, 1280, 2, 16).unwrap(),
697            GroupedFp8WorkspaceShape {
698                activation_len: 8192,
699                output_len: 20480,
700            }
701        );
702        assert!(validate_grouped_fp8_workspace_shape(15, 128, 1, 1).is_err());
703        assert!(validate_grouped_fp8_workspace_shape(i32::MAX as usize + 1, 128, 1, 1,).is_err());
704    }
705
706    #[test]
707    fn device_csr_capacity_admits_smaller_dynamic_schedules() {
708        let capacity = validate_device_expert_csr_capacity(72, 8, 64).unwrap();
709        assert_eq!(
710            capacity,
711            DeviceExpertCsrCapacity {
712                n_expert: 72,
713                max_active_experts: 64,
714                max_tokens: 8,
715                max_pairs: 64,
716            }
717        );
718        validate_device_expert_csr_refresh(capacity, 72, 5, 3, 17).unwrap();
719        assert!(validate_device_expert_csr_refresh(capacity, 72, 5, 9, 17).is_err());
720        assert!(validate_device_expert_csr_refresh(capacity, 72, 5, 3, 65).is_err());
721        assert!(validate_device_expert_csr_refresh(capacity, 71, 5, 3, 17).is_err());
722        assert!(validate_device_expert_csr_refresh(capacity, 72, 0, 3, 17).is_err());
723    }
724
725    #[test]
726    fn grouped_workspace_capacity_accepts_only_bounded_active_shapes() {
727        assert_eq!(
728            validate_grouped_fp8_workspace_active_shape(4096, 1280, 8, 64, 3, 17).unwrap(),
729            GroupedFp8WorkspaceShape {
730                activation_len: 3 * 4096,
731                output_len: 17 * 1280,
732            }
733        );
734        assert!(validate_grouped_fp8_workspace_active_shape(4096, 1280, 8, 64, 9, 17).is_err());
735        assert!(validate_grouped_fp8_workspace_active_shape(4096, 1280, 8, 64, 3, 65).is_err());
736    }
737}
738
739unsafe extern "C" {
740    fn memra_bind_device(dev: i32) -> i32;
741    /// Bytes needed for the block_fp4_mmq activation scratch for (in_f, n_tokens).
742    pub fn memra_mmq_nvfp4_act_bytes(in_f: i32, n_tokens: i32) -> usize;
743    /// Run the NVFP4 W4A4 MMQ prefill GEMM. y[n_tokens, out_f] = act[n_tokens, in_f] @ W[out_f, in_f]^T.
744    ///   W_nvfp4_blocks : raw memra NVFP4 weight rows (block_nvfp4 36B blocks, in_f/64 per row).
745    ///   act_f32        : f32 activation [n_tokens, in_f] (contiguous).
746    ///   y              : f32 output [n_tokens, out_f].
747    ///   act_scratch    : pre-alloc'd quant buffer >= memra_mmq_nvfp4_act_bytes(in_f, n_tokens).
748    /// Returns 0 on success, else (1000 + cudaError).
749    pub fn memra_mmq_nvfp4(
750        w_nvfp4_blocks: *const core::ffi::c_void,
751        act_f32: *const f32,
752        y: *mut f32,
753        in_f: i32,
754        out_f: i32,
755        n_tokens: i32,
756        act_scratch: *mut core::ffi::c_void,
757        stream: *mut core::ffi::c_void,
758        out_scale: f32,
759    ) -> i32;
760    /// Same as `memra_mmq_nvfp4`, plus the activation-quantizer selector.
761    ///   per_token_scale = 1: two-level scaling (per-token row amax folded into the GEMM epilogue
762    ///     + per-sub-block UE4M3). This is what `memra_mmq_nvfp4` does.
763    ///   per_token_scale = 0: the v1 sub-block-only quantizer, retained as the numeric oracle so
764    ///     kernel-check can measure what the row scale bought, and as the rollback seam.
765    pub fn memra_mmq_nvfp4_ex(
766        w_nvfp4_blocks: *const core::ffi::c_void,
767        act_f32: *const f32,
768        y: *mut f32,
769        in_f: i32,
770        out_f: i32,
771        n_tokens: i32,
772        act_scratch: *mut core::ffi::c_void,
773        stream: *mut core::ffi::c_void,
774        out_scale: f32,
775        per_token_scale: i32,
776    ) -> i32;
777    /// Same as `memra_mmq_nvfp4_ex`, plus the residual high-precision channel count.
778    ///   residual_k = 0: off.
779    ///   residual_k > 0: the k largest-magnitude activation channels (ranked across the batch) are
780    ///     zeroed before quantization and their exact f32 contribution is added back as a rank-k
781    ///     correction. Requires per_token_scale = 1. Clamped to MMQ_MAX_RESIDUAL_K (64).
782    pub fn memra_mmq_nvfp4_ex2(
783        w_nvfp4_blocks: *const core::ffi::c_void,
784        act_f32: *const f32,
785        y: *mut f32,
786        in_f: i32,
787        out_f: i32,
788        n_tokens: i32,
789        act_scratch: *mut core::ffi::c_void,
790        stream: *mut core::ffi::c_void,
791        out_scale: f32,
792        per_token_scale: i32,
793        residual_k: i32,
794    ) -> i32;
795    /// Bytes needed for the block_q8_1_mmq activation scratch for the NVFP4 W4A8 path.
796    pub fn memra_mmq_nvfp4_w4a8_act_bytes(in_f: i32, n_tokens: i32) -> usize;
797    /// Run the NVFP4 W4A8 MMQ prefill GEMM (STAGE 2 accuracy-safe rung). Same fast MMQ tile as
798    /// memra_mmq_nvfp4 (W4A4) but the non-Blackwell int8 pair: weight FP4 LUT-dequantized to int8 at
799    /// tile-load, activation stays q8_1 int8 (D4, the same quant class as the default int8 GEMM).
800    /// `rp`: 0 = GGUF 36B-block weight layout, 1 = A6 split-plane repack (the resident decode
801    /// layout). The rp tile loader is a pure address remap of the GGUF loader (same dequant math,
802    /// same FP op order) — output is bit-identical either way.
803    /// Same contract as memra_mmq_nvfp4 otherwise. Returns 0 or (1000 + cudaError).
804    pub fn memra_mmq_nvfp4_w4a8(
805        w_nvfp4_blocks: *const core::ffi::c_void,
806        act_f32: *const f32,
807        y: *mut f32,
808        in_f: i32,
809        out_f: i32,
810        n_tokens: i32,
811        act_scratch: *mut core::ffi::c_void,
812        stream: *mut core::ffi::c_void,
813        out_scale: f32,
814        rp: i32,
815    ) -> i32;
816    /// Bytes for the block_e4m3_mmq activation scratch (footprint-identical to block_q8_1_mmq).
817    pub fn memra_mmq_nvfp4_f8f4_act_bytes(in_f: i32, n_tokens: i32) -> usize;
818    /// R-B W4A8-FP8 MMQ prefill GEMM (research/prefill-mxf8f6f4-design.md): NVFP4 per-16 scales
819    /// fold into e4m3 weight VALUES at tile load; e4m3 activations; ONE kind::f8f6f4 m16n8k32
820    /// MMA (381-TF class) where the int8 path issues two imma k16. NEW NUMERIC CONFIG — own
821    /// battery. Same contract/rp semantics as memra_mmq_nvfp4_w4a8. Returns 0 / 1000+cudaError /
822    /// 2000+cudaError.
823    pub fn memra_mmq_nvfp4_f8f4(
824        w_nvfp4_blocks: *const core::ffi::c_void,
825        act_f32: *const f32,
826        y: *mut f32,
827        in_f: i32,
828        out_f: i32,
829        n_tokens: i32,
830        act_scratch: *mut core::ffi::c_void,
831        stream: *mut core::ffi::c_void,
832        out_scale: f32,
833        rp: i32,
834    ) -> i32;
835    /// Bytes for the per-block FP8 MMQ activation scratch (delegates to the F8F4 sizing — the
836    /// two arms deliberately share ONE activation format, `block_e4m3_mmq`).
837    pub fn memra_mmq_fp8_blk_act_bytes(in_f: i32, n_tokens: i32) -> usize;
838    pub fn memra_mmq_fp8_blk_quantize_act(
839        act_f32: *const f32,
840        act_scratch: *mut core::ffi::c_void,
841        in_f: i32,
842        n_tokens: i32,
843        stream: *mut core::ffi::c_void,
844    ) -> i32;
845    pub fn memra_mmq_fp8_blk_grouped(
846        bank_codes: *const core::ffi::c_void,
847        bank_scales: *const f32,
848        ex_ids: *const i32,
849        ex_off: *const i32,
850        ex_pairs: *const i32,
851        pair_tok: *const i32,
852        act_scratch: *const core::ffi::c_void,
853        y: *mut f32,
854        in_f: i32,
855        out_f: i32,
856        n_expert: i32,
857        n_active: i32,
858        n_pairs: i32,
859        n_tokens: i32,
860        code_stride: usize,
861        scale_stride: usize,
862        stream: *mut core::ffi::c_void,
863        out_scale: f32,
864    ) -> i32;
865    /// Scale-grid dims for an [out_f x in_f] block-128 FP8 tensor (ceil-div by 128).
866    pub fn memra_mmq_fp8_blk_scale_rows(out_f: i32) -> i32;
867    pub fn memra_mmq_fp8_blk_scale_cols(in_f: i32) -> i32;
868    /// PER-BLOCK FP8 MMQ prefill GEMM (cu/mmq_fp8_blk.cu, P1 option (b)): consumes the
869    /// Qwen-official e4m3 weight bytes + the per-[128x128] f32 scale grid DIRECTLY. The weight
870    /// side is never re-quantized (the checkpoint bytes are the MMA A operand), so unlike ARM A's
871    /// per-tensor fold there is no precision loss; unlike ARM B' it does not land on the Q8_0
872    /// floor. `blk_scales` is device f32 [ceil(out_f/128) x ceil(in_f/128)], row-major.
873    /// Requires in_f % 16 == 0. Returns 0 / 1 (bad dims) / 1000+cudaError / 2000+cudaError.
874    pub fn memra_mmq_fp8_blk(
875        w_e4m3: *const core::ffi::c_void,
876        blk_scales: *const f32,
877        act_f32: *const f32,
878        y: *mut f32,
879        in_f: i32,
880        out_f: i32,
881        n_tokens: i32,
882        act_scratch: *mut core::ffi::c_void,
883        stream: *mut core::ffi::c_void,
884        out_scale: f32,
885    ) -> i32;
886    /// Count e4m3 NaN codes (magnitude 0x7F) in a device weight buffer. Those decode to NaN in
887    /// hardware but to 0.0 in the host/ARM B' convention, so a tensor containing any must NOT
888    /// ride `memra_mmq_fp8_blk`. `out_count` is a device u32 (zeroed by the call).
889    pub fn memra_fp8_blk_count_nan(
890        w_e4m3: *const core::ffi::c_void,
891        nbytes: usize,
892        out_count: *mut u32,
893        stream: *mut core::ffi::c_void,
894    ) -> i32;
895    /// Bytes needed for the block_q8_1_mmq activation scratch (shared by Q4_K and Q5_K).
896    pub fn memra_mmq_q45k_act_bytes(in_f: i32, n_tokens: i32) -> usize;
897    /// Run the Q4_K W4A8 MMQ prefill GEMM. Same contract as memra_mmq_nvfp4 (raw ggml block_q4_K
898    /// weight rows, in_f/256 144B superblocks per row). Returns 0 or (1000 + cudaError).
899    pub fn memra_mmq_q4_K(
900        w_q4k_blocks: *const core::ffi::c_void,
901        act_f32: *const f32,
902        y: *mut f32,
903        in_f: i32,
904        out_f: i32,
905        n_tokens: i32,
906        act_scratch: *mut core::ffi::c_void,
907        stream: *mut core::ffi::c_void,
908    ) -> i32;
909    /// Run the Q5_K W4A8 MMQ prefill GEMM (176B superblocks). Same contract as memra_mmq_q4_K.
910    pub fn memra_mmq_q5_K(
911        w_q5k_blocks: *const core::ffi::c_void,
912        act_f32: *const f32,
913        y: *mut f32,
914        in_f: i32,
915        out_f: i32,
916        n_tokens: i32,
917        act_scratch: *mut core::ffi::c_void,
918        stream: *mut core::ffi::c_void,
919    ) -> i32;
920
921    /// Bytes needed for the block_q8_1_mmq (D4) activation scratch for the Q8_0 MMQ path.
922    pub fn memra_mmq_q8_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
923    /// Run the Q8_0 int8-MMA MMQ prefill GEMM (MEMRA_PP_Q8MMQ). Conventional xy-tiling only (no fixup
924    /// scratch). Weight = raw ggml block_q8_0 rows (34B blocks, in_f/32 per row); activation is
925    /// quantized internally to q8_1 D4. Requires in_f % 32 == 0. Returns 0 or (1000 + cudaError).
926    pub fn memra_mmq_q8_0(
927        w_q8_0_blocks: *const core::ffi::c_void,
928        act_f32: *const f32,
929        y: *mut f32,
930        in_f: i32,
931        out_f: i32,
932        n_tokens: i32,
933        act_scratch: *mut core::ffi::c_void,
934        stream: *mut core::ffi::c_void,
935    ) -> i32;
936
937    // ---- Q1 accumulator instrument (cu/mmq_q8_0_f32acc.cu, lane/fp8-v3-gate) ----
938    // The Q8_0 MMQ floor's GEMM with the accumulator as its ONE free variable: arm S32 is the
939    // floor's `mma...s32.s8.s8.s32`, arm F32 is the same m16n8k32 shape and the same A/B/D fragment
940    // ABI with `mma...kind::f8f6f4...f32.e4m3.e4m3.f32` — the op cu/mmq_fp8_blk.cu accumulates in.
941    // Both take a PRE-QUANTIZED block_q8_1_mmq activation buffer, so the measurement is GEMM-only
942    // and cannot differ by a quantizer. Research instrument only: no dispatch seam, and neither arm's
943    // output is a numeric claim (see the TU header).
944    /// Activation-scratch bytes for the accumulator instrument (same padding rule as the floor).
945    pub fn memra_accprobe_act_bytes(in_f: i32, n_tokens: i32) -> usize;
946    /// ARM S32 — the floor's GEMM verbatim, s32 accumulate. Returns 0, 1, or 1000+cudaError.
947    pub fn memra_accprobe_gemm_s32(
948        w_q8_0_blocks: *const core::ffi::c_void,
949        act_q: *const core::ffi::c_void,
950        y: *mut f32,
951        in_f: i32,
952        out_f: i32,
953        n_tokens: i32,
954        stream: *mut core::ffi::c_void,
955    ) -> i32;
956    /// ARM F32 — byte-identical kernel, f32 accumulate over the e4m3 reading of the same bytes.
957    pub fn memra_accprobe_gemm_f32(
958        w_q8_0_blocks: *const core::ffi::c_void,
959        act_q: *const core::ffi::c_void,
960        y: *mut f32,
961        in_f: i32,
962        out_f: i32,
963        n_tokens: i32,
964        stream: *mut core::ffi::c_void,
965    ) -> i32;
966
967    /// Bytes needed for the block_q8_1_mmq (D4) activation scratch for the Q4_0 MMQ path.
968    pub fn memra_mmq_q4_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
969    /// Run the Q4_0 int8-MMA MMQ prefill GEMM (MEMRA_PP_Q4MMQ). Nibbles dequant to int8 at
970    /// tile-load (the -8 zero-point folds into the quants, D4 epilogue — same accuracy class as
971    /// the Q8_0 MMQ). `rp`: 0 = raw ggml 18B blocks, 1 = MEMRA_Q4RP split-plane repack (qs plane +
972    /// fp16 d plane) — pure address remap, bit-identical output either way. Requires
973    /// in_f % 32 == 0. Returns 0 or (1000 + cudaError).
974    pub fn memra_mmq_q4_0(
975        w_q4_0: *const core::ffi::c_void,
976        act_f32: *const f32,
977        y: *mut f32,
978        in_f: i32,
979        out_f: i32,
980        n_tokens: i32,
981        act_scratch: *mut core::ffi::c_void,
982        stream: *mut core::ffi::c_void,
983        rp: i32,
984    ) -> i32;
985    /// Quantize-only entry (quantize-once seam): f32 activation -> block_q8_1_mmq scratch.
986    pub fn memra_mmq_q4_0_quant_act(
987        act_f32: *const f32,
988        act_scratch: *mut core::ffi::c_void,
989        in_f: i32,
990        n_tokens: i32,
991        stream: *mut core::ffi::c_void,
992    ) -> i32;
993    /// GEMM-only entry: consumes a pre-quantized scratch (from memra_mmq_q4_0_quant_act).
994    pub fn memra_mmq_q4_0_gemm(
995        w_q4_0: *const core::ffi::c_void,
996        act_scratch: *const core::ffi::c_void,
997        y: *mut f32,
998        in_f: i32,
999        out_f: i32,
1000        n_tokens: i32,
1001        stream: *mut core::ffi::c_void,
1002        rp: i32,
1003    ) -> i32;
1004    /// Stream-k fixup scratch bytes (one [MMQ_X x MMQ_Y] f32 slot per SM).
1005    pub fn memra_mmq_q4_0_fixup_bytes() -> usize;
1006    /// Stream-k GEMM entry: deterministic form selection, with the SK form itself
1007    /// falling back to tiling when wave efficiency is at least 90%.
1008    pub fn memra_mmq_q4_0_gemm_sk(
1009        w_q4_0: *const core::ffi::c_void,
1010        act_scratch: *const core::ffi::c_void,
1011        y: *mut f32,
1012        fixup_scratch: *mut core::ffi::c_void,
1013        in_f: i32,
1014        out_f: i32,
1015        n_tokens: i32,
1016        stream: *mut core::ffi::c_void,
1017        rp: i32,
1018    ) -> i32;
1019
1020    // ---- IQ3_S / IQ4_XS expert-segmented int8-MMA MMQ (cu/mmq_iq_experts.cu, MEMRA_MOE_MMA) ----
1021    /// Bytes for the token-major block_q8_1_mmq activation scratch (in_f, n_tokens).
1022    pub fn memra_mmq_iq_experts_act_bytes(in_f: i32, n_tokens: i32) -> usize;
1023    /// Quantize token-major f32 activation [n_tokens, in_f] -> block_q8_1_mmq (D4). Returns 0 or 1000+err.
1024    pub fn memra_mmq_iq_quantize_act(
1025        act_f32: *const f32,
1026        act_scratch: *mut core::ffi::c_void,
1027        in_f: i32,
1028        n_tokens: i32,
1029        stream: *mut core::ffi::c_void,
1030    ) -> i32;
1031    /// Fused act-epilogue: silu/gelu(gate)*up + q8_1_mmq (D4) quantize in ONE launch — no f32 act
1032    /// buffer. gate/up pair-major [n_tokens, in_f]; scratch identical to memra_mmq_iq_quantize_act.
1033    /// act_kind: 0=silu*mul, 1=gelu_tanh*mul. Byte-identical to the two-pass path (kernel-check gated).
1034    pub fn memra_mmq_iq_fused_act_quant(
1035        gate: *const f32,
1036        up: *const f32,
1037        act_scratch: *mut core::ffi::c_void,
1038        in_f: i32,
1039        n_tokens: i32,
1040        act_kind: i32,
1041        stream: *mut core::ffi::c_void,
1042    ) -> i32;
1043    /// Expert-segmented IQ MMA MMQ. Same CSR shape as moe_pairs_matvec_q8_dec: `table` = [3,n_expert]
1044    /// device slab ptrs, CSR ex_ids/ex_off/ex_pairs group pairs by expert, pair_tok gathers the
1045    /// activation row. y = [n_pairs, out_f] pair-major. `act_scratch` pre-quantized over n_tokens.
1046    /// qtype: 5=IQ4_XS, 6=IQ3_S. Returns 0 or 1000+cudaError.
1047    /// Dense-trunk IQ4_XS MMQ (lane/kquant-tile-loaders): the dense analog of the expert
1048    /// kernel for non-expert IQ4_XS 2-D matmuls (the KAT-Coder trunk class). Quantizes the
1049    /// f32 activation to D4 q8_1_mmq internally; `act_scratch` sized by
1050    /// `memra_mmq_iq_experts_act_bytes`. Requires in_f % 256 == 0.
1051    pub fn memra_mmq_iq4xs_dense(
1052        w_blocks: *const core::ffi::c_void,
1053        act_f32: *const f32,
1054        y: *mut f32,
1055        in_f: i32,
1056        out_f: i32,
1057        n_tokens: i32,
1058        row_bytes: i64,
1059        act_scratch: *mut core::ffi::c_void,
1060        stream: *mut core::ffi::c_void,
1061    ) -> i32;
1062    pub fn memra_mmq_iq_experts(
1063        table: *const u64,
1064        proj: i32,
1065        n_expert: i32,
1066        ex_ids: *const i32,
1067        ex_off: *const i32,
1068        ex_pairs: *const i32,
1069        pair_tok: *const i32,
1070        act_scratch: *const core::ffi::c_void,
1071        y: *mut f32,
1072        in_f: i32,
1073        out_f: i32,
1074        n_active: i32,
1075        n_tokens: i32,
1076        qtype: i32,
1077        row_bytes: i64,
1078        stream: *mut core::ffi::c_void,
1079    ) -> i32;
1080
1081    // ---- MoE grouped f16 GEMM (cu/moe_f16_grouped.cu, round 46 arc 2) ----
1082    pub fn memra_moe_f16g_dequant(
1083        table: *const u64,
1084        proj: i32,
1085        n_expert: i32,
1086        ex_ids: *const i32,
1087        w_f16: *mut core::ffi::c_void,
1088        in_f: i32,
1089        out_f: i32,
1090        n_active: i32,
1091        qtype: i32,
1092        row_bytes: i64,
1093        stream: *mut core::ffi::c_void,
1094    ) -> i32;
1095    pub fn memra_moe_f16g_gather_act(
1096        x: *const f32,
1097        pair_tok_or_null: *const i32,
1098        act_f16: *mut core::ffi::c_void,
1099        row_scale: *mut f32,
1100        in_f: i32,
1101        n_pairs: i32,
1102        stream: *mut core::ffi::c_void,
1103    ) -> i32;
1104    pub fn memra_moe_f16g_h2f_scaled(
1105        src_f16: *const core::ffi::c_void,
1106        dst: *mut f32,
1107        row_scale: *const f32,
1108        ncols: i32,
1109        nrows: i32,
1110        stream: *mut core::ffi::c_void,
1111    ) -> i32;
1112    pub fn memra_moe_f16g_gemm(
1113        w_f16: *const core::ffi::c_void,
1114        act_f16: *const core::ffi::c_void,
1115        y_f16: *mut core::ffi::c_void,
1116        ex_off_host: *const i32,
1117        n_active: i32,
1118        in_f: i32,
1119        out_f: i32,
1120        stream: *mut core::ffi::c_void,
1121    ) -> i32;
1122    pub fn memra_moe_f16g_h2f(
1123        src_f16: *const core::ffi::c_void,
1124        dst: *mut f32,
1125        n: usize,
1126        stream: *mut core::ffi::c_void,
1127    ) -> i32;
1128    // Single-kernel grouped GEMM (MEMRA_MOE_F16G=2, rounds 49+51): on OUR stream, f32 C with
1129    // the act row-scale folded in — no cublas internal-stream race, no sync. Round 51 runs it
1130    // as a persistent problem-visitor over the real tiles with two tile forms (32x64 tail
1131    // / 128x64x64 3-stage): shape_sel < 0 = the round-49 grid-scan kernel (rollback
1132    // arm); else groups with m_e >= cross ride the 128 form. ex_off_host sizes the visitor
1133    // grids host-side (the offsets are already there at the call site — no extra transfer).
1134    // tail != 0 (lane/sk-tail-form): sub-cross groups ride the DEEP tail (32x64x64 3-stage);
1135    // 0 = the round-51 2-stage 32x64x32 (MEMRA_F16G_TAIL=0 rollback). Byte-identical arms.
1136    pub fn memra_moe_f16g_gemm_sk(
1137        w_f16: *const core::ffi::c_void,
1138        act_f16: *const core::ffi::c_void,
1139        y_f32: *mut f32,
1140        row_scale: *const f32,
1141        ex_off_dev: *const i32,
1142        ex_off_host: *const i32,
1143        n_active: i32,
1144        max_m: i32,
1145        in_f: i32,
1146        out_f: i32,
1147        shape_sel: i32,
1148        cross: i32,
1149        tail: i32,
1150        stream: *mut core::ffi::c_void,
1151    ) -> i32;
1152    // DIRECT-FROM-QUANT sk visitor grouped GEMM (lane/kquant-tile-loaders + iq-direct-loaders):
1153    // the visitor forms with the B (weight) tiles dequanted in-register from the expert
1154    // superblocks — no f16 dequant workspace pass. Bit-identical to the workspace path by
1155    // construction (kernel-check "f16g-kq-direct"). qtype: QT_Q4_K | QT_Q6_K | QT_IQ4_XS |
1156    // QT_IQ3_S; rc=2 = not admitted here (caller keeps the dequant-workspace path).
1157    // tail: as memra_moe_f16g_gemm_sk.
1158    pub fn memra_moe_kq_gemm_sk(
1159        table: *const u64,
1160        proj: i32,
1161        n_expert: i32,
1162        ex_ids: *const i32,
1163        act_f16: *const core::ffi::c_void,
1164        y_f32: *mut f32,
1165        row_scale: *const f32,
1166        ex_off_dev: *const i32,
1167        ex_off_host: *const i32,
1168        n_active: i32,
1169        max_m: i32,
1170        in_f: i32,
1171        out_f: i32,
1172        qtype: i32,
1173        cross: i32,
1174        tail: i32,
1175        row_bytes: i64,
1176        stream: *mut core::ffi::c_void,
1177    ) -> i32;
1178}
1179
1180/// W4A8-MMQ DEFAULT-FLIP seam (2026-07-05): the vendored MMQ prefill suite is DEFAULT-ON — NVFP4
1181/// takes the W4A8 MMQ tile (same int8 accuracy class as the int8 GEMM it replaces, all exactness
1182/// gates hold, ~1.9x pp512; the rp tile-loader arm coexists with the A6 split-plane repack) and
1183/// Q4_K/Q5_K take the vendored k-quant int8-MMA MMQ (also int8-class; gated with W4A8 in the same
1184/// battery — the predecessor's `MEMRA_MMQ_W4A8=1` arm engaged BOTH, this flip preserves exactly
1185/// that measured config). `MEMRA_MMQ_W4A8=0` = escape hatch back to the int8 GEMM prefill
1186/// everywhere. `MEMRA_MMQ=1` additionally switches GGUF-layout NVFP4 to the W4A4 mxf4nvf4 tile
1187/// (speed/accuracy tradeoff opt-in, unchanged).
1188pub fn mmq_w4a8_enabled() -> bool {
1189    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1190    *ON.get_or_init(|| {
1191        std::env::var("MEMRA_MMQ_W4A8")
1192            .map(|v| v != "0")
1193            .unwrap_or(true)
1194    })
1195}
1196
1197/// Residual high-precision activation channels for the W4A4 MMQ prefill path.
1198/// `MEMRA_MMQ_RESIDUAL_K=<k>` keeps the k largest-magnitude activation channels out of the e2m1
1199/// quantized path and adds their exact f32 contribution back as a rank-k correction. k=0 (default)
1200/// is off; the kernel clamps to MMQ_MAX_RESIDUAL_K (64).
1201///
1202/// Read LIVE per call, not OnceLock'd, for the same reason `MEMRA_MMQ` is: the W4A4 exactness gate
1203/// sweeps arms inside ONE process against ONE set of loaded weights, and a cached first read would
1204/// pin every later arm to whatever the first one saw.
1205pub fn mmq_residual_k() -> i32 {
1206    std::env::var("MEMRA_MMQ_RESIDUAL_K")
1207        .ok()
1208        .and_then(|v| v.parse::<i32>().ok())
1209        .unwrap_or(0)
1210        .clamp(0, 64)
1211}
1212
1213/// Q8_0 MMQ prefill seam (lane/ppmmq lever 2, DEFAULT ON since 2026-07-09 — `MEMRA_PP_Q8MMQ=0`
1214/// reverts): routes Q8_0 dense
1215/// projections (m>=16) through the vendored int8-MMA MMQ (cu/mmq_q8_0.cu) instead of the hand-rolled
1216/// `qmatvec_gemm_q8_0` tiling GEMM. Its own numeric config (MMA f32 reduction order != the tiling
1217/// GEMM's) — gated with the full exactness battery. Default OFF until the battery is green.
1218pub fn mmq_q8_enabled() -> bool {
1219    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1220    // Promotion battery (2026-07-09): argmax MATCH on 35B p1/p2/p3 + 9B p2/p3 (p4-16k OOMs
1221    // identically with and without the flag — pre-existing gate capacity limit, not this seam);
1222    // kernel-check ALL GREEN; run-spec K=1..8 PASS on 9B+35B. 35B pp 2456->3069 free-clock.
1223    *ON.get_or_init(|| {
1224        std::env::var("MEMRA_PP_Q8MMQ")
1225            .map(|v| v != "0")
1226            .unwrap_or(true)
1227    })
1228}
1229
1230/// IQ4_XS dense-trunk MMQ prefill seam (lane/kquant-tile-loaders, 2026-08-02): routes
1231/// NON-expert IQ4_XS 2-D projections (m>=16) through the vendored-machinery int8-MMA dense
1232/// MMQ (cu/mmq_iq_experts.cu `mmq_iq4xs_dense_kernel`) instead of the per-column dp4a grid
1233/// — the KAT-Coder prefill wall (0.169x vs llama; zero weight reuse across tokens,
1234/// research/kat-anomaly-20260802 §6). Its own numeric config (MMA reduction order) — gated
1235/// with the full exactness battery. m=1..15 decode/verify keep dp4a (dispatch parity).
1236/// `MEMRA_PP_IQMMQ=0` reverts.
1237pub fn mmq_iq4xs_enabled() -> bool {
1238    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1239    *ON.get_or_init(|| {
1240        std::env::var("MEMRA_PP_IQMMQ")
1241            .map(|v| v != "0")
1242            .unwrap_or(true)
1243    })
1244}
1245
1246/// Q4_0 MMQ prefill seam (gemma-4-12B lane, 2026-07-22): routes Q4_0 dense projections (m>=16)
1247/// through the vendored int8-MMA MMQ (cu/mmq_q4_0.cu) instead of the hand-rolled
1248/// `qmatvec_gemm_q4_0[_rp]` tiling GEMM (measured 77% of the 12B prime pass). Its own numeric
1249/// config (MMA f32 reduction order != the tiling GEMM's) — gated with the full exactness battery
1250/// before default-flip; `MEMRA_PP_Q4MMQ=0` reverts.
1251pub fn mmq_q4_enabled() -> bool {
1252    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1253    *ON.get_or_init(|| {
1254        std::env::var("MEMRA_PP_Q4MMQ")
1255            .map(|v| v != "0")
1256            .unwrap_or(true)
1257    })
1258}
1259
1260fn nvfp4_use_w4a8(rp: bool, w4a8_explicit: bool, w4a8_default: bool, mmq_explicit: bool) -> bool {
1261    // Split-plane weights have no W4A4 loader. Otherwise an explicit W4A8 request wins, then the
1262    // default applies only while MEMRA_MMQ is absent (MEMRA_MMQ=1 selects W4A4).
1263    rp || w4a8_explicit || (w4a8_default && !mmq_explicit)
1264}
1265
1266#[cfg(test)]
1267mod b200_dry_policy_tests {
1268    use super::nvfp4_use_w4a8;
1269
1270    #[test]
1271    fn nvfp4_default_and_explicit_routes_do_not_reach_sm100_stubs() {
1272        assert!(nvfp4_use_w4a8(false, false, true, false));
1273        assert!(!nvfp4_use_w4a8(false, false, true, true));
1274        assert!(nvfp4_use_w4a8(false, true, true, true));
1275        assert!(nvfp4_use_w4a8(true, false, false, true));
1276        assert!(!nvfp4_use_w4a8(false, false, false, false));
1277    }
1278}
1279
1280impl Engine {
1281    /// True if `w` should take a vendored MMQ GEMM under the current env policy (see
1282    /// `mmq_w4a8_enabled`): NVFP4 needs in_f % 64 == 0, Q4_K/Q5_K need in_f % 256 == 0.
1283    pub fn mmq_supports(&self, w: &crate::model::GpuTensor) -> bool {
1284        use crate::model::GpuTensor;
1285        if crate::portable_mma_gated() {
1286            return false;
1287        }
1288        let mmq_opt_in = std::env::var("MEMRA_MMQ").is_ok();
1289        match w {
1290            // A6 split-plane repacked NVFP4: ONLY the W4A8 loader has an rp arm (pure address
1291            // remap, bit-identical output — mmq_nvfp4_w4a8.cu load_tiles_nvfp4_w4a8<is_rp>).
1292            // The W4A4 loader (mmq_fp4.cu load_tiles_nvfp4_nvfp4) reads 36B GGUF blocks only,
1293            // so an rp weight with W4A8 disabled falls through to the rp-ported int8 GEMM.
1294            // The split-plane layout has only a W4A8 loader. Its int8 MMA is native on sm_100a;
1295            // optional F8F4 uses the existing bit-identical plain-E4M3 rollback form there.
1296            GpuTensor::Quant { qtype, rp, .. } if *qtype == crate::QT_NVFP4 && *rp => {
1297                !cfg!(memra_portable_cuda)
1298                    && mmq_w4a8_enabled()
1299                    && w.in_features().is_multiple_of(64)
1300            }
1301            // GGUF-layout NVFP4: W4A8 stays the accuracy-safe default on both Blackwell families.
1302            // The new sm_100a tcgen05 W4A4 twin stays behind the EXISTING MEMRA_MMQ=1 opt-in until
1303            // real-B200 exactness and serving gates exist; unmeasured hardware behavior never
1304            // defaults on.
1305            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4 => {
1306                !cfg!(memra_portable_cuda)
1307                    && (mmq_w4a8_enabled() || mmq_opt_in)
1308                    && w.in_features().is_multiple_of(64)
1309            }
1310            GpuTensor::Quant { qtype, .. }
1311                if *qtype == crate::QT_Q4_K || *qtype == crate::QT_Q5_K =>
1312            {
1313                (mmq_w4a8_enabled() || mmq_opt_in) && w.in_features().is_multiple_of(256)
1314            }
1315            // Q8_0 dense projections (35B attn/ssm/shexp): opt-in only (MEMRA_PP_Q8MMQ=1), its own
1316            // numeric config vs qmatvec_gemm_q8_0. in_f % 256 == 0: MMQ_ITER_K=256 loads 8-block
1317            // groups, so a non-multiple row would read a garbage weight tail (fp16 d bytes can be
1318            // NaN-pattern, and NaN * 0-padded-activation = NaN — the 26B ffn_down lesson).
1319            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q8_0 => {
1320                mmq_q8_enabled() && w.in_features().is_multiple_of(256)
1321            }
1322            // Q4_0 dense projections (gemma QAT ggufs): MEMRA_PP_Q4MMQ seam. Both weight layouts
1323            // (raw 18B blocks and the MEMRA_Q4RP split-plane repack) have loader arms. Same
1324            // in_f % 256 == 0 tail rule as Q8_0 (26B ffn_down in_f=2112 NaN'd on the %32 gate);
1325            // non-multiples fall back to the hand-rolled qmatvec_gemm_q4_0[_rp].
1326            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q4_0 => {
1327                mmq_q4_enabled() && w.in_features().is_multiple_of(256)
1328            }
1329            // IQ4_XS dense projections (KAT-Coder trunk): m>=16 prefill only — decode and
1330            // spec-verify (m<16) keep the qmatvec_iq4_XS_dp4a per-column program (the
1331            // kat-anomaly dispatch-parity law). Requires the dp4a fast path itself enabled:
1332            // MEMRA_IQ_FAST=0 (the Stage-A oracle rollback) must also kill this arm so the
1333            // rollback stays a full-path seam. in_f % 256: MMQ_ITER_K walks whole superblocks.
1334            GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_IQ4_XS => {
1335                mmq_iq4xs_enabled()
1336                    && Self::iq_fast_enabled()
1337                    && w.in_features().is_multiple_of(256)
1338            }
1339            _ => false,
1340        }
1341    }
1342
1343    /// Unified vendored-MMQ dispatch: routes to the NVFP4 or Q4_K/Q5_K launcher by qtype.
1344    /// Caller MUST have checked `mmq_supports(w)`. `x` is the RAW f32 activation.
1345    pub fn qmatvec_mmq(
1346        &self,
1347        w: &crate::model::GpuTensor,
1348        x: &CudaSlice<f32>,
1349        m: usize,
1350    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1351        use crate::model::GpuTensor;
1352        let (in_f, out_f) = (w.in_features(), w.out_features());
1353        let GpuTensor::Quant {
1354            bytes,
1355            scale,
1356            qtype,
1357            rp,
1358            ..
1359        } = w
1360        else {
1361            return Err("qmatvec_mmq: not a Quant tensor".into());
1362        };
1363        // NVFP4 tile choice: W4A8 (accuracy-safe int8 pair, DEFAULT since the flip) vs W4A4
1364        // (mxf4nvf4 mma, explicit MEMRA_MMQ=1 speed/accuracy tradeoff). An rp weight ALWAYS takes
1365        // W4A8 — only its loader has the split-plane arm (pure address remap, bit-identical).
1366        // Explicit MEMRA_MMQ_W4A8=1 still overrides a simultaneous MEMRA_MMQ=1 (predecessor rule).
1367        let w4a8_explicit = std::env::var("MEMRA_MMQ_W4A8")
1368            .map(|v| v != "0")
1369            .unwrap_or(false);
1370        let use_w4a8 = nvfp4_use_w4a8(
1371            *rp,
1372            w4a8_explicit,
1373            mmq_w4a8_enabled(),
1374            std::env::var("MEMRA_MMQ").is_ok(),
1375        );
1376        match *qtype {
1377            // STAGE 2: the accuracy-safe int8 W4A8 MMQ tile (weight FP4->int8 dequant + q8_1
1378            // activation) — handles BOTH weight layouts (rp = A6 split-plane vs GGUF blocks).
1379            q if q == crate::QT_NVFP4 && use_w4a8 => {
1380                self.qmatvec_mmq_nvfp4_w4a8(bytes, x, m, in_f, out_f, *scale, *rp)
1381            }
1382            q if q == crate::QT_NVFP4 => self.qmatvec_mmq_nvfp4(bytes, x, m, in_f, out_f, *scale),
1383            q if q == crate::QT_Q4_K || q == crate::QT_Q5_K => {
1384                let mut y = self.qmatvec_mmq_q45k_raw(bytes, x, m, in_f, out_f, q)?;
1385                if *scale != 1.0 {
1386                    self.scale_inplace(&mut y, *scale, m * out_f)?;
1387                }
1388                Ok(y)
1389            }
1390            q if q == crate::QT_Q8_0 => {
1391                // wgmma arm (sm_90a, task 8): OPT-IN via MEMRA_WGMMA=1 — v0 measured 3845
1392                // vs MMQ 8692 tok/s pp512 (2026-07-26 N=5), so MMQ stays the default until
1393                // the pipelined wgmma wins. Reads the rp4 split-plane mirror + the engine's
1394                // q8_1 activation planes. Same numeric class as MMQ (exact s32 per 32-block,
1395                // one f32 fold per block, ascending K) — kernel-check tolerance-gated.
1396                if cfg!(memra_hopper_mma)
1397                    && out_f % 64 == 0
1398                    && crate::wgmma_gemm_enabled()
1399                    && let GpuTensor::Quant { rp4: Some(m4), .. } = w
1400                {
1401                    let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
1402                    let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, &aq, &ad, m, in_f, out_f)?;
1403                    if *scale != 1.0 {
1404                        self.scale_inplace(&mut y, *scale, m * out_f)?;
1405                    }
1406                    return Ok(y);
1407                }
1408                let mut y = self.qmatvec_mmq_q8_0_raw(bytes, x, m, in_f, out_f)?;
1409                if *scale != 1.0 {
1410                    self.scale_inplace(&mut y, *scale, m * out_f)?;
1411                }
1412                Ok(y)
1413            }
1414            q if q == crate::QT_Q4_0 => {
1415                let mut y = self.qmatvec_mmq_q4_0_raw(bytes, x, m, in_f, out_f, *rp)?;
1416                if *scale != 1.0 {
1417                    self.scale_inplace(&mut y, *scale, m * out_f)?;
1418                }
1419                Ok(y)
1420            }
1421            q if q == crate::QT_IQ4_XS => {
1422                let GpuTensor::Quant { row_bytes, .. } = w else {
1423                    unreachable!()
1424                };
1425                let mut y = self.qmatvec_mmq_iq4xs_raw(bytes, x, m, in_f, out_f, *row_bytes)?;
1426                if *scale != 1.0 {
1427                    self.scale_inplace(&mut y, *scale, m * out_f)?;
1428                }
1429                Ok(y)
1430            }
1431            q => Err(format!("qmatvec_mmq: unsupported qtype {q}").into()),
1432        }
1433    }
1434
1435    /// Bare IQ4_XS dense MMQ launch (no macro-scale) — also the kernel_check gate entry.
1436    pub fn qmatvec_mmq_iq4xs_raw(
1437        &self,
1438        bytes: &CudaSlice<u8>,
1439        x: &CudaSlice<f32>,
1440        m: usize,
1441        in_f: usize,
1442        out_f: usize,
1443        row_bytes: usize,
1444    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1445        assert!(
1446            in_f.is_multiple_of(256),
1447            "MMQ IQ4_XS requires in_f % 256 == 0, got {in_f}"
1448        );
1449        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, m as i32) };
1450        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1451        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1452        {
1453            let stream = self.gpu.stream();
1454            let (w_p, _gw) = bytes.device_ptr(&stream);
1455            let (x_p, _gx) = x.device_ptr(&stream);
1456            let (y_p, _gy) = y.device_ptr_mut(&stream);
1457            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1458            let rc = unsafe {
1459                memra_mmq_iq4xs_dense(
1460                    w_p as *const core::ffi::c_void,
1461                    x_p as *const f32,
1462                    y_p as *mut f32,
1463                    in_f as i32,
1464                    out_f as i32,
1465                    m as i32,
1466                    row_bytes as i64,
1467                    s_p as *mut core::ffi::c_void,
1468                    stream.cu_stream() as *mut core::ffi::c_void,
1469                )
1470            };
1471            if rc != 0 {
1472                return Err(format!("memra_mmq_iq4xs_dense rc={rc}").into());
1473            }
1474        }
1475        Ok(y)
1476    }
1477
1478    /// Bare Q4_K/Q5_K MMQ launch (no macro-scale) — also the kernel_check accuracy-gate entry.
1479    /// Conventional xy-tiling only (the vendored stream-K arm — MEMRA_MMQ_STREAMK — was removed
1480    /// 2026-07-08: 1.11x per-GEMM but its k-split f32 reorder flipped the model argmax gate;
1481    /// rig5090.jsonl 2026-07-03 has the record).
1482    pub fn qmatvec_mmq_q45k_raw(
1483        &self,
1484        bytes: &CudaSlice<u8>,
1485        x: &CudaSlice<f32>,
1486        m: usize,
1487        in_f: usize,
1488        out_f: usize,
1489        qtype: i32,
1490    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1491        assert!(
1492            in_f.is_multiple_of(256),
1493            "MMQ Q4_K/Q5_K requires in_f % 256 == 0, got {in_f}"
1494        );
1495        let act_bytes = unsafe { memra_mmq_q45k_act_bytes(in_f as i32, m as i32) };
1496        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1497        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1498        {
1499            let stream = self.gpu.stream();
1500            let (w_p, _gw) = bytes.device_ptr(&stream);
1501            let (x_p, _gx) = x.device_ptr(&stream);
1502            let (y_p, _gy) = y.device_ptr_mut(&stream);
1503            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1504            let launcher = if qtype == crate::QT_Q4_K {
1505                memra_mmq_q4_K
1506            } else {
1507                memra_mmq_q5_K
1508            };
1509            let rc = unsafe {
1510                launcher(
1511                    w_p as *const core::ffi::c_void,
1512                    x_p as *const f32,
1513                    y_p as *mut f32,
1514                    in_f as i32,
1515                    out_f as i32,
1516                    m as i32,
1517                    s_p as *mut core::ffi::c_void,
1518                    stream.cu_stream() as *mut core::ffi::c_void,
1519                )
1520            };
1521            if rc != 0 {
1522                return Err(format!("memra_mmq_q45k(qtype={qtype}) rc={rc}").into());
1523            }
1524        }
1525        Ok(y)
1526    }
1527
1528    /// Bare Q8_0 int8-MMA MMQ launch (no macro-scale) — the kernel_check accuracy-gate entry and
1529    /// the `qmatvec_mmq` dispatch body. Conventional xy-tiling only (no stream-K / fixup scratch).
1530    pub fn qmatvec_mmq_q8_0_raw(
1531        &self,
1532        bytes: &CudaSlice<u8>,
1533        x: &CudaSlice<f32>,
1534        m: usize,
1535        in_f: usize,
1536        out_f: usize,
1537    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1538        assert!(
1539            in_f.is_multiple_of(32),
1540            "MMQ Q8_0 requires in_f % 32 == 0, got {in_f}"
1541        );
1542        let act_bytes = unsafe { memra_mmq_q8_0_act_bytes(in_f as i32, m as i32) };
1543        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1544        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1545        {
1546            let stream = self.gpu.stream();
1547            let (w_p, _gw) = bytes.device_ptr(&stream);
1548            let (x_p, _gx) = x.device_ptr(&stream);
1549            let (y_p, _gy) = y.device_ptr_mut(&stream);
1550            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1551            let rc = unsafe {
1552                memra_mmq_q8_0(
1553                    w_p as *const core::ffi::c_void,
1554                    x_p as *const f32,
1555                    y_p as *mut f32,
1556                    in_f as i32,
1557                    out_f as i32,
1558                    m as i32,
1559                    s_p as *mut core::ffi::c_void,
1560                    stream.cu_stream() as *mut core::ffi::c_void,
1561                )
1562            };
1563            if rc != 0 {
1564                return Err(format!("memra_mmq_q8_0 rc={rc}").into());
1565            }
1566        }
1567        Ok(y)
1568    }
1569
1570    /// Accumulator-instrument bytes for a pre-quantized block_q8_1_mmq activation buffer
1571    /// (cu/mmq_q8_0_f32acc.cu). The caller synthesizes that buffer itself — see `accprobe_gemm`.
1572    pub fn accprobe_act_bytes(&self, in_f: usize, m: usize) -> usize {
1573        unsafe { memra_accprobe_act_bytes(in_f as i32, m as i32) }
1574    }
1575
1576    /// Run one arm of the Q1 accumulator instrument. `f32acc=false` is the Q8_0 MMQ floor's GEMM
1577    /// verbatim (s32 accumulate); `f32acc=true` is the byte-identical kernel with the f8f6f4 f32
1578    /// accumulate. `act_q` is a PRE-QUANTIZED block_q8_1_mmq buffer of at least
1579    /// `accprobe_act_bytes(in_f, m)` bytes — keeping the quantizer out of the timed region is the
1580    /// point, so this wrapper does not build it. Research instrument: the output is not a numeric
1581    /// claim.
1582    pub fn accprobe_gemm(
1583        &self,
1584        w_q8_0: &CudaSlice<u8>,
1585        act_q: &CudaSlice<u8>,
1586        m: usize,
1587        in_f: usize,
1588        out_f: usize,
1589        f32acc: bool,
1590    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1591        assert!(
1592            in_f.is_multiple_of(32),
1593            "accprobe requires in_f % 32 == 0, got {in_f}"
1594        );
1595        assert!(
1596            act_q.len() >= self.accprobe_act_bytes(in_f, m),
1597            "accprobe act_q too small: {} < {}",
1598            act_q.len(),
1599            self.accprobe_act_bytes(in_f, m)
1600        );
1601        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1602        {
1603            let stream = self.gpu.stream();
1604            let (w_p, _gw) = w_q8_0.device_ptr(&stream);
1605            let (a_p, _ga) = act_q.device_ptr(&stream);
1606            let (y_p, _gy) = y.device_ptr_mut(&stream);
1607            let f = if f32acc {
1608                memra_accprobe_gemm_f32
1609            } else {
1610                memra_accprobe_gemm_s32
1611            };
1612            let rc = unsafe {
1613                f(
1614                    w_p as *const core::ffi::c_void,
1615                    a_p as *const core::ffi::c_void,
1616                    y_p as *mut f32,
1617                    in_f as i32,
1618                    out_f as i32,
1619                    m as i32,
1620                    stream.cu_stream() as *mut core::ffi::c_void,
1621                )
1622            };
1623            if rc != 0 {
1624                let arm = if f32acc { "f32" } else { "s32" };
1625                return Err(format!("memra_accprobe_gemm_{arm} rc={rc}").into());
1626            }
1627        }
1628        Ok(y)
1629    }
1630
1631    /// Open a quantize-once sharing window for the NEXT activation (quantize-once seam): sibling
1632    /// Q4_0 MMQ matmuls on the SAME input (q/k/v; gate/up) quantize its D4 scratch once. Safe by
1633    /// construction: a hit requires the same window epoch AND the same (ptr, m, in_f) — the caller
1634    /// opens a window while it holds the shared input alive, so its address can neither change nor
1635    /// be recycled inside the window. Paths that never call this never hit the cache.
1636    pub fn mmq_act_begin(&self) {
1637        use std::sync::atomic::Ordering;
1638        MMQ_ACT_EPOCH.fetch_add(1, Ordering::Relaxed);
1639        *MMQ_ACT_SLOT.lock().unwrap() = None;
1640    }
1641
1642    /// Bare Q4_0 int8-MMA MMQ launch (no macro-scale) — the kernel_check accuracy-gate entry and
1643    /// the `qmatvec_mmq` dispatch body. `rp` selects the weight layout (MEMRA_Q4RP split-plane vs
1644    /// raw ggml 18B blocks) — pure address remap, bit-identical output.
1645    pub fn qmatvec_mmq_q4_0_raw(
1646        &self,
1647        bytes: &CudaSlice<u8>,
1648        x: &CudaSlice<f32>,
1649        m: usize,
1650        in_f: usize,
1651        out_f: usize,
1652        rp: bool,
1653    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1654        use std::sync::atomic::Ordering;
1655        assert!(
1656            in_f.is_multiple_of(32),
1657            "MMQ Q4_0 requires in_f % 32 == 0, got {in_f}"
1658        );
1659        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1660        let stream = self.gpu.stream();
1661        let (x_p, _gx) = x.device_ptr(&stream);
1662        let epoch = MMQ_ACT_EPOCH.load(Ordering::Relaxed);
1663        // quantize-once: reuse the window's scratch when the SAME activation comes back.
1664        let mut slot = MMQ_ACT_SLOT.lock().unwrap();
1665        let hit = matches!(&*slot,
1666            Some((e, p, mm, inf, _)) if *e == epoch && *p == x_p && *mm == m && *inf == in_f);
1667        if !hit {
1668            let act_bytes = unsafe { memra_mmq_q4_0_act_bytes(in_f as i32, m as i32) };
1669            let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1670            {
1671                let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1672                let rc = unsafe {
1673                    memra_mmq_q4_0_quant_act(
1674                        x_p as *const f32,
1675                        s_p as *mut core::ffi::c_void,
1676                        in_f as i32,
1677                        m as i32,
1678                        stream.cu_stream() as *mut core::ffi::c_void,
1679                    )
1680                };
1681                if rc != 0 {
1682                    return Err(
1683                        format!("memra_mmq_q4_0_quant_act(in_f={in_f}, m={m}) rc={rc}").into(),
1684                    );
1685                }
1686            }
1687            *slot = Some((epoch, x_p, m, in_f, scratch));
1688        }
1689        let scratch = &slot.as_ref().unwrap().4;
1690        {
1691            let (w_p, _gw) = bytes.device_ptr(&stream);
1692            let (y_p, _gy) = y.device_ptr_mut(&stream);
1693            let (s_p, _gs) = scratch.device_ptr(&stream);
1694            // Stream-k arm (DEFAULT since 2026-07-23; MEMRA_MMQ_SK=0 reverts to xy-tiling):
1695            // small-batch tail-wave fix — the sk entry itself falls back to (bit-identical)
1696            // tiling at >=90% wave efficiency. Band-class fold order below that. Gate: 12B
1697            // pp512 +3.3% (1.005x vs llama), pp1736 +1.0%; 31B +0.5%; D512 sentinel MATCH.
1698            //
1699            // SPEC-SERVING FLIP (2026-07-27, the f16pv/wkv acceptance-law pattern): with
1700            // MEMRA_DRAFT set, big dense models force tiling while MoE/small models defer
1701            // to the fail-closed TILE form. The former shape-timing autotune was removed 2026-08-14:
1702            // its per-process timing coin selected different fold orders on independent
1703            // boots. On the measured 82-SM 5090, TILE is both faster and higher-acceptance
1704            // for the 26B depth cell. Every other hardware class requires its own gate
1705            // before selecting SK without an explicit form override.
1706            // MEMRA_MMQ_SK controls entry and MEMRA_MMQ_SK_FORM pins the numerical form.
1707            // HOPPER DEFAULT OFF (2026-07-31, #23): on sm_90a the SK arm computes WRONG
1708            // values for the 26B a4b's non-rp Q4_0 shapes once the prefill width crosses
1709            // 256 (prefill argmax garbage, maxdiff ~10; MEMRA_MMQ_SK=0 -> MATCH,
1710            // one-variable kill x confirmed on-box). The SK split/fixup is SM-count
1711            // dependent (132 vs 170) — until the kernel is
1712            // fixed for that class, Hopper fails CLOSED to the bit-identical xy-tiling
1713            // (cost on the healthy models: g12 -1.4%, g31 -0.6% prefill, N=3 on-box).
1714            // sm_120a keeps the SK entry on (rig-divergence law). MEMRA_MMQ_SK=1 forces
1715            // entry; MEMRA_MMQ_SK_FORM=sk forces the actual SK numerical form.
1716            static SK_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1717            let sk = match crate::MMQ_SK_FORCE.load(std::sync::atomic::Ordering::Relaxed) {
1718                0 => false,
1719                1 => true,
1720                _ => *SK_ON.get_or_init(|| {
1721                    std::env::var("MEMRA_MMQ_SK")
1722                        .map(|v| v != "0")
1723                        .unwrap_or(!cfg!(memra_hopper_mma))
1724                }),
1725            };
1726            let rc = if sk {
1727                let mut fx = MMQ_FIXUP_SLOT.lock().unwrap();
1728                if fx.is_none() {
1729                    let nb = unsafe { memra_mmq_q4_0_fixup_bytes() };
1730                    *fx = Some(self.alloc_uninit::<u8>(nb)?);
1731                }
1732                let (f_p, _gf) = fx.as_mut().unwrap().device_ptr_mut(&stream);
1733                unsafe {
1734                    memra_mmq_q4_0_gemm_sk(
1735                        w_p as *const core::ffi::c_void,
1736                        s_p as *const core::ffi::c_void,
1737                        y_p as *mut f32,
1738                        f_p as *mut core::ffi::c_void,
1739                        in_f as i32,
1740                        out_f as i32,
1741                        m as i32,
1742                        stream.cu_stream() as *mut core::ffi::c_void,
1743                        rp as i32,
1744                    )
1745                }
1746            } else {
1747                unsafe {
1748                    memra_mmq_q4_0_gemm(
1749                        w_p as *const core::ffi::c_void,
1750                        s_p as *const core::ffi::c_void,
1751                        y_p as *mut f32,
1752                        in_f as i32,
1753                        out_f as i32,
1754                        m as i32,
1755                        stream.cu_stream() as *mut core::ffi::c_void,
1756                        rp as i32,
1757                    )
1758                }
1759            };
1760            if rc != 0 {
1761                return Err(format!(
1762                    "memra_mmq_q4_0_gemm(rp={rp}, in_f={in_f}, out_f={out_f}, m={m}, wbytes={}) rc={rc}",
1763                    bytes.len()
1764                )
1765                .into());
1766            }
1767        }
1768        Ok(y)
1769    }
1770
1771    /// Run the vendored NVFP4 MMQ prefill GEMM from raw weight bytes + f32 activation.
1772    /// y[m, out_f] = x[m, in_f] @ W^T. The per-tensor NVFP4 macro-scale is FOLDED into the MMQ
1773    /// write-back epilogue (was a separate scale_inplace launch + full y round-trip per matmul).
1774    /// Same elementwise multiply -> bit-identical to the two-launch form.
1775    /// `x` is the RAW f32 activation (the launcher quantizes it to block_fp4_mmq internally).
1776    pub fn qmatvec_mmq_nvfp4(
1777        &self,
1778        bytes: &CudaSlice<u8>,
1779        x: &CudaSlice<f32>,
1780        m: usize,
1781        in_f: usize,
1782        out_f: usize,
1783        scale: f32,
1784    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1785        self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, scale)
1786    }
1787
1788    /// Bare MMQ launch (no macro-scale) — for the kernel_check accuracy gate.
1789    pub fn qmatvec_mmq_nvfp4_raw(
1790        &self,
1791        bytes: &CudaSlice<u8>,
1792        x: &CudaSlice<f32>,
1793        m: usize,
1794        in_f: usize,
1795        out_f: usize,
1796    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1797        self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, 1.0)
1798    }
1799
1800    /// Bare MMQ launch on the PRE-PORT activation quantizer (per-sub-block UE4M3 scale only, no
1801    /// per-token row amax). The numeric oracle for the two-level quantizer: kernel-check runs both
1802    /// and reports the accuracy delta, so the port's value is measured rather than asserted.
1803    pub fn qmatvec_mmq_nvfp4_raw_v1(
1804        &self,
1805        bytes: &CudaSlice<u8>,
1806        x: &CudaSlice<f32>,
1807        m: usize,
1808        in_f: usize,
1809        out_f: usize,
1810    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1811        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, false, 0)
1812    }
1813
1814    /// Bare MMQ launch with an explicit residual-channel count — for the kernel-check k sweep.
1815    pub fn qmatvec_mmq_nvfp4_raw_res(
1816        &self,
1817        bytes: &CudaSlice<u8>,
1818        x: &CudaSlice<f32>,
1819        m: usize,
1820        in_f: usize,
1821        out_f: usize,
1822        residual_k: i32,
1823    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1824        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, true, residual_k)
1825    }
1826
1827    fn qmatvec_mmq_nvfp4_scaled(
1828        &self,
1829        bytes: &CudaSlice<u8>,
1830        x: &CudaSlice<f32>,
1831        m: usize,
1832        in_f: usize,
1833        out_f: usize,
1834        scale: f32,
1835    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1836        self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, scale, true, mmq_residual_k())
1837    }
1838
1839    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
1840    fn qmatvec_mmq_nvfp4_inner(
1841        &self,
1842        bytes: &CudaSlice<u8>,
1843        x: &CudaSlice<f32>,
1844        m: usize,
1845        in_f: usize,
1846        out_f: usize,
1847        scale: f32,
1848        per_token_scale: bool,
1849        residual_k: i32,
1850    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1851        assert!(
1852            in_f.is_multiple_of(64),
1853            "MMQ NVFP4 requires in_f % 64 == 0, got {in_f}"
1854        );
1855        let act_bytes = unsafe { memra_mmq_nvfp4_act_bytes(in_f as i32, m as i32) };
1856        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1857        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1858        {
1859            let stream = self.gpu.stream();
1860            let (w_p, _gw) = bytes.device_ptr(&stream);
1861            let (x_p, _gx) = x.device_ptr(&stream);
1862            let (y_p, _gy) = y.device_ptr_mut(&stream);
1863            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1864            let rc = unsafe {
1865                memra_mmq_nvfp4_ex2(
1866                    w_p as *const core::ffi::c_void,
1867                    x_p as *const f32,
1868                    y_p as *mut f32,
1869                    in_f as i32,
1870                    out_f as i32,
1871                    m as i32,
1872                    s_p as *mut core::ffi::c_void,
1873                    stream.cu_stream() as *mut core::ffi::c_void,
1874                    scale,
1875                    per_token_scale as i32,
1876                    residual_k,
1877                )
1878            };
1879            if rc != 0 {
1880                return Err(format!("memra_mmq_nvfp4_ex2 rc={rc}").into());
1881            }
1882        }
1883        Ok(y)
1884    }
1885
1886    /// STAGE 2 W4A8 MMQ NVFP4: same tile as the W4A4 path, but weight FP4 is LUT-dequantized to
1887    /// int8 at tile-load and the activation stays q8_1 int8 — the accuracy-safe rung. Macro-scale
1888    /// folded into the write-back epilogue (bit-identical to a post-matmul scale_inplace).
1889    /// `rp` selects the weight layout (A6 split-plane vs GGUF blocks) — bit-identical output.
1890    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
1891    pub fn qmatvec_mmq_nvfp4_w4a8(
1892        &self,
1893        bytes: &CudaSlice<u8>,
1894        x: &CudaSlice<f32>,
1895        m: usize,
1896        in_f: usize,
1897        out_f: usize,
1898        scale: f32,
1899        rp: bool,
1900    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1901        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, scale, rp)
1902    }
1903
1904    /// Bare W4A8 MMQ launch (no macro-scale, GGUF layout) — for the kernel_check accuracy gate.
1905    pub fn qmatvec_mmq_nvfp4_w4a8_raw(
1906        &self,
1907        bytes: &CudaSlice<u8>,
1908        x: &CudaSlice<f32>,
1909        m: usize,
1910        in_f: usize,
1911        out_f: usize,
1912    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1913        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, false)
1914    }
1915
1916    /// Bare W4A8 MMQ launch on an A6 split-plane repacked weight — the rp-loader bit-identity gate
1917    /// compares this against `qmatvec_mmq_nvfp4_w4a8_raw` on the same weight.
1918    pub fn qmatvec_mmq_nvfp4_w4a8_raw_rp(
1919        &self,
1920        bytes: &CudaSlice<u8>,
1921        x: &CudaSlice<f32>,
1922        m: usize,
1923        in_f: usize,
1924        out_f: usize,
1925    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1926        self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, true)
1927    }
1928
1929    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
1930    fn qmatvec_mmq_nvfp4_w4a8_scaled(
1931        &self,
1932        bytes: &CudaSlice<u8>,
1933        x: &CudaSlice<f32>,
1934        m: usize,
1935        in_f: usize,
1936        out_f: usize,
1937        scale: f32,
1938        rp: bool,
1939    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1940        // MEMRA_MMQ_F8F4=1: the R-B W4A8-FP8 tile (own numeric config; battery-gated seam).
1941        // SM100 compiles this route with the existing plain-E4M3 rollback form; SM120 keeps the
1942        // faster block-scale identity form. Both consume the same scratch and numeric program.
1943        static F8F4: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1944        let f8f4 = *F8F4.get_or_init(|| std::env::var("MEMRA_MMQ_F8F4").as_deref() == Ok("1"));
1945        assert!(
1946            in_f.is_multiple_of(64),
1947            "MMQ NVFP4 W4A8 requires in_f % 64 == 0, got {in_f}"
1948        );
1949        let act_bytes = unsafe { memra_mmq_nvfp4_w4a8_act_bytes(in_f as i32, m as i32) };
1950        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1951        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1952        {
1953            let stream = self.gpu.stream();
1954            let (w_p, _gw) = bytes.device_ptr(&stream);
1955            let (x_p, _gx) = x.device_ptr(&stream);
1956            let (y_p, _gy) = y.device_ptr_mut(&stream);
1957            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1958            // Scratch layouts are footprint-identical, so only the entry point swaps.
1959            let rc = unsafe {
1960                if f8f4 {
1961                    memra_mmq_nvfp4_f8f4(
1962                        w_p as *const core::ffi::c_void,
1963                        x_p as *const f32,
1964                        y_p as *mut f32,
1965                        in_f as i32,
1966                        out_f as i32,
1967                        m as i32,
1968                        s_p as *mut core::ffi::c_void,
1969                        stream.cu_stream() as *mut core::ffi::c_void,
1970                        scale,
1971                        rp as i32,
1972                    )
1973                } else {
1974                    memra_mmq_nvfp4_w4a8(
1975                        w_p as *const core::ffi::c_void,
1976                        x_p as *const f32,
1977                        y_p as *mut f32,
1978                        in_f as i32,
1979                        out_f as i32,
1980                        m as i32,
1981                        s_p as *mut core::ffi::c_void,
1982                        stream.cu_stream() as *mut core::ffi::c_void,
1983                        scale,
1984                        rp as i32,
1985                    )
1986                }
1987            };
1988            if rc != 0 {
1989                return Err(format!("memra_mmq_nvfp4_w4a8(f8f4={f8f4}) rc={rc}").into());
1990            }
1991        }
1992        Ok(y)
1993    }
1994
1995    /// PER-BLOCK FP8 MMQ prefill GEMM (cu/mmq_fp8_blk.cu). `w_e4m3` is the raw checkpoint e4m3
1996    /// plane [out_f x in_f] and `blk_scales` the device f32 grid [ceil(out_f/128) x
1997    /// ceil(in_f/128)] — no re-quantization of either.
1998    pub fn qmatvec_mmq_fp8_blk(
1999        &self,
2000        w_e4m3: &CudaSlice<u8>,
2001        blk_scales: &CudaSlice<f32>,
2002        x: &CudaSlice<f32>,
2003        m: usize,
2004        in_f: usize,
2005        out_f: usize,
2006    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2007        self.qmatvec_mmq_fp8_blk_scaled(w_e4m3, blk_scales, x, m, in_f, out_f, 1.0)
2008    }
2009
2010    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
2011    pub fn qmatvec_mmq_fp8_blk_scaled(
2012        &self,
2013        w_e4m3: &CudaSlice<u8>,
2014        blk_scales: &CudaSlice<f32>,
2015        x: &CudaSlice<f32>,
2016        m: usize,
2017        in_f: usize,
2018        out_f: usize,
2019        scale: f32,
2020    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2021        if cfg!(memra_sm100_tcgen05) && std::env::var("MEMRA_FP8_MMQ").as_deref() != Ok("1") {
2022            return Err(
2023                "B200 block-FP8 tcgen05 is NativeReference but not tuned; set \
2024                 MEMRA_FP8_MMQ=1 only for explicit qualification or research (the pinned \
2025                 pp1483 receipt measured 0.173x the established fallback)"
2026                    .into(),
2027            );
2028        }
2029        assert!(
2030            in_f.is_multiple_of(16),
2031            "per-block FP8 MMQ requires in_f % 16 == 0, got {in_f}"
2032        );
2033        #[allow(clippy::manual_div_ceil)]
2034        // allow: explicit (n + k - 1) / k is the load-bearing sizing form, kept textually identical to the kernel-side math
2035        let want_scales = ((out_f + 127) / 128) * ((in_f + 127) / 128);
2036        assert!(
2037            blk_scales.len() >= want_scales,
2038            "blk_scales too small: {} < {want_scales}",
2039            blk_scales.len()
2040        );
2041        assert!(
2042            w_e4m3.len() >= out_f * in_f,
2043            "e4m3 plane too small: {} < {}",
2044            w_e4m3.len(),
2045            out_f * in_f
2046        );
2047        let act_bytes = unsafe { memra_mmq_fp8_blk_act_bytes(in_f as i32, m as i32) };
2048        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
2049        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
2050        {
2051            let stream = self.gpu.stream();
2052            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
2053            let (sc_p, _gsc) = blk_scales.device_ptr(&stream);
2054            let (x_p, _gx) = x.device_ptr(&stream);
2055            let (y_p, _gy) = y.device_ptr_mut(&stream);
2056            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
2057            let rc = unsafe {
2058                memra_mmq_fp8_blk(
2059                    w_p as *const core::ffi::c_void,
2060                    sc_p as *const f32,
2061                    x_p as *const f32,
2062                    y_p as *mut f32,
2063                    in_f as i32,
2064                    out_f as i32,
2065                    m as i32,
2066                    s_p as *mut core::ffi::c_void,
2067                    stream.cu_stream() as *mut core::ffi::c_void,
2068                    scale,
2069                )
2070            };
2071            if rc != 0 {
2072                return Err(format!("memra_mmq_fp8_blk rc={rc}").into());
2073            }
2074        }
2075        Ok(y)
2076    }
2077
2078    /// View-backed twin of `qmatvec_mmq_fp8_blk`. Resident expert banks remain in their
2079    /// layer-wide allocations while the selected expert and token rows are passed as views.
2080    /// The CUDA launcher still performs dynamic E4M3 activation quantization; no Q8 activation
2081    /// sidecar is created.
2082    pub fn qmatvec_mmq_fp8_blk_view(
2083        &self,
2084        w_e4m3: &CudaView<'_, u8>,
2085        blk_scales: &CudaView<'_, f32>,
2086        x: &CudaView<'_, f32>,
2087        m: usize,
2088        in_f: usize,
2089        out_f: usize,
2090    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2091        if cfg!(memra_sm100_tcgen05) && std::env::var("MEMRA_FP8_MMQ").as_deref() != Ok("1") {
2092            return Err(
2093                "B200 block-FP8 tcgen05 is NativeReference but not tuned; set \
2094                 MEMRA_FP8_MMQ=1 only for explicit qualification or research (the pinned \
2095                 pp1483 receipt measured 0.173x the established fallback)"
2096                    .into(),
2097            );
2098        }
2099        assert!(
2100            in_f.is_multiple_of(16),
2101            "per-block FP8 MMQ requires in_f % 16 == 0, got {in_f}"
2102        );
2103        let want_scales = out_f.div_ceil(128) * in_f.div_ceil(128);
2104        assert!(
2105            blk_scales.len() >= want_scales,
2106            "blk_scales view too small: {} < {want_scales}",
2107            blk_scales.len()
2108        );
2109        assert!(
2110            w_e4m3.len() >= out_f * in_f,
2111            "e4m3 view too small: {} < {}",
2112            w_e4m3.len(),
2113            out_f * in_f
2114        );
2115        assert!(
2116            x.len() >= m * in_f,
2117            "activation view too small: {} < {}",
2118            x.len(),
2119            m * in_f
2120        );
2121
2122        let act_bytes = unsafe { memra_mmq_fp8_blk_act_bytes(in_f as i32, m as i32) };
2123        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
2124        let mut y = self.alloc_uninit::<f32>(m * out_f)?;
2125        {
2126            let stream = self.gpu.stream();
2127            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
2128            let (sc_p, _gsc) = blk_scales.device_ptr(&stream);
2129            let (x_p, _gx) = x.device_ptr(&stream);
2130            let (y_p, _gy) = y.device_ptr_mut(&stream);
2131            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
2132            let rc = unsafe {
2133                memra_mmq_fp8_blk(
2134                    w_p as *const core::ffi::c_void,
2135                    sc_p as *const f32,
2136                    x_p as *const f32,
2137                    y_p as *mut f32,
2138                    in_f as i32,
2139                    out_f as i32,
2140                    m as i32,
2141                    s_p as *mut core::ffi::c_void,
2142                    stream.cu_stream() as *mut core::ffi::c_void,
2143                    1.0,
2144                )
2145            };
2146            if rc != 0 {
2147                return Err(format!("memra_mmq_fp8_blk(view) rc={rc}").into());
2148            }
2149        }
2150        Ok(y)
2151    }
2152
2153    /// Count e4m3 NaN codes (magnitude 0x7F) in a device e4m3 plane. 0 is the precondition for
2154    /// routing that tensor through `qmatvec_mmq_fp8_blk` (hardware decodes them to NaN, the
2155    /// host/ARM B' reference to 0.0).
2156    pub fn fp8_blk_nan_count(
2157        &self,
2158        w_e4m3: &CudaSlice<u8>,
2159    ) -> Result<u32, Box<dyn std::error::Error>> {
2160        let mut cnt = self.htod_u32_v(&[0u32])?;
2161        let n = w_e4m3.len();
2162        {
2163            let stream = self.gpu.stream();
2164            let (w_p, _gw) = w_e4m3.device_ptr(&stream);
2165            let (c_p, _gc) = cnt.device_ptr_mut(&stream);
2166            let rc = unsafe {
2167                memra_fp8_blk_count_nan(
2168                    w_p as *const core::ffi::c_void,
2169                    n,
2170                    c_p as *mut u32,
2171                    stream.cu_stream() as *mut core::ffi::c_void,
2172                )
2173            };
2174            if rc != 0 {
2175                return Err(format!("memra_fp8_blk_count_nan rc={rc}").into());
2176            }
2177        }
2178        Ok(self.dtoh_u32(&cnt)?[0])
2179    }
2180
2181    /// Quantize token-major f32 activation [n_tokens, in_f] to the block_q8_1_mmq (D4) scratch the
2182    /// IQ expert-MMA kernel consumes. Returns the scratch buffer (one per proj input per layer).
2183    pub fn mmq_iq_quantize_act(
2184        &self,
2185        x: &CudaSlice<f32>,
2186        in_f: usize,
2187        n_tokens: usize,
2188    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
2189        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
2190        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
2191        {
2192            let stream = self.gpu.stream();
2193            let (x_p, _gx) = x.device_ptr(&stream);
2194            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
2195            let rc = unsafe {
2196                memra_mmq_iq_quantize_act(
2197                    x_p as *const f32,
2198                    s_p as *mut core::ffi::c_void,
2199                    in_f as i32,
2200                    n_tokens as i32,
2201                    stream.cu_stream() as *mut core::ffi::c_void,
2202                )
2203            };
2204            if rc != 0 {
2205                return Err(format!("memra_mmq_iq_quantize_act rc={rc}").into());
2206            }
2207        }
2208        Ok(scratch)
2209    }
2210
2211    /// Fused act-epilogue (research lever #3): silu/gelu(gate)*up + D4 quantize in one launch —
2212    /// replaces moe_pairs_{silu,gelu}_mul + mmq_iq_quantize_act without materializing the f32 act
2213    /// buffer (saves one full write + one full read pass over [n_pairs x n_ff]). Scratch bytes are
2214    /// BYTE-IDENTICAL to the two-pass path (kernel-check `iq fused act+quant` gates it).
2215    /// `act_kind`: 0 = silu*mul (qwen35moe), 1 = gelu_tanh*mul (gemma4).
2216    pub fn mmq_iq_fused_act_quant(
2217        &self,
2218        gate: &CudaSlice<f32>,
2219        up: &CudaSlice<f32>,
2220        in_f: usize,
2221        n_tokens: usize,
2222        act_kind: i32,
2223    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
2224        let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
2225        let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
2226        {
2227            let stream = self.gpu.stream();
2228            let (g_p, _gg) = gate.device_ptr(&stream);
2229            let (u_p, _gu) = up.device_ptr(&stream);
2230            let (s_p, _gs) = scratch.device_ptr_mut(&stream);
2231            let rc = unsafe {
2232                memra_mmq_iq_fused_act_quant(
2233                    g_p as *const f32,
2234                    u_p as *const f32,
2235                    s_p as *mut core::ffi::c_void,
2236                    in_f as i32,
2237                    n_tokens as i32,
2238                    act_kind,
2239                    stream.cu_stream() as *mut core::ffi::c_void,
2240                )
2241            };
2242            if rc != 0 {
2243                return Err(format!("memra_mmq_iq_fused_act_quant rc={rc}").into());
2244            }
2245        }
2246        Ok(scratch)
2247    }
2248
2249    /// Expert-segmented IQ3_S/IQ4_XS int8-MMA MMQ (the m16n8k16.s8 analog of moe_pairs_matvec_q8_dec).
2250    /// Same CSR inputs (table/ex_ids/ex_off/ex_pairs/pair_tok) + a pre-quantized q8_1_mmq activation
2251    /// scratch (from `mmq_iq_quantize_act` over n_tokens). y = [n_pairs, out_f] pair-major.
2252    #[allow(clippy::too_many_arguments)]
2253    pub fn mmq_iq_experts(
2254        &self,
2255        table: &CudaSlice<u64>,
2256        proj: i32,
2257        n_expert: usize,
2258        ex_ids: &CudaSlice<i32>,
2259        ex_off: &CudaSlice<i32>,
2260        ex_pairs: &CudaSlice<i32>,
2261        pair_tok: &CudaSlice<i32>,
2262        act_scratch: &CudaSlice<u8>,
2263        in_f: usize,
2264        out_f: usize,
2265        n_active: usize,
2266        n_pairs: usize,
2267        n_tokens: usize,
2268        qtype: i32,
2269        row_bytes: usize,
2270    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2271        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
2272        {
2273            let stream = self.gpu.stream();
2274            let (tab_p, _g0) = table.device_ptr(&stream);
2275            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
2276            let (eo_p, _g2) = ex_off.device_ptr(&stream);
2277            let (ep_p, _g3) = ex_pairs.device_ptr(&stream);
2278            let (pt_p, _g4) = pair_tok.device_ptr(&stream);
2279            let (as_p, _g5) = act_scratch.device_ptr(&stream);
2280            let (y_p, _g6) = y.device_ptr_mut(&stream);
2281            let rc = unsafe {
2282                memra_mmq_iq_experts(
2283                    tab_p as *const u64,
2284                    proj,
2285                    n_expert as i32,
2286                    ei_p as *const i32,
2287                    eo_p as *const i32,
2288                    ep_p as *const i32,
2289                    pt_p as *const i32,
2290                    as_p as *const core::ffi::c_void,
2291                    y_p as *mut f32,
2292                    in_f as i32,
2293                    out_f as i32,
2294                    n_active as i32,
2295                    n_tokens as i32,
2296                    qtype,
2297                    row_bytes as i64,
2298                    stream.cu_stream() as *mut core::ffi::c_void,
2299                )
2300            };
2301            if rc != 0 {
2302                return Err(format!("memra_mmq_iq_experts rc={rc}").into());
2303            }
2304        }
2305        Ok(y)
2306    }
2307
2308    /// Gather+convert the activation to f16 pair-major [n_pairs, in_f] for the grouped
2309    /// GEMM, normalized per row by its amax (raw f16 overflows on gemma's activation
2310    /// spikes — round 46 NaN find). Returns (act_f16, row_scales) — the scales fold back
2311    /// into the GEMM output. `pair_tok` = None when the input is already pair-major.
2312    pub fn moe_f16g_act(
2313        &self,
2314        x: &CudaSlice<f32>,
2315        pair_tok: Option<&CudaSlice<i32>>,
2316        in_f: usize,
2317        n_pairs: usize,
2318    ) -> Result<(CudaSlice<u8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
2319        let mut act = self.alloc_uninit::<u8>(n_pairs * in_f * 2)?;
2320        let mut scales = self.alloc_uninit::<f32>(n_pairs)?;
2321        {
2322            let stream = self.gpu.stream();
2323            let (x_p, _gx) = x.device_ptr(&stream);
2324            let pt_p = match pair_tok {
2325                Some(pt) => {
2326                    let (p, _g) = pt.device_ptr(&stream);
2327                    p as *const i32
2328                }
2329                None => std::ptr::null(),
2330            };
2331            let (a_p, _ga) = act.device_ptr_mut(&stream);
2332            let (s_p, _gs) = scales.device_ptr_mut(&stream);
2333            let rc = unsafe {
2334                memra_moe_f16g_gather_act(
2335                    x_p as *const f32,
2336                    pt_p,
2337                    a_p as *mut core::ffi::c_void,
2338                    s_p as *mut f32,
2339                    in_f as i32,
2340                    n_pairs as i32,
2341                    stream.cu_stream() as *mut core::ffi::c_void,
2342                )
2343            };
2344            if rc != 0 {
2345                return Err(format!("memra_moe_f16g_gather_act rc={rc}").into());
2346            }
2347        }
2348        Ok((act, scales))
2349    }
2350
2351    /// One projection through the grouped f16 lane: dequant the active experts' rows to an
2352    /// f16 workspace, then ONE grouped GEMM over the CSR groups (variable m per expert).
2353    /// y = f32 [n_pairs, out_f] pair-major — same layout as mmq_iq_experts.
2354    /// MEMRA_MOE_F16G=1: cublasGemmGroupedBatchedEx (+ h2f pass + per-projection sync — the
2355    /// grouped API runs on internal streams unordered with ours, round-47 ledger).
2356    /// MEMRA_MOE_F16G=2: single-kernel grouped GEMM on the engine stream (round 49) — the
2357    /// row scale folds into the kernel epilogue; no f16 C, no h2f, NO sync (ordered by
2358    /// construction). f16-MIRROR numeric class either way (argmax/spec gated, not
2359    /// byte-identity). Errors on unsupported qtype (caller keeps the MMQ arm as fallback).
2360    #[allow(clippy::too_many_arguments)]
2361    /// Bind the RUNTIME API's current device to `ordinal`. Every raw `<<<>>>` launch in the
2362    /// grouped-MoE FFI follows this, not cudarc's pushed driver context — mandatory before
2363    /// calling the FFI on a non-root rank engine (the TP2 grouped prime), a mismatch is
2364    /// cudaErrorInvalidValue.
2365    pub fn bind_runtime_device(&self, ordinal: i32) -> Result<(), Box<dyn std::error::Error>> {
2366        let rc = unsafe { memra_bind_device(ordinal) };
2367        if rc != 0 {
2368            return Err(format!("cudaSetDevice({ordinal}) rc={rc}").into());
2369        }
2370        Ok(())
2371    }
2372
2373    #[allow(clippy::too_many_arguments)]
2374    // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
2375    #[allow(clippy::manual_is_multiple_of)] // allow: divisor is runtime-derived; the modulo form keeps a zero divisor loud (a panic), where is_multiple_of would return false silently
2376    pub fn moe_f16_grouped(
2377        &self,
2378        table: &CudaSlice<u64>,
2379        proj: i32,
2380        n_expert: usize,
2381        ex_ids: &CudaSlice<i32>,
2382        ex_off_host: &[i32],
2383        ex_off_dev: &CudaSlice<i32>,
2384        act_f16: &CudaSlice<u8>,
2385        act_scale: &CudaSlice<f32>,
2386        in_f: usize,
2387        out_f: usize,
2388        n_active: usize,
2389        n_pairs: usize,
2390        qtype: i32,
2391        row_bytes: usize,
2392    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2393        let sk = crate::moe_f16g_mode() >= 2 && in_f.is_multiple_of(32);
2394        // DIRECT-FROM-QUANT lane (lane/kquant-tile-loaders + lane/iq-direct-loaders, default
2395        // ON — MEMRA_F16G_DIRECT=0 is the rollback seam): Q4_K/Q6_K/IQ4_XS/IQ3_S expert
2396        // projections skip the dequant-workspace pass entirely; the sk visitor forms dequant
2397        // B tiles in-register from the superblocks. Bit-identical to the workspace path by
2398        // construction (kernel-check "f16g-kq-direct") — this is a pure data-movement change,
2399        // not a numeric-class change. Admission mirrors the C-side guards; the grid-scan
2400        // rollback arm (MEMRA_F16G_SK=0) keeps the workspace.
2401        let (shape_sel, cross) = crate::moe_f16g_sk_params();
2402        if sk
2403            && shape_sel >= 0
2404            && crate::moe_f16g_direct_on(qtype)
2405            && (qtype == crate::QT_Q4_K
2406                || qtype == crate::QT_Q6_K
2407                || qtype == crate::QT_IQ4_XS
2408                || qtype == crate::QT_IQ3_S
2409                || qtype == crate::QT_NVFP4
2410                // v2 slot-major banks read through the same direct lane (kq_fetch's v2 branch),
2411                // which is what keeps the grouped prime off the 1.5 GB/projection dequant
2412                // workspace it otherwise falls back to.
2413                || qtype == crate::QT_NVFP4_V2)
2414            // NVFP4 walks 64-value blocks (its 16-value window is one UE4M3 sub-block);
2415            // the kq/IQ classes walk 256-value superblocks. Mirrors the C-side guard.
2416            && in_f % (if qtype == crate::QT_NVFP4 || qtype == crate::QT_NVFP4_V2 { 64 } else { 256 }) == 0
2417            && n_active <= 512
2418            && n_active > 0
2419        {
2420            let max_m = ex_off_host
2421                .windows(2)
2422                .map(|w| w[1] - w[0])
2423                .max()
2424                .unwrap_or(0);
2425            let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
2426            {
2427                let stream = self.gpu.stream();
2428                let (tab_p, _g0) = table.device_ptr(&stream);
2429                let (ei_p, _g1) = ex_ids.device_ptr(&stream);
2430                let (a_p, _g2) = act_f16.device_ptr(&stream);
2431                let (s_p, _g3) = act_scale.device_ptr(&stream);
2432                let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
2433                let (y_p, _g5) = y.device_ptr_mut(&stream);
2434                let rc = unsafe {
2435                    memra_moe_kq_gemm_sk(
2436                        tab_p as *const u64,
2437                        proj,
2438                        n_expert as i32,
2439                        ei_p as *const i32,
2440                        a_p as *const core::ffi::c_void,
2441                        y_p as *mut f32,
2442                        s_p as *const f32,
2443                        off_p as *const i32,
2444                        ex_off_host.as_ptr(),
2445                        n_active as i32,
2446                        max_m,
2447                        in_f as i32,
2448                        out_f as i32,
2449                        qtype,
2450                        cross,
2451                        crate::moe_f16g_tail_on() as i32,
2452                        row_bytes as i64,
2453                        stream.cu_stream() as *mut core::ffi::c_void,
2454                    )
2455                };
2456                if rc != 0 {
2457                    return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into());
2458                }
2459            }
2460            return Ok(y);
2461        }
2462        // one-time cublas grouped init (algo heuristics + module load cost ~10% of a cold
2463        // g26 prime when paid inside the first projection): a tiny dummy grouped GEMM at
2464        // first use, synced, so the real prime runs warm. The =2 path never touches cublas.
2465        if !sk {
2466            static WARM: std::sync::Once = std::sync::Once::new();
2467            let mut warm_err = None;
2468            WARM.call_once(|| {
2469                let r = (|| -> Result<(), Box<dyn std::error::Error>> {
2470                    let w = self.alloc_uninit::<u8>(2 * 32 * 64 * 2)?;
2471                    let a = self.alloc_uninit::<u8>(4 * 64 * 2)?;
2472                    let mut yw = self.alloc_uninit::<u8>(4 * 32 * 2)?;
2473                    let off = [0i32, 2, 4];
2474                    let stream = self.gpu.stream();
2475                    let (w_p, _a1) = w.device_ptr(&stream);
2476                    let (a_p, _a2) = a.device_ptr(&stream);
2477                    let (y_p, _a3) = yw.device_ptr_mut(&stream);
2478                    let rc = unsafe {
2479                        memra_moe_f16g_gemm(
2480                            w_p as *const core::ffi::c_void,
2481                            a_p as *const core::ffi::c_void,
2482                            y_p as *mut core::ffi::c_void,
2483                            off.as_ptr(),
2484                            2,
2485                            64,
2486                            32,
2487                            stream.cu_stream() as *mut core::ffi::c_void,
2488                        )
2489                    };
2490                    if rc != 0 {
2491                        return Err(format!("f16g warmup rc={rc}").into());
2492                    }
2493                    self.gpu.stream().synchronize()?;
2494                    Ok(())
2495                })();
2496                if let Err(e) = r {
2497                    warm_err = Some(e.to_string());
2498                }
2499            });
2500            if let Some(we) = warm_err {
2501                return Err(we.into());
2502            }
2503        }
2504        let w_bytes = n_active * out_f * in_f * 2;
2505        let mut w_f16 = self.alloc_uninit::<u8>(w_bytes)?;
2506        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
2507        {
2508            let stream = self.gpu.stream();
2509            let (tab_p, _g0) = table.device_ptr(&stream);
2510            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
2511            let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
2512            let rc = unsafe {
2513                memra_moe_f16g_dequant(
2514                    tab_p as *const u64,
2515                    proj,
2516                    n_expert as i32,
2517                    ei_p as *const i32,
2518                    w_p as *mut core::ffi::c_void,
2519                    in_f as i32,
2520                    out_f as i32,
2521                    n_active as i32,
2522                    qtype,
2523                    row_bytes as i64,
2524                    stream.cu_stream() as *mut core::ffi::c_void,
2525                )
2526            };
2527            if rc != 0 {
2528                return Err(format!("memra_moe_f16g_dequant rc={rc}").into());
2529            }
2530            let (a_p, _g3) = act_f16.device_ptr(&stream);
2531            let (s_p, _g6) = act_scale.device_ptr(&stream);
2532            let (y_p, _g5) = y.device_ptr_mut(&stream);
2533            if sk {
2534                let max_m = ex_off_host
2535                    .windows(2)
2536                    .map(|w| w[1] - w[0])
2537                    .max()
2538                    .unwrap_or(0);
2539                let (off_p, _g7) = ex_off_dev.device_ptr(&stream);
2540                let (shape_sel, cross) = crate::moe_f16g_sk_params();
2541                let rc = unsafe {
2542                    memra_moe_f16g_gemm_sk(
2543                        w_p as *const core::ffi::c_void,
2544                        a_p as *const core::ffi::c_void,
2545                        y_p as *mut f32,
2546                        s_p as *const f32,
2547                        off_p as *const i32,
2548                        ex_off_host.as_ptr(),
2549                        n_active as i32,
2550                        max_m,
2551                        in_f as i32,
2552                        out_f as i32,
2553                        shape_sel,
2554                        cross,
2555                        crate::moe_f16g_tail_on() as i32,
2556                        stream.cu_stream() as *mut core::ffi::c_void,
2557                    )
2558                };
2559                if rc != 0 {
2560                    return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into());
2561                }
2562            } else {
2563                let mut y16 = self.alloc_uninit::<u8>(n_pairs * out_f * 2)?;
2564                let (y16_p, _g4) = y16.device_ptr_mut(&stream);
2565                let rc = unsafe {
2566                    memra_moe_f16g_gemm(
2567                        w_p as *const core::ffi::c_void,
2568                        a_p as *const core::ffi::c_void,
2569                        y16_p as *mut core::ffi::c_void,
2570                        ex_off_host.as_ptr(),
2571                        n_active as i32,
2572                        in_f as i32,
2573                        out_f as i32,
2574                        stream.cu_stream() as *mut core::ffi::c_void,
2575                    )
2576                };
2577                if rc != 0 {
2578                    return Err(format!("memra_moe_f16g_gemm rc={rc}").into());
2579                }
2580                let rc = unsafe {
2581                    memra_moe_f16g_h2f_scaled(
2582                        y16_p as *const core::ffi::c_void,
2583                        y_p as *mut f32,
2584                        s_p as *const f32,
2585                        out_f as i32,
2586                        n_pairs as i32,
2587                        stream.cu_stream() as *mut core::ffi::c_void,
2588                    )
2589                };
2590                if rc != 0 {
2591                    return Err(format!("memra_moe_f16g_h2f_scaled rc={rc}").into());
2592                }
2593            }
2594        }
2595        // MODE 1 ONLY: cublasGemmGroupedBatchedEx issues through internal streams NOT ordered
2596        // with ours (round 46: NaN race, clean under sync — 205=205 MATCH). Full sync per
2597        // projection. Mode 2 (single kernel, our stream) is ordered by construction — no sync,
2598        // that is the point of this arc.
2599        if !sk {
2600            self.gpu.stream().synchronize()?;
2601        }
2602        if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
2603            // FULL NaN/Inf scan of w, act (through h2f) and y — localizes the corrupt stage.
2604            let wn = n_active * out_f * in_f;
2605            let an = n_pairs * in_f;
2606            let mut wf = self.alloc_uninit::<f32>(wn)?;
2607            let mut af = self.alloc_uninit::<f32>(an)?;
2608            {
2609                let stream = self.gpu.stream();
2610                let (w_p, _a) = w_f16.device_ptr(&stream);
2611                let (a_p, _b) = act_f16.device_ptr(&stream);
2612                let (wf_p, _c) = wf.device_ptr_mut(&stream);
2613                let (af_p, _d) = af.device_ptr_mut(&stream);
2614                unsafe {
2615                    memra_moe_f16g_h2f(
2616                        w_p as *const core::ffi::c_void,
2617                        wf_p as *mut f32,
2618                        wn,
2619                        stream.cu_stream() as *mut core::ffi::c_void,
2620                    );
2621                    memra_moe_f16g_h2f(
2622                        a_p as *const core::ffi::c_void,
2623                        af_p as *mut f32,
2624                        an,
2625                        stream.cu_stream() as *mut core::ffi::c_void,
2626                    );
2627                }
2628            }
2629            let (wh, ah, yh) = (self.dtoh(&wf)?, self.dtoh(&af)?, self.dtoh(&y)?);
2630            let scan = |v: &[f32]| -> (usize, f32) {
2631                let bad = v.iter().filter(|x| !x.is_finite()).count();
2632                let mx = v
2633                    .iter()
2634                    .filter(|x| x.is_finite())
2635                    .fold(0.0f32, |m, x| m.max(x.abs()));
2636                (bad, mx)
2637            };
2638            let (wb, wm) = scan(&wh);
2639            let (ab, am) = scan(&ah);
2640            let (yb, ym) = scan(&yh);
2641            eprintln!(
2642                "[f16g-debug] proj={proj} w: bad={wb} max={wm:.3e} | act: bad={ab} \
2643                       max={am:.3e} | y: bad={yb} max={ym:.3e} (na={n_active} np={n_pairs} \
2644                       in={in_f} out={out_f})"
2645            );
2646        }
2647        Ok(y)
2648    }
2649
2650    /// Raw sk grouped-GEMM entry for kernel-check ("f16g-sk" section): explicit shape/cross
2651    /// instead of the env policy. shape_sel < 0 = the round-49 grid-scan rollback arm; else
2652    /// the round-51 problem-visitor split at `cross` (1 forces all-128, i32::MAX all-32).
2653    /// tail: 1 = the deep tail (32x64x64 3-stage, lane/sk-tail-form) on sub-cross groups,
2654    /// 0 = the round-51 2-stage 32x64x32 tail.
2655    /// w_f16 = [n_active][out_f][in_f] f16 bytes, act_f16 = [n_pairs][in_f] f16 bytes.
2656    #[allow(clippy::too_many_arguments)]
2657    pub fn moe_f16g_gemm_sk_raw(
2658        &self,
2659        w_f16: &CudaSlice<u8>,
2660        act_f16: &CudaSlice<u8>,
2661        row_scale: &CudaSlice<f32>,
2662        ex_off_host: &[i32],
2663        ex_off_dev: &CudaSlice<i32>,
2664        in_f: usize,
2665        out_f: usize,
2666        n_pairs: usize,
2667        shape_sel: i32,
2668        cross: i32,
2669        tail: i32,
2670    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2671        let n_active = ex_off_host.len() - 1;
2672        let max_m = ex_off_host
2673            .windows(2)
2674            .map(|w| w[1] - w[0])
2675            .max()
2676            .unwrap_or(0);
2677        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
2678        {
2679            let stream = self.gpu.stream();
2680            let (w_p, _g0) = w_f16.device_ptr(&stream);
2681            let (a_p, _g1) = act_f16.device_ptr(&stream);
2682            let (s_p, _g2) = row_scale.device_ptr(&stream);
2683            let (off_p, _g3) = ex_off_dev.device_ptr(&stream);
2684            let (y_p, _g4) = y.device_ptr_mut(&stream);
2685            let rc = unsafe {
2686                memra_moe_f16g_gemm_sk(
2687                    w_p as *const core::ffi::c_void,
2688                    a_p as *const core::ffi::c_void,
2689                    y_p as *mut f32,
2690                    s_p as *const f32,
2691                    off_p as *const i32,
2692                    ex_off_host.as_ptr(),
2693                    n_active as i32,
2694                    max_m,
2695                    in_f as i32,
2696                    out_f as i32,
2697                    shape_sel,
2698                    cross,
2699                    tail,
2700                    stream.cu_stream() as *mut core::ffi::c_void,
2701                )
2702            };
2703            if rc != 0 {
2704                return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into());
2705            }
2706        }
2707        Ok(y)
2708    }
2709
2710    /// Raw direct-from-quant sk grouped-GEMM entry for kernel-check ("f16g-kq-direct"):
2711    /// explicit cross/tail instead of the env policy. `table` = device u64 pointer table
2712    /// (proj-major, [n_proj][n_expert] — same contract as moe_f16_grouped), `ex_ids` =
2713    /// active-expert ids (device). Visitor forms only (the C side rejects anything else).
2714    #[allow(clippy::too_many_arguments)]
2715    pub fn moe_kq_gemm_sk_raw(
2716        &self,
2717        table: &CudaSlice<u64>,
2718        proj: i32,
2719        n_expert: usize,
2720        ex_ids: &CudaSlice<i32>,
2721        act_f16: &CudaSlice<u8>,
2722        row_scale: &CudaSlice<f32>,
2723        ex_off_host: &[i32],
2724        ex_off_dev: &CudaSlice<i32>,
2725        in_f: usize,
2726        out_f: usize,
2727        n_pairs: usize,
2728        qtype: i32,
2729        row_bytes: usize,
2730        cross: i32,
2731        tail: i32,
2732    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2733        let n_active = ex_off_host.len() - 1;
2734        let max_m = ex_off_host
2735            .windows(2)
2736            .map(|w| w[1] - w[0])
2737            .max()
2738            .unwrap_or(0);
2739        let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
2740        {
2741            let stream = self.gpu.stream();
2742            let (tab_p, _g0) = table.device_ptr(&stream);
2743            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
2744            let (a_p, _g2) = act_f16.device_ptr(&stream);
2745            let (s_p, _g3) = row_scale.device_ptr(&stream);
2746            let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
2747            let (y_p, _g5) = y.device_ptr_mut(&stream);
2748            let rc = unsafe {
2749                memra_moe_kq_gemm_sk(
2750                    tab_p as *const u64,
2751                    proj,
2752                    n_expert as i32,
2753                    ei_p as *const i32,
2754                    a_p as *const core::ffi::c_void,
2755                    y_p as *mut f32,
2756                    s_p as *const f32,
2757                    off_p as *const i32,
2758                    ex_off_host.as_ptr(),
2759                    n_active as i32,
2760                    max_m,
2761                    in_f as i32,
2762                    out_f as i32,
2763                    qtype,
2764                    cross,
2765                    tail,
2766                    row_bytes as i64,
2767                    stream.cu_stream() as *mut core::ffi::c_void,
2768                )
2769            };
2770            if rc != 0 {
2771                return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into());
2772            }
2773        }
2774        Ok(y)
2775    }
2776
2777    /// Raw dequant-workspace entry for kernel-check: dequant the active experts' rows to a
2778    /// fresh f16 workspace via the same kernel `moe_f16_grouped` uses (the direct loaders'
2779    /// bitwise reference).
2780    #[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
2781    pub fn moe_f16g_dequant_raw(
2782        &self,
2783        table: &CudaSlice<u64>,
2784        proj: i32,
2785        n_expert: usize,
2786        ex_ids: &CudaSlice<i32>,
2787        in_f: usize,
2788        out_f: usize,
2789        n_active: usize,
2790        qtype: i32,
2791        row_bytes: usize,
2792    ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
2793        let mut w_f16 = self.alloc_uninit::<u8>(n_active * out_f * in_f * 2)?;
2794        {
2795            let stream = self.gpu.stream();
2796            let (tab_p, _g0) = table.device_ptr(&stream);
2797            let (ei_p, _g1) = ex_ids.device_ptr(&stream);
2798            let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
2799            let rc = unsafe {
2800                memra_moe_f16g_dequant(
2801                    tab_p as *const u64,
2802                    proj,
2803                    n_expert as i32,
2804                    ei_p as *const i32,
2805                    w_p as *mut core::ffi::c_void,
2806                    in_f as i32,
2807                    out_f as i32,
2808                    n_active as i32,
2809                    qtype,
2810                    row_bytes as i64,
2811                    stream.cu_stream() as *mut core::ffi::c_void,
2812                )
2813            };
2814            if rc != 0 {
2815                return Err(format!("memra_moe_f16g_dequant rc={rc}").into());
2816            }
2817        }
2818        Ok(w_f16)
2819    }
2820}