Skip to main content

memra_engine/
model.rs

1//! Dense transformer model: loads GGUF weights to GPU (Stage-1: dequant→f32), runs the
2//! shared full-attention + SwiGLU forward graph. Arch-agnostic via ModelConfig; this path is
3//! exactly the dense-transformer graph (qwen3) and the full-attention layers of hybrids.
4
5use crate::{
6    Engine, QT_BF16, QT_F32, QT_IQ3_S, QT_IQ4_XS, QT_NVFP4, QT_Q2_K, QT_Q3_K, QT_Q4_0, QT_Q4_K,
7    QT_Q5_K, QT_Q6_K, QT_Q8_0,
8};
9use memra_gguf::config::ModelConfig;
10use memra_gguf::source::{DiskExtent, GgufSource, TensorSource};
11use memra_gguf::{dequant, GgmlType, GgufFile};
12use cudarc::driver::CudaSlice;
13use std::collections::HashMap;
14
15/// A weight tensor resident on GPU. Quantized weights stay in GGUF block bytes (`Quant`);
16/// small non-quant tensors (norms, sometimes embed/lm_head) are kept dequantized as f32 (`Float`).
17/// This keeps VRAM ~= on-disk quant size (fixes the f32-on-load OOM).
18pub enum GpuTensor {
19    Quant {
20        bytes: CudaSlice<u8>,
21        qtype: i32,
22        row_bytes: usize,
23        ne: Vec<u64>,
24        scale: f32,
25        /// SPLIT-PLANE walk-order repack (A6, 2026-07-04): NVFP4 matmul weights are repacked at
26        /// load into [quant plane out_f x in_f/64 x 32B][scale plane out_f x in_f/64 x 4B] — same
27        /// bytes, same total size, but a lane's per-group weight read becomes ONE 16B-aligned
28        /// LDG.128 + a dense 4B scale word instead of 5 scattered 4B LDGs at 36B stride (the "18B
29        /// straggle"). Every consumer kernel has an `_rp` twin (bit-identical: pure byte
30        /// permutation, same dot order). `rp=false` = original GGUF block layout (all other
31        /// dtypes, MoE-staged expert bytes, MEMRA_RP=0 escape).
32        rp: bool,
33        /// CUTLASS NVFP4 prefill operand (repacked B + swizzled SFB), built ALONGSIDE `bytes` at load
34        /// when MEMRA_FP4_CUTLASS is set. `bytes` stays raw GGUF so decode (MMVQ/dp4a) is untouched;
35        /// prefill (m>=128) reads this. Only ever Some for NVFP4 weights under cfg(memra_cutlass).
36        #[cfg(memra_cutlass)]
37        cutlass: Option<CutlassWeight>,
38        /// FP8-ACT PREFILL operand (MEMRA_PP_FP8=1, probe verdict 2026-07-08): the checkpoint's RAW
39        /// e4m3 bytes + per-tensor f32 weight_scale, stashed ALONGSIDE the Q8_0 re-encode for the
40        /// F8-E4M3-origin 2D projections (~1 B/w extra on those layers). `bytes` stays Q8_0 so
41        /// decode (dp4a/MMVQ) is untouched; only the m>=16 prefill dispatch (cuBLASLt FP8 TN,
42        /// fp8_ffi.rs) reads this. None unless the env is set at load (zero VRAM cost by default).
43        fp8: Option<Fp8Weight>,
44        /// Q4_0 SPLIT-PLANE MIRROR (2026-07-10, the 18B-straggle cure for decode): qs plane
45        /// [out_f x nblk x 16B] + d plane [out_f x nblk x 2B] built device-side at model load
46        /// (q4_0_split_rp_build) for decode-hot trunk weights. Raw `bytes` stay resident —
47        /// prefill (gemm/MMQ) and Stage-A read those; the m<=8 mmvq/batched/fused dispatch
48        /// reads this when present (`_rp` twins; microprobe m=1 1.34x, m=3 1.17x, bitwise).
49        /// None everywhere except where the arch-load hook opted in (VRAM cost = weight size).
50        rp4: Option<CudaSlice<u8>>,
51        /// FP16 DEQUANT MIRROR (MEMRA_PP_F16=1, probe 2026-07-26): row-major fp16 of a 2D Q8_0
52        /// projection, built device-side at load (f16_ffi::build_q8_f16). `bytes` stay Q8_0 so
53        /// decode is untouched; the m>=16 prefill dispatch (cuBLASLt FP16 TN, 611-687 TF vs
54        /// MMQ's ~200 TF class) reads this. None unless the env is set (VRAM = 2 B/w extra).
55        f16: Option<CudaSlice<u8>>,
56    },
57    Float {
58        data: CudaSlice<f32>,
59        ne: Vec<u64>,
60    },
61    /// BF16-RESIDENT full-precision matmul weight (MEMRA_FULL_PREC only). Holds the checkpoint's raw
62    /// bf16 bytes (`u8`, little-endian u16 pairs) — 2 B/w vs the 4 B/w a `Float` f32 materialization
63    /// would cost, so the 9B trunk stays ~18GB in VRAM instead of ~36GB. Consumed via dequant-on-use:
64    /// each matmul expands this to a transient f32 scratch and rides the SAME cuBLASLt f32 GEMV the
65    /// `Float` arm uses (bit-identical to a load-time bf16->f32 dequant, just deferred). Never a norm
66    /// (norms stay `Float` f32); never on a fast/GEMM/MMQ path (uses_q8_1_fast/gemm_supports = false).
67    FloatBf16 {
68        data: CudaSlice<u8>,
69        ne: Vec<u64>,
70    },
71}
72
73/// FP8-native prefill operand: raw checkpoint e4m3 codes `[out_f, in_f]` row-major (EXACT — the
74/// weight side of the FP8 GEMM does no re-quantization) + its weight scale(s). Per-tensor class:
75/// `scale` is the dequant scalar folded into the GEMM's scale pointer together with the per-batch
76/// activation scale, `blk == None`. Block-128 class (Qwen official FP8): `blk == Some` and
77/// `scale == 1.0` — see `Fp8BlockScales` for the resident layout contract.
78pub struct Fp8Weight {
79    pub bytes: CudaSlice<u8>,
80    pub scale: f32,
81    pub blk: Option<Fp8BlockScales>,
82}
83
84/// Device-resident block-128 weight-scale grid for an e4m3 operand (B1b, lane fp8st 2026-08-03).
85///
86/// STORAGE LAYOUT (the canonical device layout every future consumer builds from): a flat f32
87/// buffer in the CHECKPOINT'S on-disk order — row-major `[rows = ceil(out_f/128),
88/// cols = ceil(in_f/128)]`, so `scales[ob * cols + kb]` scales the 128x128 weight tile at
89/// output-block `ob`, input-block `kb` (uploaded verbatim from `memra_gguf::source::F8BlockGrid`,
90/// no permutation — one host decode, one htod). Rationale: (1) the per-block-dequant mmvq twin
91/// (qmatvec_e4m3_mmvq extension, DECISION.md B1) indexes `(o >> 7) * cols + (e >> 7)` — natural
92/// in this order; (2) for cuBLASLt BLK128x128 the weight `[out, in]` row-major is the TN GEMM's
93/// column-major `[k=in, n=out]` A operand, and this same linear order IS that view's column-major
94/// block grid with ld = cols(=kblk) — probe P1 (`probe/fp8_lt_blk_probe.cu`) verifies whether
95/// sm_120 accepts it directly; if Lt wants a different order, the reorder happens at the GEMM
96/// plan build, NOT here. NO KERNEL CONSUMES THIS YET: the loader keeps every block-128 tensor's
97/// decode/prefill on the Q8_0 re-encode until the consuming kernels land (try_fp8_gemm skips
98/// blk operands; the QT_F8_E4M3 one-copy arm rejects them). This struct's job is bytes+scales
99/// resident and correct.
100pub struct Fp8BlockScales {
101    pub scales: CudaSlice<f32>,
102    pub rows: usize, // ceil(out_f/128)
103    pub cols: usize, // ceil(in_f/128)
104}
105
106/// Host-side split-plane repack of NVFP4 GGUF block bytes (A6). Input: out_f rows of in_f/64
107/// 36-byte blocks ([4B UE4M3 scales][32B packed e2m1]). Output (same length): quant plane
108/// (out_f x nsb64 x 32B) followed by scale plane (out_f x nsb64 x 4B). Pure byte permutation.
109pub fn repack_nvfp4_split(bytes: &[u8], out_f: usize) -> Vec<u8> {
110    let row_bytes = bytes.len() / out_f;
111    let nsb64 = row_bytes / 36;
112    debug_assert_eq!(
113        row_bytes % 36,
114        0,
115        "NVFP4 row_bytes must be a multiple of 36"
116    );
117    let qplane = out_f * nsb64 * 32;
118    let mut rp = vec![0u8; bytes.len()];
119    for o in 0..out_f {
120        for s in 0..nsb64 {
121            let src = &bytes[o * row_bytes + s * 36..o * row_bytes + s * 36 + 36];
122            rp[qplane + (o * nsb64 + s) * 4..qplane + (o * nsb64 + s) * 4 + 4]
123                .copy_from_slice(&src[0..4]);
124            rp[(o * nsb64 + s) * 32..(o * nsb64 + s) * 32 + 32].copy_from_slice(&src[4..36]);
125        }
126    }
127    rp
128}
129
130/// Inverse of `repack_nvfp4_split` (the roundtrip gate).
131pub fn unpack_nvfp4_split(rp: &[u8], out_f: usize) -> Vec<u8> {
132    let row_bytes = rp.len() / out_f;
133    let nsb64 = row_bytes / 36;
134    let qplane = out_f * nsb64 * 32;
135    let mut back = vec![0u8; rp.len()];
136    for o in 0..out_f {
137        for s in 0..nsb64 {
138            back[o * row_bytes + s * 36..o * row_bytes + s * 36 + 4].copy_from_slice(
139                &rp[qplane + (o * nsb64 + s) * 4..qplane + (o * nsb64 + s) * 4 + 4],
140            );
141            back[o * row_bytes + s * 36 + 4..o * row_bytes + s * 36 + 36]
142                .copy_from_slice(&rp[(o * nsb64 + s) * 32..(o * nsb64 + s) * 32 + 32]);
143        }
144    }
145    back
146}
147
148/// A6 repack seam: default ON, `MEMRA_RP=0` restores the GGUF block layout everywhere (rollback/A-B).
149pub fn rp_enabled() -> bool {
150    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
151    *ON.get_or_init(|| std::env::var("MEMRA_RP").map(|v| v != "0").unwrap_or(true))
152}
153
154/// FULL-PRECISION LOADER MODE (MEMRA_FULL_PREC=1, default OFF — MTP-heal research platform).
155/// Bypasses the standing loader law (large BF16/F8 -> Q8_0/NVFP4 re-encode, the "Float-poison"
156/// tripwire). Under this flag every weight loads as Float and compute rides the Stage-A f32 oracle
157/// path end to end — SLOW IS FINE, this mode exists for exactness (the MTP acceptance CEILING at
158/// full precision), not speed. Large 2D matmul weights stay bf16-resident (`GpuTensor::FloatBf16`)
159/// with dequant-on-use so the 9B (~18GB bf16) + f32 activations fit 24GB instead of blowing to
160/// ~38GB as an all-f32 materialization. The Float-poison tripwire warnings are CORRECT behavior
161/// here and are suppressed. See docs/FLAGS.md and HANDOVER "MEMRA DUAL-SHAPE".
162pub fn full_prec_enabled() -> bool {
163    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
164    *ON.get_or_init(|| {
165        std::env::var("MEMRA_FULL_PREC")
166            .map(|v| v == "1")
167            .unwrap_or(false)
168    })
169}
170
171/// LOADER-LAW allowlist (loadersweep audit 2026-07-08): 2D Float tensors that are DELIBERATELY
172/// Float despite being matmul-class. Every entry needs an audit rationale — this list silences
173/// the tripwire below, so an unjustified entry re-opens the trap.
174///   * ffn_gate_inp (MoE router, 35B GGUF F32 [2048,256] / M3 ST F32 [6144,64]): the router's
175///     top-k SELECTION is discontinuous — quantizing shifts logits and flips expert choice (a
176///     class change, not an FP-order change). llama.cpp keeps every router F32 (its converter
177///     forces F32) so Float is bench-parity, it sits on NO all-or-nothing predicate, and the
178///     decode-exact contract is already built around its cuBLASLt path
179///     (hybrid_forward.rs moe_ffn_sequential_zq8 router comment).
180fn float_2d_audited(name: &str) -> bool {
181    name.ends_with("ffn_gate_inp.weight")
182}
183
184/// Once-per-name-pattern loader-law warning (`blk.{il}.` collapses to `blk.*.` so a 48-layer
185/// offender prints ONE line, not 48). See the call site in `load_from_source` for the law.
186fn warn_float_2d_once(name: &str, ne: &[u64], src_type: GgmlType) {
187    use std::sync::{Mutex, OnceLock};
188    static SEEN: OnceLock<Mutex<std::collections::HashSet<String>>> = OnceLock::new();
189    let pat = match name.strip_prefix("blk.").and_then(|r| r.split_once('.')) {
190        Some((_, suffix)) => format!("blk.*.{suffix}"),
191        None => name.to_string(),
192    };
193    let mut seen = SEEN
194        .get_or_init(|| Mutex::new(std::collections::HashSet::new()))
195        .lock()
196        .unwrap();
197    if seen.insert(pat.clone()) {
198        eprintln!(
199            "[loader-law] WARNING: {pat} loads as 2D Float ne={ne:?} (src {src_type:?}) — \
200                   a Float matmul weight rides cuBLAS f32 GEMV and poisons all-or-nothing q8-fast \
201                   predicates (uses_q8_1_fast/mixer_in_q8_1_fast). If matmul-class: Q8_0-encode at \
202                   load (model.rs ssm arm / source.rs BF16+F8 gates). If deliberately Float: add \
203                   it to float_2d_audited with the audit rationale."
204        );
205    }
206}
207
208/// CUTLASS-layout NVFP4 weight (B operand) for the prefill FP4 GEMM. Built once at load from the raw
209/// GGUF bytes (de-interleave + SFB swizzle). Coexists with the raw `bytes` (decode reads bytes).
210#[cfg(memra_cutlass)]
211pub struct CutlassWeight {
212    /// Plain K-contiguous packed e2m1, [out_f, in_f/2] bytes.
213    pub b_packed: CudaSlice<u8>,
214    /// Swizzled SFB (CUTLASS SfAtom layout), sized via cutlass_sfb_size(out_f, in_f).
215    pub sfb_swizzled: CudaSlice<u8>,
216}
217
218impl GpuTensor {
219    pub fn ne(&self) -> &[u64] {
220        match self {
221            GpuTensor::Quant { ne, .. } => ne,
222            GpuTensor::Float { ne, .. } => ne,
223            GpuTensor::FloatBf16 { ne, .. } => ne,
224        }
225    }
226    pub fn in_features(&self) -> usize {
227        self.ne()[0] as usize
228    }
229    pub fn out_features(&self) -> usize {
230        self.ne()[1] as usize
231    }
232    /// Per-tensor post-matmul macro-scale (NVFP4 carries scale != 1.0; all others -> 1.0, a no-op).
233    /// Used by the fused SwiGLU epilogue to fold the gate/up scale into one kernel.
234    pub fn scale(&self) -> f32 {
235        match self {
236            GpuTensor::Quant { scale, .. } => *scale,
237            GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => 1.0,
238        }
239    }
240
241    /// Load a tensor, keeping quant types packed and float types as f32. (GGUF entry point —
242    /// thin wrapper over the source-agnostic `load_from_source`; behavior is unchanged.)
243    pub fn load(e: &Engine, g: &GgufFile, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
244        Self::load_from_source(e, &GgufSource(g), name)
245    }
246
247    /// Source-agnostic load: works from any `TensorSource` (GGUF or safetensors). The engine's
248    /// forward graph only ever asks for ggml-style names; the source maps them to its own layout.
249    pub fn load_from_source(
250        e: &Engine,
251        src: &dyn TensorSource,
252        name: &str,
253    ) -> Result<Self, Box<dyn std::error::Error>> {
254        // A1 DIRECT NVFP4 IMPORT (2026-07-04): a PLAIN modelopt/Reza NVFP4 weight from a
255        // safetensors source repacks straight into the A6 split-plane resident layout in ONE host
256        // pass (nvfp4_repack::repack_modelopt_to_split — the scale plane is the file's
257        // weight_scale bytes verbatim), never materializing the GGUF 36B-block intermediate.
258        // The GGUF hop remains only for MEMRA_ST_DIRECT=0 (rollback/A-B seam — byte-identical
259        // resident weights either way), MEMRA_RP=0, the hybrid V-reorder transforms, and the
260        // opt-in CUTLASS resident operand (which is built from raw GGUF-layout bytes).
261        let cutlass_wants_raw = cfg!(memra_cutlass) && std::env::var("MEMRA_FP4_CUTLASS").is_ok();
262        let st_direct = std::env::var("MEMRA_ST_DIRECT")
263            .map(|v| v != "0")
264            .unwrap_or(true);
265        if rp_enabled() && st_direct && !cutlass_wants_raw {
266            if let Some(nv) = src.find_nvfp4_native(name) {
267                if nv.in_f % 64 == 0 && nv.out_f > 0 {
268                    // Same post-matmul macro-scale sibling lookup as the GGUF-layout arm below.
269                    let stem = name.strip_suffix(".weight").unwrap_or(name);
270                    let scale = match src.find(&format!("{stem}.scale")) {
271                        Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
272                        None => 1.0,
273                    };
274                    let bytes =
275                        e.htod_bytes(&memra_gguf::nvfp4_repack::repack_modelopt_to_split(
276                            nv.wbytes, nv.wscale, nv.out_f, nv.in_f,
277                        ))?;
278                    return Ok(GpuTensor::Quant {
279                        bytes,
280                        qtype: QT_NVFP4,
281                        row_bytes: nv.in_f / 64 * 36,
282                        ne: vec![nv.in_f as u64, nv.out_f as u64],
283                        scale,
284                        rp: true,
285                        #[cfg(memra_cutlass)]
286                        cutlass: None,
287                        fp8: None, f16: None,
288                        rp4: None,
289                    });
290                }
291            }
292        }
293        // E4M3-DIRECT (MEMRA_ST_E4M3=1, lane e4m3dec 2026-07-08): F8-E4M3-origin 2D projections keep
294        // the checkpoint's RAW e4m3 device bytes + per-tensor weight_scale as the ONE resident copy
295        // (QT_F8_E4M3) instead of the Q8_0 re-encode — decode dequants e4m3 in-kernel
296        // (qmatvec_e4m3_mmvq, the checkpoint's own precision, no lossy re-quant hop), prefill
297        // (m>=16) rides the cuBLASLt FP8 GEMM on the SAME bytes (try_fp8_gemm). Frees the Q8_0
298        // duplicate the MEMRA_PP_FP8 stash needed (~3.4GB on the NV-27B) — full FP8 prefill coverage
299        // with no VRAM budget. Placed BEFORE `find` so the host-side F8->Q8_0 re-encode is skipped
300        // entirely (faster load). in_f%32 is the q8_1 activation block gate (every F8 projection in
301        // the NV-27B satisfies it; a violator falls through to the Q8_0 arm unchanged).
302        // BLOCK-128 GATE: the QT_F8_E4M3 decode kernel family (qmatvec_e4m3_mmvq + batched
303        // twins) consumes ONE scalar weight scale — dispatching a block-128 operand through it
304        // would silently dequant every tile with scale 1.0. Until the per-block-dequant mmvq
305        // twin lands (DECISION.md B1 second half), block-128 tensors fall through to the Q8_0
306        // re-encode (correct floor); their raw bytes+scales still go resident via the
307        // MEMRA_PP_FP8 stash arm below for the P1 GEMM work.
308        if crate::fp8_ffi::st_e4m3_enabled() {
309            if let Some(f8) = src.find_fp8_native(name) {
310                if f8.blk.is_none() && f8.in_f % 32 == 0 && f8.out_f > 0 {
311                    return Ok(GpuTensor::Quant {
312                        bytes: e.htod_bytes(&f8.bytes)?,
313                        qtype: crate::QT_F8_E4M3,
314                        row_bytes: f8.in_f,
315                        ne: vec![f8.in_f as u64, f8.out_f as u64],
316                        scale: f8.scale,
317                        rp: false,
318                        #[cfg(memra_cutlass)]
319                        cutlass: None,
320                        fp8: None, f16: None,
321                        rp4: None,
322                    });
323                }
324            }
325        }
326        // ARM B' — GPU BLOCK-128 DEQUANT (MEMRA_FP8_BLK_GPU=1, default OFF; lane fp8-gemm-arm
327        // 2026-08-03). A block-128 FP8 checkpoint (Qwen official FP8 / DeepSeek-V3 lineage)
328        // currently loads via the host path: full f32 dequant of the tensor (f8_deq_f32) then a
329        // host Q8_0 re-encode (f32_to_q8_0) — correct, but a serial CPU pass over every byte of
330        // every projection. This arm does the same math on the GPU in ONE pass
331        // (cu/fp8_blk_dequant.cu): upload the raw e4m3 codes + the scale grid, write Q8_0
332        // blocks directly. BYTE-IDENTICAL to the host path (kernel-check [fp8-blk-gpu] arm
333        // asserts it on ragged and aligned shapes), so the resident tensor, the MMQ/MMVQ
334        // dispatch, and decode are all bit-for-bit unchanged — this is a LOAD-TIME
335        // optimization only, not a numeric config change.
336        //
337        // Placed BEFORE `find` for exactly the reason the MEMRA_ST_E4M3 arm above is: `find`
338        // would otherwise do the host dequant+re-encode we are replacing. Per-tensor and
339        // per-row scale classes are NOT touched (find_fp8_native returns blk=None / None for
340        // them) and neither are V-reorder Transform targets (find_fp8_native rejects those with
341        // a grid — the permutation invalidates the on-disk grid, so they keep the host path).
342        if crate::fp8_ffi::fp8_blk_gpu_enabled() && !crate::fp8_ffi::st_e4m3_enabled() {
343            if let Some(f8) = src.find_fp8_native(name) {
344                if let Some(grid) = f8.blk.as_ref() {
345                    let (in_f, out_f) = (f8.in_f, f8.out_f);
346                    if in_f % 32 == 0 && out_f > 0 && f8.bytes.len() == out_f * in_f {
347                        let bytes =
348                            e.fp8_blk_dequant_q8_0(&f8.bytes, &grid.scales, out_f, in_f)?;
349                        return Ok(GpuTensor::Quant {
350                            bytes,
351                            qtype: QT_Q8_0,
352                            row_bytes: in_f / 32 * 34,
353                            ne: vec![in_f as u64, out_f as u64],
354                            scale: 1.0,
355                            rp: false,
356                            #[cfg(memra_cutlass)]
357                            cutlass: None,
358                            fp8: None, f16: None,
359                            rp4: None,
360                        });
361                    }
362                }
363            }
364        }
365        let mut v = src
366            .find(name)
367            .unwrap_or_else(|| panic!("missing tensor {name}"));
368        // MEMRA_KQ_NVFP4=1 (opt-in, 2026-07-08): re-encode Q4_K/Q5_K 2D matmul weights to NVFP4 at
369        // load. The k-quant mmvq family runs at 61-70% of the bandwidth wall on this rig (measured
370        // BOTH engines — the kernels share ancestry) while the in-house NVFP4 path runs at 96%.
371        // The daily GGUF's quant mix was chosen for llama's kernels, not ours: Q4_K -> NVFP4 is
372        // 4-bit -> 4-bit at +26pp kernel efficiency; Q5_K -> NVFP4 also drops bytes (0.69 -> 0.56
373        // B/w) at a small real re-quant cost (5 -> 4 bit; gates + acceptance arbitrate). Q6_K/Q8_0
374        // excluded (6/8-bit -> 4-bit is a real quality cliff — the lm_head stays untouched).
375        // MEMRA_KQ_NVFP4 (opt-in SPEED-OVER-QUALITY mode, measured 2026-07-08 on the 9B):
376        // =2 (Q4_K+Q5_K -> NVFP4): +3.9% plain decode (129.5 -> 134.5, the Q5 bytes win),
377        //    acceptance tax ~3pts on hard content (p2 74.0 -> 70.7, p3 66.9 -> 64.9).
378        // =1 (Q4_K only): NO perf gain AND still ~3pts tax — Q4_K is ASYMMETRIC (6-bit
379        //    scale+min per 32); NVFP4 is symmetric e2m1: dropping the zero-point is real
380        //    error even 4-bit -> 4-bit. The "same bpw = same class" assumption is FALSE
381        //    across asymmetric/symmetric formats. Kept only for the record.
382        let kq = std::env::var("MEMRA_KQ_NVFP4")
383            .ok()
384            .and_then(|x| x.parse::<u8>().ok())
385            .unwrap_or(0);
386        if (kq >= 1 && v.ggml_type == GgmlType::Q4_K || kq >= 2 && v.ggml_type == GgmlType::Q5_K)
387            && v.ne.len() == 2
388            && v.ne[0] % 64 == 0
389            && !name.starts_with("output")
390        {
391            let n: u64 = v.ne.iter().product();
392            let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n as usize);
393            let packed = memra_gguf::nvfp4_repack::f32_to_nvfp4(&f32v);
394            v = memra_gguf::source::TensorView {
395                bytes: std::borrow::Cow::Owned(packed),
396                ggml_type: GgmlType::NVFP4,
397                ne: v.ne.clone(),
398            };
399        }
400        let qtype = match v.ggml_type {
401            GgmlType::Q8_0 => Some(QT_Q8_0),
402            GgmlType::Q4_K => Some(QT_Q4_K),
403            GgmlType::Q6_K => Some(QT_Q6_K),
404            GgmlType::Q5_K => Some(QT_Q5_K),
405            GgmlType::Q3_K => Some(QT_Q3_K),
406            GgmlType::IQ4_XS => Some(QT_IQ4_XS),
407            GgmlType::IQ3_S => Some(QT_IQ3_S),
408            GgmlType::NVFP4 => Some(QT_NVFP4),
409            GgmlType::Q4_0 => Some(QT_Q4_0),
410            // F32/F16/BF16 (the dtypes safetensors carries) -> Float path below.
411            _ => None,
412        };
413        match qtype {
414            Some(qt) => {
415                let out_f = v.ne[1] as usize;
416                let row_bytes = v.bytes.len() / out_f;
417                // NVFP4 two-level scale: per-16 ue4m3 micro-scale is in the dequant; the per-tensor
418                // F32 macro-scale lives in a sibling "<stem>.scale" tensor, applied POST-matmul
419                // (llama build_lora_mm: ggml_mul(res, w_s)). ".input_scale" is the W4A4 activation
420                // scale — UNUSED on our W4A16/f32 path. Only NVFP4 carries it; others -> 1.0 (no-op).
421                let scale = if qt == QT_NVFP4 {
422                    let stem = name.strip_suffix(".weight").unwrap_or(name);
423                    match src.find(&format!("{stem}.scale")) {
424                        Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
425                        None => 1.0,
426                    }
427                } else {
428                    1.0
429                };
430                // A6 SPLIT-PLANE repack: NVFP4 2-D matmul weights upload in walk-order layout
431                // (host-side permutation before htod — zero VRAM spike, layer-streamed by
432                // construction). Every consumer kernel dispatches its `_rp` twin off the flag.
433                let rp = qt == QT_NVFP4
434                    && v.ne.len() == 2
435                    && (v.ne[0] as usize) % 64 == 0
436                    && v.bytes.len() % out_f == 0
437                    && (v.bytes.len() / out_f) % 36 == 0
438                    && rp_enabled();
439                let bytes = if rp {
440                    e.htod_bytes(&repack_nvfp4_split(&v.bytes, out_f))?
441                } else {
442                    e.htod_bytes(&v.bytes)?
443                };
444                // CUTLASS NVFP4 prefill operand, built from the RAW GGUF bytes (a temp raw upload
445                // when the resident `bytes` are repacked). Gated: only NVFP4 weights, only when
446                // MEMRA_FP4_CUTLASS is set, only under cfg(memra_cutlass). in_f%64==0 is the NVFP4
447                // K-block constraint (same as the dispatch).
448                #[cfg(memra_cutlass)]
449                let cutlass = {
450                    let in_f = v.ne[0] as usize;
451                    // Skip the resident repack when OTF is requested (per-call repack instead) — the
452                    // resident path ~doubles NVFP4 weight VRAM and OOMs larger models (e.g. 27B/24GB).
453                    if qt == QT_NVFP4
454                        && in_f % 64 == 0
455                        && v.ne.len() == 2
456                        && std::env::var("MEMRA_FP4_CUTLASS").is_ok()
457                        && std::env::var("MEMRA_FP4_CUTLASS_OTF").is_err()
458                    {
459                        let raw_dev;
460                        let src_dev = if rp {
461                            raw_dev = e.htod_bytes(&v.bytes)?;
462                            &raw_dev
463                        } else {
464                            &bytes
465                        };
466                        let (b_packed, sfb_swizzled) =
467                            e.build_cutlass_weight(src_dev, out_f, in_f, row_bytes)?;
468                        Some(CutlassWeight {
469                            b_packed,
470                            sfb_swizzled,
471                        })
472                    } else {
473                        None
474                    }
475                };
476                // FP8-ACT PREFILL operand (MEMRA_PP_FP8=1): for F8-E4M3-sourced projections (they
477                // surface as Q8_0 from the source's re-encode) ALSO stash the raw e4m3 device
478                // bytes + weight_scale. The source guarantees byte order matches `v` (the
479                // Transform arm's V-reorder is baked into both); the ne check guards a mixup.
480                // VRAM BUDGET (24GB rigs, 2026-07-08): the stash duplicates every F8-origin
481                // projection (~+3.4GB on the 27B) — fine on the 96GB box, OOM here. The stash
482                // spends from MEMRA_PP_FP8_BUDGET_MB (default 1536); once spent, remaining
483                // tensors ride the old path. Load order is layer order, so the budget covers a
484                // PREFIX of layers — coverage (and the prefill win) scales with the budget.
485                // MEMRA_FP8_MMQ=1 (lane/fp8-mmq) admits the SAME stash for the block-128 class:
486                // the per-block MMQ prefill kernel is that class's consumer, and it needs exactly
487                // what this arm makes resident (raw e4m3 bytes + the verbatim f32 grid). It shares
488                // the budget accounting below, so a 24GB rig still caps the duplicate.
489                let fp8 = if qt == QT_Q8_0
490                    && (crate::fp8_ffi::pp_fp8_enabled() || crate::fp8_ffi::fp8_mmq_enabled())
491                {
492                    match src.find_fp8_native(name) {
493                        Some(f8)
494                            if v.ne.len() == 2
495                                && f8.in_f as u64 == v.ne[0]
496                                && f8.out_f as u64 == v.ne[1] =>
497                        {
498                            use std::sync::atomic::{AtomicUsize, Ordering};
499                            static FP8_SPENT: AtomicUsize = AtomicUsize::new(0);
500                            static FP8_BUDGET: std::sync::OnceLock<usize> =
501                                std::sync::OnceLock::new();
502                            let budget = *FP8_BUDGET.get_or_init(|| {
503                                std::env::var("MEMRA_PP_FP8_BUDGET_MB")
504                                    .ok()
505                                    .and_then(|v| v.parse::<usize>().ok())
506                                    .unwrap_or(1536)
507                                    << 20
508                            });
509                            let sz = f8.bytes.len();
510                            if FP8_SPENT.fetch_add(sz, Ordering::Relaxed) + sz <= budget {
511                                // Block-128 grid rides along resident (checkpoint order,
512                                // Fp8BlockScales layout contract). try_fp8_gemm still skips blk
513                                // operands (cuBLASLt takes no block grid on sm_120, P1-VERDICT);
514                                // try_fp8_blk_mmq is their consumer under MEMRA_FP8_MMQ=1.
515                                let blk = match f8.blk {
516                                    Some(g) => Some(Fp8BlockScales {
517                                        scales: e.htod(&g.scales)?,
518                                        rows: g.rows,
519                                        cols: g.cols,
520                                    }),
521                                    None => None,
522                                };
523                                Some(Fp8Weight {
524                                    bytes: e.htod_bytes(&f8.bytes)?,
525                                    scale: f8.scale,
526                                    blk,
527                                })
528                            } else {
529                                FP8_SPENT.fetch_sub(sz, Ordering::Relaxed);
530                                None
531                            }
532                        }
533                        _ => None,
534                    }
535                } else {
536                    None
537                };
538                Ok(GpuTensor::Quant {
539                    bytes,
540                    qtype: qt,
541                    row_bytes,
542                    ne: v.ne.clone(),
543                    scale,
544                    rp,
545                    #[cfg(memra_cutlass)]
546                    cutlass,
547                    fp8,
548                    rp4: None,
549                    f16: None,
550                })
551            }
552            None => {
553                let n: u64 = v.ne.iter().product();
554                // FULL-PRECISION MODE (MEMRA_FULL_PREC): NO re-encodes. Large 2D bf16 matmul weights
555                // stay bf16-resident (FloatBf16, dequant-on-use) so the trunk fits VRAM; everything
556                // else (small 2D, 1D norms, F16/F32) rides the exact f32 Float path below. The ssm
557                // Q8_0 re-encode and the Float-poison tripwire are BYPASSED here (both are the loader
558                // law this mode exists to suspend — the warnings would be correct but noise).
559                if full_prec_enabled() {
560                    // Only bf16 sources take the resident-bf16 arm; F16/F32 fall through to f32 Float
561                    // (exact, and tiny/absent in the bf16 ST checkpoints this mode targets). The 1M
562                    // threshold keeps small tensors (norms, gate_inp) on the proven f32 path — only
563                    // the big trunk matrices need the 2 B/w VRAM saving.
564                    if v.ggml_type == GgmlType::BF16 && v.ne.len() == 2 && n >= 1_000_000 {
565                        let data = e.htod_bytes(&v.bytes)?; // raw bf16 bytes, u16 LE pairs
566                        return Ok(GpuTensor::FloatBf16 {
567                            data,
568                            ne: v.ne.clone(),
569                        });
570                    }
571                    let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n as usize);
572                    return Ok(GpuTensor::Float {
573                        data: e.htod(&f32v)?,
574                        ne: v.ne.clone(),
575                    });
576                }
577                let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n as usize);
578                // ssm_beta/ssm_alpha stored F32 (the 35B GGUF): Q8_0-encode at load. F32 here
579                // fails `mixer_in_q8_1_fast` for the whole linear-attn mixer -> every linear
580                // layer falls off the fused norm+quantize chain onto cuBLAS f32 GEMV pairs
581                // (the NV-27B in_proj_a/b lesson, same all-or-nothing capability check; nsys
582                // 35B: 100 dot+reduce launches/token). Q8_0 of an F32 source is the same
583                // class-lossless step every 9B GGUF already ships for these tensors.
584                if v.ne.len() == 2
585                    && v.ne[0] % 32 == 0
586                    && (name.ends_with("ssm_beta.weight") || name.ends_with("ssm_alpha.weight")
587                        // E4B per_layer_model_proj (F16 [2560, 10752]): matmul-class — the
588                        // loader-law recipe (2026-07-12). As Float it rode cuBLAS f32 whose
589                        // m=1-vs-m=16 FP-order gap seeds inp_pl noise into EVERY layer's PLE
590                        // tail; the 42-layer stack amplifies it to logit maxdiff ~27 and the
591                        // chat-prompt prefill-vs-decode argmax gate fails.
592                        || name.ends_with("per_layer_model_proj.weight"))
593                {
594                    let q8 = memra_gguf::nvfp4_repack::f32_to_q8_0(&f32v);
595                    return GpuTensor::from_quant_bytes(
596                        e,
597                        &q8,
598                        GgmlType::Q8_0,
599                        v.ne[0],
600                        v.ne[1],
601                        1.0,
602                    );
603                }
604                // LOADER-LAW TRIPWIRE (loadersweep 2026-07-08): a 2D Float tensor with both dims
605                // >= 16 is almost certainly MATMUL-class, and a Float matmul weight (a) rides
606                // cuBLAS f32 GEMV pairs (dot_kernel + reduce_1Block in nsys) and (b) fails
607                // uses_q8_1_fast, poisoning every ALL-OR-NOTHING fast-path predicate it sits on
608                // (mixer_in_q8_1_fast etc.) — the trap that cost measurable perf 4 times (NV-27B
609                // in_proj_a/b BF16, 35B ssm_beta/alpha F32, M3 shexp cousin, M3 BF16 lm_head).
610                // Fix recipe: name-gated f32_to_q8_0 encode at load (see the ssm arm above /
611                // source.rs BF16+F8 gates). Norm-class tensors are 1D or have a dim < 16
612                // (conv1d ne[0]=4) and never reach this warning.
613                if v.ne.len() == 2 && v.ne[0] >= 16 && v.ne[1] >= 16 && !float_2d_audited(name) {
614                    warn_float_2d_once(name, &v.ne, v.ggml_type);
615                }
616                // F32/F16/BF16 (or as-yet-unhandled quant): dequant to f32. Small tensors only.
617                Ok(GpuTensor::Float {
618                    data: e.htod(&f32v)?,
619                    ne: v.ne.clone(),
620                })
621            }
622        }
623    }
624
625    /// Build a Quant tensor directly from raw ggml block bytes (FR-Spec self-trim: byte-level row
626    /// gather from an already-loaded weight — rows in every ggml quant are independent, so a
627    /// contiguous per-row byte copy is a lossless "trim"). `ne0` = in_features, `ne1` = rows.
628    pub fn from_quant_bytes(
629        e: &Engine,
630        bytes: &[u8],
631        ty: GgmlType,
632        ne0: u64,
633        ne1: u64,
634        scale: f32,
635    ) -> Result<Self, Box<dyn std::error::Error>> {
636        let qt = match ty {
637            GgmlType::Q8_0 => QT_Q8_0,
638            GgmlType::Q4_K => QT_Q4_K,
639            GgmlType::Q6_K => QT_Q6_K,
640            GgmlType::Q5_K => QT_Q5_K,
641            GgmlType::Q3_K => QT_Q3_K,
642            GgmlType::IQ4_XS => QT_IQ4_XS,
643            GgmlType::IQ3_S => QT_IQ3_S,
644            GgmlType::NVFP4 => QT_NVFP4,
645            GgmlType::Q4_0 => QT_Q4_0,
646            other => panic!("from_quant_bytes: unsupported dtype {other:?}"),
647        };
648        let row_bytes = bytes.len() / ne1 as usize;
649        // Same A6 repack as load_from_source: callers pass GGUF-layout host bytes (the FR-Spec
650        // self-trim row-gathers from the source file bytes, which are always original layout).
651        let rp = qt == QT_NVFP4 && ne0 % 64 == 0 && row_bytes % 36 == 0 && rp_enabled();
652        let dev = if rp {
653            e.htod_bytes(&repack_nvfp4_split(bytes, ne1 as usize))?
654        } else {
655            e.htod_bytes(bytes)?
656        };
657        Ok(GpuTensor::Quant {
658            bytes: dev,
659            qtype: qt,
660            row_bytes,
661            ne: vec![ne0, ne1],
662            scale,
663            rp,
664            #[cfg(memra_cutlass)]
665            cutlass: None,
666            fp8: None, f16: None,
667            rp4: None,
668        })
669    }
670
671    pub fn load_opt(
672        e: &Engine,
673        g: &GgufFile,
674        name: &str,
675    ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
676        Self::load_opt_from_source(e, &GgufSource(g), name)
677    }
678
679    pub fn load_opt_from_source(
680        e: &Engine,
681        src: &dyn TensorSource,
682        name: &str,
683    ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
684        if src.has(name) {
685            Ok(Some(Self::load_from_source(e, src, name)?))
686        } else {
687            Ok(None)
688        }
689    }
690
691    /// Accessor for tensors that MUST be f32 (norm weights). Panics if quantized.
692    pub fn float_data(&self) -> &CudaSlice<f32> {
693        match self {
694            GpuTensor::Float { data, .. } => data,
695            GpuTensor::Quant { .. } => panic!("expected float tensor (norm), got quantized"),
696            GpuTensor::FloatBf16 { .. } => {
697                panic!("expected f32 float tensor (norm), got bf16-resident matmul weight")
698            }
699        }
700    }
701}
702
703pub struct Layer {
704    pub attn_norm: GpuTensor,
705    pub wq: GpuTensor,
706    pub wk: GpuTensor,
707    pub wv: GpuTensor,
708    pub wo: GpuTensor,
709    pub q_norm: Option<GpuTensor>,
710    pub k_norm: Option<GpuTensor>,
711    pub ffn_norm: GpuTensor,
712    /// FFN: dense SwiGLU or routed MoE (OLMoE — dense attention + MoE FFN). Reuses the hybrid
713    /// `Ffn` enum + `load_ffn` so the routed-expert forward is shared with `HybridModel::moe_ffn`.
714    pub ffn: crate::hybrid::Ffn,
715}
716
717/// Host-resident embedding table for row gather (dequant only the needed token rows).
718pub struct EmbedHost {
719    pub raw: Vec<u8>,
720    pub ggml_type: GgmlType,
721    pub n_embd: usize,
722}
723impl EmbedHost {
724    pub fn from_gguf(g: &GgufFile, name: &str) -> Self {
725        Self::from_source(&GgufSource(g), name)
726    }
727    pub fn from_source(src: &dyn TensorSource, name: &str) -> Self {
728        let v = src
729            .find(name)
730            .unwrap_or_else(|| panic!("missing embed {name}"));
731        EmbedHost {
732            raw: v.bytes.to_vec(),
733            ggml_type: v.ggml_type,
734            n_embd: v.ne[0] as usize,
735        }
736    }
737    /// QT int + row_bytes for this embed table's dtype (for the device embed-gather kernel).
738    /// CUDA-GRAPH-PLAN Phase 1. Mirrors the GpuTensor qtype mapping.
739    pub fn qt_and_row_bytes(&self, n_embd: usize) -> (i32, usize) {
740        let (blk, tsize) = self.ggml_type.block_and_type_size();
741        let row_bytes = (n_embd as u64 / blk * tsize) as usize;
742        let qt = match self.ggml_type {
743            GgmlType::Q8_0 => QT_Q8_0,
744            GgmlType::Q4_K => QT_Q4_K,
745            GgmlType::Q6_K => QT_Q6_K,
746            GgmlType::Q5_K => QT_Q5_K,
747            GgmlType::Q3_K => QT_Q3_K,
748            GgmlType::IQ4_XS => QT_IQ4_XS,
749            GgmlType::IQ3_S => QT_IQ3_S,
750            GgmlType::NVFP4 => QT_NVFP4,
751            GgmlType::F32 => QT_F32,
752            // BF16 embed table (FULL_PREC research mode: qwen35-9b-hf) — device gather does the
753            // exact bits<<16 expansion; 2 B/elem resident instead of an f32-doubled table.
754            GgmlType::BF16 => QT_BF16,
755            other => panic!("embed_gather: unsupported dtype {other:?}"),
756        };
757        (qt, row_bytes)
758    }
759
760    /// Gather rows for tokens -> [T, n_embd] f32. Dequant per-row from raw bytes.
761    pub fn gather(&self, n_embd: usize, tokens: &[u32]) -> Vec<f32> {
762        let (blk, tsize) = self.ggml_type.block_and_type_size();
763        let row_bytes = (n_embd as u64 / blk * tsize) as usize;
764        let mut x = vec![0f32; tokens.len() * n_embd];
765        for (ti, &tok) in tokens.iter().enumerate() {
766            let off = tok as usize * row_bytes;
767            let row = dequant::dequantize(self.ggml_type, &self.raw[off..off + row_bytes], n_embd);
768            x[ti * n_embd..ti * n_embd + n_embd].copy_from_slice(&row);
769        }
770        x
771    }
772}
773
774pub struct Model {
775    pub cfg: ModelConfig,
776    pub embd: EmbedHost,
777    pub output_norm: GpuTensor,
778    pub output: GpuTensor,
779    pub layers: Vec<Layer>,
780}
781
782impl Model {
783    /// Load a dense (vanilla-transformer) model from GGUF. Thin wrapper over
784    /// `load_dense_from_source`. Panics if the arch has SSM/MoE layers.
785    pub fn load_dense(e: &Engine, g: &GgufFile) -> Result<Self, Box<dyn std::error::Error>> {
786        Self::load_dense_from_source(e, &GgufSource(g))
787    }
788
789    /// Load a dense-attention model from any `TensorSource` — GGUF or a safetensors HF checkpoint.
790    /// The whole loop speaks ggml names; the source maps them. The FFN is dense SwiGLU OR routed MoE
791    /// (OLMoE: dense full-attention + MoE FFN). Panics on hybrid (SSM) arches — use the hybrid path.
792    pub fn load_dense_from_source(
793        e: &Engine,
794        src: &dyn TensorSource,
795    ) -> Result<Self, Box<dyn std::error::Error>> {
796        let cfg = src.config();
797        assert!(
798            cfg.full_attention_interval == 0,
799            "model has linear-attn layers; use hybrid path"
800        );
801        // FP8-KV per-model door: OFF everywhere by default (explicit MEMRA_KV_FP8 wins).
802        // The 2026-07-12 9B "+0.7-4% scaling with depth" did NOT reproduce on the
803        // 2026-07-28 build (12k A/B: fp8 117.0/118.2 vs q8 119.3/119.2 = −1%; d1736
804        // flat; the fa-v3/f16pv/PDL stack moved underneath it). Adoption reverted by
805        // measurement — fp8-KV's remaining value is bytes (~45% smaller KV) for
806        // ctx-limited serving, not speed. Gates all green under both formats.
807        crate::KV_FP8_FORCE.store(0, std::sync::atomic::Ordering::Relaxed);
808
809        let embd = EmbedHost::from_source(src, "token_embd.weight");
810        let output_norm = GpuTensor::load_from_source(e, src, "output_norm.weight")?;
811        // tied embeddings: fall back to tok_embd if output.weight absent (OLMoE has untied output).
812        let output = if src.has("output.weight") {
813            GpuTensor::load_from_source(e, src, "output.weight")?
814        } else {
815            GpuTensor::load_from_source(e, src, "token_embd.weight")?
816        };
817
818        let mut layers = Vec::with_capacity(cfg.n_layer as usize);
819        for il in 0..cfg.n_layer {
820            let p = |s: &str| format!("blk.{il}.{s}");
821            let hy3_dense_ffn = cfg
822                .hy3
823                .as_ref()
824                .is_some_and(|h| il < h.first_k_dense_replace);
825            let ffn = if hy3_dense_ffn {
826                crate::hybrid::Ffn::Dense {
827                    ffn_gate: GpuTensor::load_from_source(e, src, &p("ffn_gate.weight"))?,
828                    ffn_up: GpuTensor::load_from_source(e, src, &p("ffn_up.weight"))?,
829                    ffn_down: GpuTensor::load_from_source(e, src, &p("ffn_down.weight"))?,
830                }
831            } else {
832                crate::hybrid::load_ffn(e, src, &cfg, il, None)?
833            };
834            layers.push(Layer {
835                attn_norm: GpuTensor::load_from_source(e, src, &p("attn_norm.weight"))?,
836                wq: GpuTensor::load_from_source(e, src, &p("attn_q.weight"))?,
837                wk: GpuTensor::load_from_source(e, src, &p("attn_k.weight"))?,
838                wv: GpuTensor::load_from_source(e, src, &p("attn_v.weight"))?,
839                wo: GpuTensor::load_from_source(e, src, &p("attn_output.weight"))?,
840                q_norm: GpuTensor::load_opt_from_source(e, src, &p("attn_q_norm.weight"))?,
841                k_norm: GpuTensor::load_opt_from_source(e, src, &p("attn_k_norm.weight"))?,
842                ffn_norm: GpuTensor::load_from_source(e, src, &p("ffn_norm.weight"))?,
843                ffn,
844            });
845        }
846        Ok(Model {
847            cfg,
848            embd,
849            output_norm,
850            output,
851            layers,
852        })
853    }
854
855    /// Largest expert block (bytes) across all MoE layers — the fixed cache-slot size (mirrors
856    /// `HybridModel::max_moe_block`). 0 for a dense (non-MoE) model.
857    pub(crate) fn max_moe_block(&self) -> usize {
858        use crate::hybrid::Ffn;
859        let mut mx = 0usize;
860        for l in &self.layers {
861            if let Ffn::Moe(m) = &l.ffn {
862                mx = mx
863                    .max(m.gate_exps.max_expert_bytes())
864                    .max(m.up_exps.max_expert_bytes())
865                    .max(m.down_exps.max_expert_bytes());
866            }
867        }
868        mx
869    }
870
871    /// Gather embedding rows into f32 [T, n_embd] (token-major) by dequantizing only the needed
872    /// rows from the host-side embedding bytes (token_embd is [n_embd, n_vocab], row per token).
873    pub fn embed_tokens(
874        &self,
875        e: &Engine,
876        tokens: &[u32],
877    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
878        let n_embd = self.cfg.n_embd as usize;
879        let x = self.embd.gather(n_embd, tokens);
880        Ok(e.htod(&x)?)
881    }
882}
883
884pub type TensorMap = HashMap<String, GpuTensor>;
885
886/// One layer's stacked 256-expert tensor, raw GGUF quant bytes held HOST-RESIDENT.
887///
888/// EDGE-1: these bytes are NEVER uploaded at load (uploading 29.75GB would OOM a 24GB GPU —
889/// this is BUG-4). Per token, only the 8 routed experts are staged H2D into a small GPU scratch.
890///
891/// ne = [in_f, out_f, n_expert]; the expert axis (ne[2]) is the slowest/highest-stride axis, so
892/// expert `e` occupies the CONTIGUOUS byte block `bytes[e*expert_stride .. (e+1)*expert_stride]`.
893///
894/// THE 3D FIX: GpuTensor::load computes `row_bytes = raw.len()/ne[1]`, which for a stacked 3D
895/// tensor ignores the 256-expert axis and is 256x too large (gate_exps -> 430080 instead of 1680).
896/// load() here uses `row_bytes = raw.len() / (out_f * n_expert)` (= 1680 gate/up, 544 down).
897/// Host byte storage for the expert blocks. Default = a pageable `Vec<u8>` (current behavior). Under
898/// MEMRA_MOE_PINNED (auto-on when MEMRA_MOE_CACHE is set), the bytes live in CUDA pinned host memory so
899/// the miss-path `memcpy_htod` is a true DMA, not a pageable bounce copy (MOE-SLRU-PLAN §C.1).
900///
901/// CAVEAT (§C.1): `alloc_pinned` uses CU_MEMHOSTALLOC_WRITECOMBINED — great for H2D-only (the expert
902/// bytes are never read by the CPU on the hot path), but write-combined memory is SLOW for CPU reads.
903/// A future CPU-VNNI cold-expert fallback must NOT read from this buffer.
904pub enum HostBuf {
905    Paged(Vec<u8>),
906    /// Pinned host memory. We keep the `PinnedHostSlice` alive (it owns the allocation; Drop frees it)
907    /// AND cache its raw base pointer + len so the hot-path `as_bytes()` needs no per-call event sync.
908    Pinned {
909        slice: std::sync::Arc<cudarc::driver::PinnedHostSlice<u8>>,
910        base: *const u8,
911        len: usize,
912    },
913    /// Alias into a shared pinned slab (ST pinned tier): `owner` keeps the slab alive; `base`/`len`
914    /// select this expert's window. Same DMA class as `Pinned`.
915    PinnedAlias {
916        owner: std::sync::Arc<HostBuf>,
917        base: *const u8,
918        len: usize,
919    },
920    /// SPILLING-PLAN §1, Tier 2 (disk): the bytes live in an mmap'd region of the GGUF file, NOT in
921    /// RAM. `map` is `MAP_SHARED`, no `MAP_POPULATE` — zero upfront copy. The first `memcpy_htod` of
922    /// this slice page-faults → NVMe read → DMA (the demand-fault disk path). `off`/`len` select this
923    /// expert's contiguous block within the shared file mmap. Bit-identical to `Paged`/`Pinned` —
924    /// those copied FROM exactly these on-disk bytes, so the GEMM result is unchanged.
925    Mmap {
926        map: std::sync::Arc<memmap2::Mmap>,
927        /// The same opened inode backing `map`. It must outlive the loader source so future explicit
928        /// positioned reads cannot accidentally reopen a replaced path.
929        file: std::sync::Arc<std::fs::File>,
930        /// Absolute byte offset within both the whole-file mmap and `file`.
931        off: usize,
932        len: usize,
933    },
934}
935// SAFETY: `base` is a stable pinned-host pointer owned by `slice`; the buffer is written once at load
936// then only READ for H2D. HostExps is shared `&` across the (single per-Engine) forward, so Send/Sync
937// mirror the underlying PinnedHostSlice (which is already Send+Sync). The `Mmap` arm holds
938// `Arc<Mmap>` + `Arc<File>` (both Send+Sync) plus plain usize fields, so it does not weaken bounds.
939unsafe impl Send for HostBuf {}
940unsafe impl Sync for HostBuf {}
941impl HostBuf {
942    #[inline]
943    pub fn as_bytes(&self) -> &[u8] {
944        match self {
945            HostBuf::Paged(v) => v.as_slice(),
946            // SAFETY: base+len are the pinned allocation's stable extent; written once at load, then
947            // read-only. We avoid `as_slice()` here because it would synchronize the buffer's event
948            // on every hot-path call.
949            HostBuf::Pinned { base, len, .. } => unsafe { std::slice::from_raw_parts(*base, *len) },
950            HostBuf::PinnedAlias { base, len, .. } => unsafe {
951                std::slice::from_raw_parts(*base, *len)
952            },
953            // Slicing the mmap is the same `&[u8]` the kernel DMAs; the read page-faults the NVMe.
954            HostBuf::Mmap { map, off, len, .. } => &map[*off..*off + *len],
955        }
956    }
957    #[inline]
958    pub fn len(&self) -> usize {
959        match self {
960            HostBuf::Paged(v) => v.len(),
961            HostBuf::Pinned { len, .. } => *len,
962            HostBuf::PinnedAlias { len, .. } => *len,
963            HostBuf::Mmap { len, .. } => *len,
964        }
965    }
966
967    /// Best-effort OS read-ahead for a future mmap-backed expert range. This does not touch or
968    /// copy the bytes, so the zero-copy ownership contract is unchanged. Non-mmap buffers are
969    /// already resident and need no advice. Kept fallible-at-the-OS but non-fatal at the call site:
970    /// an unsupported/pressured kernel simply leaves the normal demand-fault path in place.
971    #[inline]
972    pub fn advise_willneed(&self, rel_off: usize, len: usize) -> bool {
973        let HostBuf::Mmap {
974            map,
975            off,
976            len: extent,
977            ..
978        } = self
979        else {
980            return false;
981        };
982        if len == 0 || rel_off > *extent || len > *extent - rel_off {
983            return false;
984        }
985        #[cfg(unix)]
986        {
987            map.advise_range(memmap2::Advice::WillNeed, *off + rel_off, len)
988                .is_ok()
989        }
990        #[cfg(not(unix))]
991        {
992            let _ = (map, off);
993            false
994        }
995    }
996
997    #[inline]
998    fn expert_source(&self, rel_off: usize, len: usize) -> ExpertSource<'_> {
999        debug_assert!(rel_off <= self.len() && len <= self.len() - rel_off);
1000        match self {
1001            HostBuf::Mmap { map, file, off, .. } => {
1002                let offset = *off + rel_off;
1003                ExpertSource::Disk {
1004                    file,
1005                    offset: offset as u64,
1006                    len,
1007                    fallback: &map[offset..offset + len],
1008                    keepalive: ExpertKeepalive::Mmap(map.clone()),
1009                }
1010            }
1011            HostBuf::Pinned { slice, .. } => ExpertSource::Memory {
1012                bytes: &self.as_bytes()[rel_off..rel_off + len],
1013                keepalive: Some(ExpertKeepalive::Pinned(slice.clone())),
1014            },
1015            HostBuf::PinnedAlias { owner, .. } => ExpertSource::Memory {
1016                bytes: &self.as_bytes()[rel_off..rel_off + len],
1017                keepalive: Some(ExpertKeepalive::Buffer(owner.clone())),
1018            },
1019            HostBuf::Paged(_) => ExpertSource::Memory {
1020                bytes: &self.as_bytes()[rel_off..rel_off + len],
1021                // CUDA stages pageable input before returning from the async-copy API. Only true
1022                // pinned and mmap-backed sources need an explicit lifetime owner in the cache.
1023                keepalive: None,
1024            },
1025        }
1026    }
1027}
1028
1029/// Clonable ownership retained by asynchronous cache transfers. The payload is intentionally never
1030/// read: keeping it alive is the contract.
1031#[allow(dead_code)]
1032pub(crate) enum ExpertKeepalive {
1033    Pinned(std::sync::Arc<cudarc::driver::PinnedHostSlice<u8>>),
1034    Buffer(std::sync::Arc<HostBuf>),
1035    Mmap(std::sync::Arc<memmap2::Mmap>),
1036}
1037
1038/// Source-aware view of one expert block. The mmap fallback remains the byte oracle; retaining the
1039/// opened file enables a later explicit-read backend without changing tensor layout or numerics.
1040pub(crate) enum ExpertSource<'a> {
1041    Memory {
1042        bytes: &'a [u8],
1043        keepalive: Option<ExpertKeepalive>,
1044    },
1045    Disk {
1046        file: &'a std::sync::Arc<std::fs::File>,
1047        offset: u64,
1048        len: usize,
1049        fallback: &'a [u8],
1050        keepalive: ExpertKeepalive,
1051    },
1052}
1053
1054/// One layer's stacked 256-expert tensor, raw GGUF quant bytes held HOST-RESIDENT.
1055///
1056/// EDGE-1: these bytes are NEVER uploaded at load (uploading 29.75GB would OOM a 24GB GPU —
1057/// this is BUG-4). Per token, only the 8 routed experts are staged H2D into a small GPU scratch.
1058///
1059/// ne = [in_f, out_f, n_expert]; the expert axis (ne[2]) is the slowest/highest-stride axis, so
1060/// expert `e` occupies the CONTIGUOUS byte block `bytes[e*expert_stride .. (e+1)*expert_stride]`.
1061///
1062/// THE 3D FIX: GpuTensor::load computes `row_bytes = raw.len()/ne[1]`, which for a stacked 3D
1063/// tensor ignores the 256-expert axis and is 256x too large (gate_exps -> 430080 instead of 1680).
1064/// load() here uses `row_bytes = raw.len() / (out_f * n_expert)` (= 1680 gate/up, 544 down).
1065#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1066pub struct ExpertLayout {
1067    pub offset: usize,
1068    pub len: usize,
1069    pub qtype: i32,
1070    pub row_bytes: usize,
1071}
1072
1073fn staged_expert_qtype(ty: GgmlType) -> Option<i32> {
1074    Some(match ty {
1075        GgmlType::Q8_0 => QT_Q8_0,
1076        GgmlType::Q2_K => QT_Q2_K,
1077        GgmlType::Q4_K => QT_Q4_K,
1078        GgmlType::Q6_K => QT_Q6_K,
1079        GgmlType::Q5_K => QT_Q5_K,
1080        GgmlType::Q3_K => QT_Q3_K,
1081        GgmlType::IQ4_XS => QT_IQ4_XS,
1082        GgmlType::IQ3_S => QT_IQ3_S,
1083        GgmlType::NVFP4 => QT_NVFP4,
1084        GgmlType::F32 => QT_F32,
1085        GgmlType::BF16 => QT_BF16,
1086        _ => return None,
1087    })
1088}
1089
1090fn staged_expert_row_bytes(ty: GgmlType, in_f: usize) -> Option<usize> {
1091    staged_expert_qtype(ty)?;
1092    let (block, type_size) = ty.block_and_type_size();
1093    assert_eq!(
1094        in_f as u64 % block,
1095        0,
1096        "expert row width {in_f} is not divisible by {ty:?} block {block}"
1097    );
1098    Some((in_f as u64 / block * type_size) as usize)
1099}
1100
1101fn find_expert_disk_strict(
1102    src: &dyn TensorSource,
1103    name: &str,
1104) -> Result<Option<DiskExtent>, Box<dyn std::error::Error>> {
1105    if let Some(extent) = src.find_expert_disk(name) {
1106        return Ok(Some(extent));
1107    }
1108    if src.find_expert_mmap(name).is_some() {
1109        return Err(std::io::Error::new(
1110            std::io::ErrorKind::InvalidData,
1111            format!(
1112                "expert tensor {name} exposes legacy find_expert_mmap without find_expert_disk; \
1113                 disk-backed expert loading requires a retained Arc<File>"
1114            ),
1115        )
1116        .into());
1117    }
1118    Ok(None)
1119}
1120
1121pub struct HostExps {
1122    pub bytes: HostBuf, // raw GGUF block bytes (host); per-token DMA src for the 8 routed exps
1123    /// SPILLING-PLAN §1.1: per-expert backing tier. `None` => the layer fits in one `bytes` store and
1124    /// every expert slices it (the unchanged in-RAM path). `Some` => per-expert split: the hottest
1125    /// experts are `Pinned` (Tier 1, fast async DMA), the rest `Mmap` into the GGUF (Tier 2, disk
1126    /// demand-fault). `expert_bytes(e)` resolves `tiers[e]` if present, else slices `bytes`.
1127    pub tiers: Option<Vec<HostBuf>>,
1128    pub qtype: i32,           // QT_Q6_K (gate/up) | QT_Q8_0 (down)
1129    pub in_f: usize,          // ne[0]   (gate/up = 2048, down = 512)
1130    pub out_f: usize,         // ne[1]   (gate/up = 512,  down = 2048)
1131    pub n_expert: usize,      // ne[2] = 256
1132    pub row_bytes: usize,     // raw.len()/(out_f*n_expert)  -> 1680 (gate/up) / 544 (down)
1133    pub expert_stride: usize, // raw.len()/n_expert          -> 860160 (gate/up) / 1114112 (down)
1134    /// Per-expert encoding metadata when experts in this projection do not share one dtype/layout.
1135    /// `None` preserves the existing uniform slab contract and every resident/fused fast path.
1136    /// `Some` routes through the per-expert staged/cache path, using each entry's qtype/row size.
1137    pub layouts: Option<Vec<ExpertLayout>>,
1138    /// Per-expert post-matmul macro-scale (ModelOpt NVFP4 `weight_scale_2`, one scalar per expert
1139    /// tensor). `None` => all 1.0 (GGUF experts; block scales carry everything). The MoE forward
1140    /// folds gate/up macros into the activation epilogue (gs/us) and the down macro into the
1141    /// per-expert accumulate weight.
1142    pub macros: Option<Vec<f32>>,
1143}
1144
1145impl HostExps {
1146    /// Load a stacked 3D expert tensor, keeping its quant bytes on the HOST. `e` supplies the CUDA
1147    /// context for the optional pinned allocation (§C.1). Default storage is pageable `Vec<u8>`
1148    /// (identical to the prior behavior); pinned is chosen when MEMRA_MOE_PINNED or MEMRA_MOE_CACHE is set.
1149    pub fn load(e: &Engine, g: &GgufFile, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
1150        Self::load_stacked_from_source(e, &GgufSource(g), name)
1151    }
1152
1153    /// Load a STACKED 3D expert tensor (`ne=[in_f,out_f,n_expert]`) from any source. GGUF stores the
1154    /// experts this way; the source returns the same mmap bytes (`GgufSource::find` == `tensor_data`),
1155    /// so the GGUF path is byte-identical to the prior direct-`GgufFile` loader. (Safetensors stores N
1156    /// 2D tensors instead — those go through `load_from_source`, which gathers them.)
1157    /// Row-range variant for FUSED stacked tensors (gemma4 ffn_gate_up_exps: gate = rows
1158    /// [0,ff), up = [ff,2ff) per expert — llama-graph view convention). Copies only the range.
1159    pub fn load_stacked_split_from_source(
1160        e: &Engine,
1161        src: &dyn TensorSource,
1162        name: &str,
1163        row0: usize,
1164        row1: usize,
1165    ) -> Result<Self, Box<dyn std::error::Error>> {
1166        let t = src
1167            .find(name)
1168            .unwrap_or_else(|| panic!("missing exps tensor {name}"));
1169        assert_eq!(t.ne.len(), 3, "{name} is not 3D (ne={:?})", t.ne);
1170        let qtype = match t.ggml_type {
1171            GgmlType::Q8_0 => QT_Q8_0,
1172            GgmlType::Q4_K => QT_Q4_K,
1173            GgmlType::Q6_K => QT_Q6_K,
1174            GgmlType::Q5_K => QT_Q5_K,
1175            GgmlType::Q3_K => QT_Q3_K,
1176            GgmlType::IQ4_XS => QT_IQ4_XS,
1177            GgmlType::IQ3_S => QT_IQ3_S,
1178            GgmlType::NVFP4 => QT_NVFP4,
1179            GgmlType::Q4_0 => QT_Q4_0,
1180            other => panic!("exps {name} unsupported quant {other:?}"),
1181        };
1182        let raw: &[u8] = &t.bytes;
1183        let in_f = t.ne[0] as usize;
1184        let out_full = t.ne[1] as usize;
1185        let n_expert = t.ne[2] as usize;
1186        let full_stride = raw.len() / n_expert;
1187        let row_bytes = raw.len() / (out_full * n_expert);
1188        assert_eq!(full_stride, out_full * row_bytes, "{name} stride mismatch");
1189        let out_f = row1 - row0;
1190        let expert_stride = out_f * row_bytes;
1191        let mut buf = vec![0u8; n_expert * expert_stride];
1192        for ex in 0..n_expert {
1193            let s0 = ex * full_stride + row0 * row_bytes;
1194            buf[ex * expert_stride..(ex + 1) * expert_stride]
1195                .copy_from_slice(&raw[s0..s0 + expert_stride]);
1196        }
1197        let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
1198            || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
1199        let bytes = if pinned {
1200            let mut pn = unsafe { e.ctx().alloc_pinned::<u8>(buf.len())? };
1201            {
1202                let dst = pn.as_mut_slice()?;
1203                dst.copy_from_slice(&buf);
1204            }
1205            let base = pn.as_ptr()? as *const u8;
1206            let len = buf.len();
1207            HostBuf::Pinned {
1208                slice: std::sync::Arc::new(pn),
1209                base,
1210                len,
1211            }
1212        } else {
1213            HostBuf::Paged(buf)
1214        };
1215        Ok(HostExps {
1216            bytes,
1217            tiers: None,
1218            qtype,
1219            in_f,
1220            out_f,
1221            n_expert,
1222            row_bytes,
1223            expert_stride,
1224            layouts: None,
1225            macros: None,
1226        })
1227    }
1228
1229    /// Stacked per-expert macro-scale sidecar: `blk.N.ffn_{proj}_exps.scale` f32 [n_expert]
1230    /// (the qwen3.6 NVFP4 converter emits one per stacked expert tensor — compressed-tensors
1231    /// global scales, inverted to multipliers). Absent (every k-quant GGUF) => None.
1232    /// NOTE gemma4 consumes ffn_down_exps.scale through its OWN router-fold (Gemma4MoeBits) —
1233    /// its MoE forward does not read HostExps::macros, so a Some here is inert there.
1234    fn stacked_macros(src: &dyn TensorSource, name: &str) -> Option<Vec<f32>> {
1235        let stem = name.strip_suffix(".weight")?;
1236        let sv = src.find(&format!("{stem}.scale"))?;
1237        if sv.ggml_type != GgmlType::F32 { return None; }
1238        let macros: Vec<f32> = sv.bytes.chunks_exact(4)
1239            .map(|c| f32::from_le_bytes(c.try_into().unwrap())).collect();
1240        if macros.iter().all(|&m| m == 1.0) { None } else { Some(macros) }
1241    }
1242
1243    pub fn load_stacked_from_source(e: &Engine, src: &dyn TensorSource, name: &str)
1244                                    -> Result<Self, Box<dyn std::error::Error>> {
1245        let t = src.find(name).unwrap_or_else(|| panic!("missing exps tensor {name}"));
1246        assert_eq!(t.ne.len(), 3, "{name} is not a 3D stacked-expert tensor (ne={:?})", t.ne);
1247        // MMAP-BACKED SPILL TIER (Hy3 repack dir, 2026-07-09): when the source's on-disk layout IS
1248        // already the engine's expert layout (one expert-axis-slowest slab file per (layer, proj),
1249        // the transcoder's contract), back the HostExps with `HostBuf::Mmap` directly — ZERO host
1250        // copy. The default copy path below would pin/allocate the WHOLE stacked slab (80.5 GB for
1251        // Hy3-REAP50 on a 60 GB host = the M3 first-load OOM class); the mmap tier instead lets the
1252        // page cache carry the hot expert mass (RAM tier) and demand-faults the overflow from NVMe,
1253        // exactly like the proven M3 `.memra-repack` path (model.rs NVFP4 disk arm). Bit-identity:
1254        // `expert_bytes(e)` slices the same on-disk bytes the copy would have staged. The SLRU VRAM
1255        // cache stacks on top unchanged. The configured whole-map advice is applied at source open.
1256        if let Some(DiskExtent {
1257            map,
1258            file,
1259            offset,
1260            len,
1261        }) = find_expert_disk_strict(src, name)?
1262        {
1263            let off = usize::try_from(offset)
1264                .map_err(|_| format!("{name} disk offset {offset} does not fit usize"))?;
1265            let qtype = match t.ggml_type {
1266                GgmlType::Q8_0 => QT_Q8_0,
1267                GgmlType::Q4_K => QT_Q4_K,
1268                GgmlType::Q6_K => QT_Q6_K,
1269                GgmlType::Q5_K => QT_Q5_K,
1270                GgmlType::Q3_K => QT_Q3_K,
1271                GgmlType::IQ4_XS => QT_IQ4_XS,
1272                GgmlType::IQ3_S => QT_IQ3_S,
1273                GgmlType::NVFP4 => QT_NVFP4,
1274                GgmlType::Q4_0 => QT_Q4_0,
1275                other => panic!("exps {name} unsupported quant {other:?}"),
1276            };
1277            let in_f = t.ne[0] as usize;
1278            let out_f = t.ne[1] as usize;
1279            let n_expert = t.ne[2] as usize;
1280            let expert_stride = len / n_expert;
1281            let row_bytes = len / (out_f * n_expert);
1282            assert_eq!(expert_stride, out_f * row_bytes,
1283                "{name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}");
1284            assert_eq!(
1285                len,
1286                n_expert * expert_stride,
1287                "{name} mmap len != n_expert*stride"
1288            );
1289            return Ok(HostExps {
1290                bytes: HostBuf::Mmap { map, file, off, len },
1291                tiers: None, qtype, in_f, out_f, n_expert, row_bytes, expert_stride,
1292                layouts: None, macros: Self::stacked_macros(src, name),
1293            });
1294        }
1295        let raw: &[u8] = &t.bytes;
1296        // All quant types the staged-expert qmatvec can decode (dp4a-fast or Stage-A f32).
1297        let qtype = match t.ggml_type {
1298            GgmlType::Q8_0 => QT_Q8_0,
1299            GgmlType::Q4_K => QT_Q4_K,
1300            GgmlType::Q6_K => QT_Q6_K,
1301            GgmlType::Q5_K => QT_Q5_K,
1302            GgmlType::Q3_K => QT_Q3_K,
1303            GgmlType::IQ4_XS => QT_IQ4_XS,
1304            GgmlType::IQ3_S => QT_IQ3_S,
1305            GgmlType::NVFP4 => QT_NVFP4,
1306            GgmlType::Q4_0 => QT_Q4_0,
1307            other => panic!("exps {name} unsupported quant {other:?}"),
1308        };
1309        let in_f = t.ne[0] as usize;
1310        let out_f = t.ne[1] as usize;
1311        let n_expert = t.ne[2] as usize;
1312        // VERIFIED: gate/up Q6_K total/256 = 860160; row = total/(512*256) = 1680.
1313        //           down  Q8_0 total/256 = 1114112; row = total/(2048*256) = 544.
1314        let expert_stride = raw.len() / n_expert;
1315        let row_bytes = raw.len() / (out_f * n_expert);
1316        // sanity: expert_stride must equal out_f * row_bytes exactly (catches a dim mixup)
1317        assert_eq!(
1318            expert_stride,
1319            out_f * row_bytes,
1320            "{name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}"
1321        );
1322
1323        let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
1324            || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
1325        let bytes = if pinned {
1326            // alloc pinned host memory, copy the GGUF block bytes in once, cache the base pointer.
1327            let mut p = unsafe { e.ctx().alloc_pinned::<u8>(raw.len())? };
1328            {
1329                let dst = p.as_mut_slice()?;
1330                dst.copy_from_slice(raw);
1331            }
1332            let base = p.as_ptr()? as *const u8; // syncs once here at load; stable afterward
1333            let len = raw.len();
1334            HostBuf::Pinned {
1335                slice: std::sync::Arc::new(p),
1336                base,
1337                len,
1338            }
1339        } else {
1340            HostBuf::Paged(raw.to_vec())
1341        };
1342        Ok(HostExps { bytes, tiers: None, qtype, in_f, out_f, n_expert, row_bytes,
1343                      expert_stride, layouts: None, macros: Self::stacked_macros(src, name) })
1344    }
1345
1346    /// SPILLING-PLAN §1.1, §2 step 4: load a stacked 3D expert tensor with a PER-EXPERT tier split.
1347    /// Under `MEMRA_SPILL_DISK`, the hottest experts (greedy in expert order, until the shared pinned
1348    /// budget in `ctx` is exhausted) get `HostBuf::Pinned` (Tier 1, fast async DMA); every remaining
1349    /// expert is `HostBuf::Mmap` into the GGUF (Tier 2, demand-faulted from disk on first H2D). The
1350    /// resulting bytes are bit-identical to the in-RAM path either way — `qmatvec_view` is untouched.
1351    ///
1352    /// `ctx.file_map` is ONE shared `MAP_SHARED` mmap of the whole GGUF (`Arc`-cloned per spilled
1353    /// expert), so the 120 expert tensors of a 40-layer MoE never open the file more than once.
1354    pub fn load_tiered(
1355        e: &Engine,
1356        g: &GgufFile,
1357        name: &str,
1358        ctx: &mut crate::spill::SpillCtx,
1359    ) -> Result<Self, Box<dyn std::error::Error>> {
1360        let t = g
1361            .find(name)
1362            .unwrap_or_else(|| panic!("missing exps tensor {name}"));
1363        assert_eq!(
1364            t.ne.len(),
1365            3,
1366            "{name} is not a 3D stacked-expert tensor (ne={:?})",
1367            t.ne
1368        );
1369        let raw = g.tensor_data(t);
1370        let qtype = match t.ggml_type {
1371            GgmlType::Q8_0 => QT_Q8_0,
1372            GgmlType::Q4_K => QT_Q4_K,
1373            GgmlType::Q6_K => QT_Q6_K,
1374            GgmlType::Q5_K => QT_Q5_K,
1375            GgmlType::Q3_K => QT_Q3_K,
1376            GgmlType::IQ4_XS => QT_IQ4_XS,
1377            GgmlType::IQ3_S => QT_IQ3_S,
1378            GgmlType::NVFP4 => QT_NVFP4,
1379            GgmlType::Q4_0 => QT_Q4_0,
1380            other => panic!("exps {name} unsupported quant {other:?}"),
1381        };
1382        let in_f = t.ne[0] as usize;
1383        let out_f = t.ne[1] as usize;
1384        let n_expert = t.ne[2] as usize;
1385        let expert_stride = raw.len() / n_expert;
1386        let row_bytes = raw.len() / (out_f * n_expert);
1387        assert_eq!(
1388            expert_stride,
1389            out_f * row_bytes,
1390            "{name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}"
1391        );
1392
1393        // Absolute file offset of this tensor's data (start of expert 0); each expert is the next
1394        // `expert_stride` bytes. The `Mmap` arm slices `ctx.file_map` at these offsets.
1395        let (file_start, _file_end) = g.tensor_file_range(t);
1396
1397        // Per-expert tier decision under the shared running budget. `bytes` keeps a 0-byte sentinel
1398        // (`Paged(empty)`) since every read now goes through `tiers`.
1399        let mut tiers = Vec::with_capacity(n_expert);
1400        for ex in 0..n_expert {
1401            let blk = &raw[ex * expert_stride..(ex + 1) * expert_stride];
1402            let file_off = file_start + ex * expert_stride;
1403            tiers.push(crate::spill::place_expert(ctx, e, blk, file_off)?);
1404        }
1405        Ok(HostExps {
1406            bytes: HostBuf::Paged(Vec::new()), // unused when `tiers` is Some
1407            tiers: Some(tiers),
1408            qtype, in_f, out_f, n_expert, row_bytes, expert_stride, layouts: None,
1409            macros: Self::stacked_macros(&GgufSource(g), name),
1410        })
1411    }
1412
1413    /// MoE expert GATHER from a `TensorSource` (the safetensors path; ST-MOE-PLAN §1.3). GGUF stacks
1414    /// all experts into ONE 3D tensor; HF stores them as N separate 2D tensors
1415    /// `model.layers.{il}.mlp.experts.{e}.{gate,up,down}_proj.weight`. `find` returns `None` for the
1416    /// ggml `*_exps` name on purpose, so the experts are gathered out-of-band here.
1417    ///
1418    /// PATH A (load-time only, no quantize): each HF 2D expert tensor is dequantized to f32 and the
1419    /// per-expert blocks are concatenated expert-axis-slowest into ONE contiguous buffer — exactly the
1420    /// layout `expert_bytes(e)` slices and the staged `qmatvec_view` (qtype=QT_F32) reads. The same
1421    /// `expert_stride == out_f*row_bytes` invariant as the GGUF path is asserted at the end.
1422    ///
1423    /// `ggml_exps_name` is `blk.{il}.ffn_{gate,up,down}_exps.weight`; it is split to recover `il` and
1424    /// the proj. `n_expert` comes from `cfg.moe`. The HF per-expert literal `mlp.experts.{e}.{p}_proj`
1425    /// is the qwen3moe / olmoe layout (a future arch with `block_sparse_moe.experts.*` would need a
1426    /// branch in `hf_expert_name`).
1427    pub fn load_from_source(
1428        e: &Engine,
1429        src: &dyn TensorSource,
1430        ggml_exps_name: &str,
1431        n_expert: usize,
1432    ) -> Result<Self, Box<dyn std::error::Error>> {
1433        // Recover il + proj from `blk.{il}.ffn_{gate,up,down}_exps.weight`.
1434        let rest = ggml_exps_name
1435            .strip_prefix("blk.")
1436            .unwrap_or_else(|| panic!("not a blk.* name: {ggml_exps_name}"));
1437        let (il_s, suffix) = rest.split_once('.').unwrap();
1438        let il: u32 = il_s.parse().unwrap();
1439        let proj = match suffix {
1440            "ffn_gate_exps.weight" => "gate",
1441            "ffn_up_exps.weight" => "up",
1442            "ffn_down_exps.weight" => "down",
1443            other => panic!("not a *_exps suffix: {other}"),
1444        };
1445
1446        // A mixed-precision safetensors/repack source exposes experts as separate 2D tensors.
1447        // Detect a dtype/layout change before the uniform gather paths normalize the whole layer
1448        // to one encoding. Uniform checkpoints take the unchanged optimized path below.
1449        let mut signatures = Vec::with_capacity(n_expert);
1450        let active = src.active_experts(il);
1451        for ex in 0..n_expert {
1452            if active.is_some_and(|mask| !mask[ex]) {
1453                signatures.push((i32::MIN, 0));
1454                continue;
1455            }
1456            let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
1457            if let Some(nv) = src.find_nvfp4_native(&name) {
1458                signatures.push((QT_NVFP4, nv.in_f / 64 * 36));
1459            } else {
1460                let v = src
1461                    .find(&name)
1462                    .unwrap_or_else(|| panic!("missing expert tensor {name}"));
1463                let in_f = v.ne[0] as usize;
1464                signatures.push(match staged_expert_row_bytes(v.ggml_type, in_f) {
1465                    Some(row_bytes) => (staged_expert_qtype(v.ggml_type).unwrap(), row_bytes),
1466                    None => (QT_F32, in_f * 4),
1467                });
1468            }
1469        }
1470        let mixed_layout = signatures.windows(2).any(|pair| pair[0] != pair[1]);
1471        if src.preserve_expert_encodings() && !mixed_layout {
1472            if let Some(uniform) = Self::load_uniform_mmap_from_source(src, il, proj, n_expert)? {
1473                return Ok(uniform);
1474            }
1475        }
1476        if src.preserve_expert_encodings() || mixed_layout {
1477            return Self::load_mixed_from_source(src, il, proj, n_expert);
1478        }
1479
1480        // PATH B (NVFP4-NATIVE GATHER, 2026-07-05): when the source exposes the experts as packed
1481        // ModelOpt/Reza NVFP4 (find_nvfp4_native), keep them QUANTIZED — repack each expert's
1482        // modelopt bytes to the GGUF 36B-block layout the staged qmatvec decodes, and concatenate.
1483        // No f32 blow-up: a 129GB checkpoint gathers to ~the same bytes instead of ~8x (which is
1484        // what makes MiniMax-M3 REAP50 loadable on a 60GB-RAM host at all, with spill on top).
1485        // Per-expert `weight_scale_2` macros go to `macros` (folded post-matmul by the MoE forward).
1486        {
1487            let name0 = format!("blk.{il}.ffn_{proj}_exps.0.weight");
1488            if let Some(nv0) = src.find_nvfp4_native(&name0) {
1489                let (in_f, out_f) = (nv0.in_f, nv0.out_f);
1490                let row_bytes = in_f / 64 * 36;
1491                let expert_stride = out_f * row_bytes;
1492                // ST DISK TIER (2026-07-06, the MiniMax OOM fix): when the total expert bytes
1493                // exceed host RAM (M3 REAP50 = 122GB repacked on a 60GB host, first-load host-OOM
1494                // at layer ~24), repack each layer ONCE into an on-disk cache file next to the
1495                // checkpoint and mmap it (HostBuf::Mmap, MAP_SHARED no-populate — the same tier-2
1496                // mechanism the GGUF spill path uses). Reloads hit the cache (size-checked), pay
1497                // zero repack. MEMRA_ST_REPACK_DISK=0 forces the old in-RAM gather.
1498                let disk = std::env::var("MEMRA_ST_REPACK_DISK")
1499                    .map(|v| v != "0")
1500                    .unwrap_or(true)
1501                    && src.st_dir().is_some();
1502                let cache_path = src.st_dir().map(|d| {
1503                    let cd = d.join(".memra-repack");
1504                    let _ = std::fs::create_dir_all(&cd);
1505                    cd.join(format!("blk{il}-{proj}-{n_expert}x{out_f}x{in_f}.nvfp4"))
1506                });
1507                let total = n_expert * expert_stride;
1508                let mut macros = vec![1.0f32; n_expert];
1509                let read_macros = |macros: &mut Vec<f32>| {
1510                    for ex in 0..n_expert {
1511                        let stem = format!("blk.{il}.ffn_{proj}_exps.{ex}");
1512                        if let Some(sv) = src.find(&format!("{stem}.scale")) {
1513                            macros[ex] = f32::from_le_bytes(sv.bytes[..4].try_into().unwrap());
1514                        }
1515                    }
1516                };
1517                let bytes = if disk {
1518                    let cp = cache_path.as_ref().unwrap();
1519                    let fresh = std::fs::metadata(cp)
1520                        .map(|m| m.len() as usize == total)
1521                        .unwrap_or(false);
1522                    if !fresh {
1523                        // stream one expert at a time to disk — peak RAM = one expert (~8MB)
1524                        use std::io::Write;
1525                        let mut f = std::io::BufWriter::new(std::fs::File::create(cp)?);
1526                        for ex in 0..n_expert {
1527                            let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
1528                            let nv = src.find_nvfp4_native(&name).unwrap_or_else(|| {
1529                                panic!("expert {name} lost NVFP4-native mid-gather")
1530                            });
1531                            assert_eq!(
1532                                (nv.in_f, nv.out_f),
1533                                (in_f, out_f),
1534                                "expert {ex} dims ({},{}) != expert 0 ({in_f},{out_f})",
1535                                nv.in_f,
1536                                nv.out_f
1537                            );
1538                            f.write_all(&memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
1539                                nv.wbytes, nv.wscale, out_f, in_f,
1540                            ))?;
1541                        }
1542                        f.flush()?;
1543                    }
1544                    read_macros(&mut macros);
1545                    let file = std::sync::Arc::new(std::fs::File::open(cp)?);
1546                    let map = unsafe { memmap2::Mmap::map(file.as_ref())? };
1547                    assert_eq!(map.len(), total, "repack cache {cp:?} size mismatch");
1548                    // Default random preserves the original policy; normal lets Linux readahead
1549                    // within each multi-megabyte expert on the spill-bound path.
1550                    let _ = memra_gguf::source::apply_expert_mmap_advice(&map);
1551                    let map = std::sync::Arc::new(map);
1552                    // ST PINNED TIER (2026-07-07, the M3 1.5-tok/s lever): mmap-only backing makes
1553                    // every SLRU miss a page-cache (or NVMe) synchronous read into the H2D copy.
1554                    // Pin as many experts as the live budget allows (same MemBudget probe + 0.6
1555                    // MemAvailable cap as the GGUF spill tier) — pinned pages upload via true
1556                    // async DMA at full PCIe. Budget is GLOBAL across layers (first-come: earlier
1557                    // layers pin first; routing is roughly uniform so early-layer bias is benign).
1558                    // MEMRA_ST_PINNED=0 disables (pure-mmap, the 2026-07-06 behavior).
1559                    // DEFAULT OFF (2026-07-07 measured): with a 122GB expert set on 60GB RAM,
1560                    // pinning 26GB EVICTED the page cache backing the mmap tier — every unpinned
1561                    // expert faulted cold from NVMe and gen fell 1.5 -> 0.05 tok/s (30x WORSE).
1562                    // Pinning only pays when (total - pinned) fits page cache; here it never can.
1563                    // MEMRA_ST_PINNED=1 opt-in for fits-in-RAM checkpoints (e.g. REAP-heavier cuts).
1564                    let tiers = if std::env::var("MEMRA_ST_PINNED")
1565                        .map(|v| v == "1")
1566                        .unwrap_or(false)
1567                    {
1568                        static PIN_BUDGET: std::sync::OnceLock<std::sync::Mutex<usize>> =
1569                            std::sync::OnceLock::new();
1570                        let budget = PIN_BUDGET.get_or_init(|| {
1571                            let b = crate::spill::MemBudget::probe(e)
1572                                .map(|b| b.free_pinnable_ram)
1573                                .unwrap_or(0);
1574                            eprintln!("[st-spill] pinned budget {:.1} GB", b as f64 / 1e9);
1575                            std::sync::Mutex::new(b)
1576                        });
1577                        let mut rem = budget.lock().unwrap();
1578                        // ONE pinned slab per file prefix (n_pin experts contiguous): 1 alloc +
1579                        // 1 bulk copy instead of n_pin small allocs (per-expert cudaHostAllocs
1580                        // stalled the 122GB M3 load >10min).
1581                        let n_pin = (*rem / expert_stride).min(n_expert);
1582                        if n_pin == 0 {
1583                            None
1584                        } else {
1585                            let slab_len = n_pin * expert_stride;
1586                            let mut pn = unsafe { e.ctx().alloc_pinned::<u8>(slab_len)? };
1587                            {
1588                                let dst = pn.as_mut_slice()?;
1589                                dst.copy_from_slice(&map[..slab_len]);
1590                            }
1591                            let base = pn.as_ptr()? as *const u8;
1592                            *rem -= slab_len;
1593                            let slab = std::sync::Arc::new(HostBuf::Pinned {
1594                                slice: std::sync::Arc::new(pn),
1595                                base,
1596                                len: slab_len,
1597                            });
1598                            let mut tiers: Vec<HostBuf> = Vec::with_capacity(n_expert);
1599                            for ex in 0..n_expert {
1600                                let off = ex * expert_stride;
1601                                if ex < n_pin {
1602                                    tiers.push(HostBuf::PinnedAlias {
1603                                        owner: slab.clone(),
1604                                        base: unsafe { base.add(off) },
1605                                        len: expert_stride,
1606                                    });
1607                                } else {
1608                                    tiers.push(HostBuf::Mmap {
1609                                        map: map.clone(),
1610                                        file: file.clone(),
1611                                        off,
1612                                        len: expert_stride,
1613                                    });
1614                                }
1615                            }
1616                            Some(tiers)
1617                        }
1618                    } else {
1619                        None
1620                    };
1621                    if let Some(tiers) = tiers {
1622                        let all_one = macros.iter().all(|&m| m == 1.0);
1623                        return Ok(HostExps {
1624                            bytes: HostBuf::Mmap {
1625                                map,
1626                                file,
1627                                off: 0,
1628                                len: total,
1629                            },
1630                            tiers: Some(tiers),
1631                            qtype: QT_NVFP4,
1632                            in_f,
1633                            out_f,
1634                            n_expert,
1635                            row_bytes,
1636                            expert_stride,
1637                            layouts: None,
1638                            macros: if all_one { None } else { Some(macros) },
1639                        });
1640                    }
1641                    HostBuf::Mmap {
1642                        map,
1643                        file,
1644                        off: 0,
1645                        len: total,
1646                    }
1647                } else {
1648                    let mut buf: Vec<u8> = Vec::with_capacity(total);
1649                    for ex in 0..n_expert {
1650                        let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
1651                        let nv = src.find_nvfp4_native(&name).unwrap_or_else(|| {
1652                            panic!("expert {name} lost NVFP4-native mid-gather")
1653                        });
1654                        assert_eq!(
1655                            (nv.in_f, nv.out_f),
1656                            (in_f, out_f),
1657                            "expert {ex} dims ({},{}) != expert 0 ({in_f},{out_f})",
1658                            nv.in_f,
1659                            nv.out_f
1660                        );
1661                        buf.extend_from_slice(&memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
1662                            nv.wbytes, nv.wscale, out_f, in_f,
1663                        ));
1664                    }
1665                    assert_eq!(buf.len(), total);
1666                    read_macros(&mut macros);
1667                    let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
1668                        || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
1669                    if pinned {
1670                        let mut p = unsafe { e.ctx().alloc_pinned::<u8>(buf.len())? };
1671                        {
1672                            let dst = p.as_mut_slice()?;
1673                            dst.copy_from_slice(&buf);
1674                        }
1675                        let base = p.as_ptr()? as *const u8;
1676                        let len = buf.len();
1677                        HostBuf::Pinned {
1678                            slice: std::sync::Arc::new(p),
1679                            base,
1680                            len,
1681                        }
1682                    } else {
1683                        HostBuf::Paged(buf)
1684                    }
1685                };
1686                let all_one = macros.iter().all(|&m| m == 1.0);
1687                return Ok(HostExps {
1688                    bytes,
1689                    tiers: None,
1690                    qtype: QT_NVFP4,
1691                    in_f,
1692                    out_f,
1693                    n_expert,
1694                    row_bytes,
1695                    expert_stride,
1696                    layouts: None,
1697                    macros: if all_one { None } else { Some(macros) },
1698                });
1699            }
1700        }
1701
1702        // expert 0 fixes (in_f, out_f); every later expert must match (catches a layer/arch mixup).
1703        let mut buf: Vec<u8> = Vec::new();
1704        let mut in_f = 0usize;
1705        let mut out_f = 0usize;
1706        for ex in 0..n_expert {
1707            // Per-expert ggml name; the source maps it to the HF expert tensor (ST-MOE-PLAN §1.3).
1708            let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
1709            let v = src
1710                .find(&name)
1711                .unwrap_or_else(|| panic!("missing expert tensor {name}"));
1712            assert_eq!(v.ne.len(), 2, "expert {name} is not 2D (ne={:?})", v.ne);
1713            let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
1714            if ex == 0 {
1715                in_f = cur_in;
1716                out_f = cur_out;
1717            } else {
1718                assert_eq!(
1719                    (cur_in, cur_out),
1720                    (in_f, out_f),
1721                    "expert {ex} dims {:?} != expert 0 [{in_f},{out_f}]",
1722                    (cur_in, cur_out)
1723                );
1724            }
1725            // PATH A: dequant the 2D expert (F32/F16/BF16) to f32, append its bytes verbatim. The
1726            // dequantized [out_f, in_f] row-major f32 block is exactly one expert_stride slow→fast.
1727            let n = cur_in * cur_out;
1728            let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n);
1729            buf.reserve(n * 4);
1730            for f in &f32v {
1731                buf.extend_from_slice(&f.to_le_bytes());
1732            }
1733        }
1734        let row_bytes = in_f * 4; // one out-row = in_f contiguous f32s
1735        let expert_stride = out_f * row_bytes;
1736        assert_eq!(
1737            buf.len(),
1738            n_expert * expert_stride,
1739            "{ggml_exps_name} gather size {} != n_expert*stride {}",
1740            buf.len(),
1741            n_expert * expert_stride
1742        );
1743        // Hold to the identical invariant as the GGUF path (ST-MOE-PLAN §1.3 step 4).
1744        assert_eq!(expert_stride, out_f * row_bytes,
1745            "{ggml_exps_name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}");
1746
1747        // Same pinned-vs-paged choice as the GGUF loader (the bytes are H2D-only on the hot path).
1748        let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
1749            || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
1750        let bytes = if pinned {
1751            let mut p = unsafe { e.ctx().alloc_pinned::<u8>(buf.len())? };
1752            {
1753                let dst = p.as_mut_slice()?;
1754                dst.copy_from_slice(&buf);
1755            }
1756            let base = p.as_ptr()? as *const u8;
1757            let len = buf.len();
1758            HostBuf::Pinned {
1759                slice: std::sync::Arc::new(p),
1760                base,
1761                len,
1762            }
1763        } else {
1764            HostBuf::Paged(buf)
1765        };
1766        Ok(HostExps {
1767            bytes,
1768            tiers: None,
1769            qtype: QT_F32,
1770            in_f,
1771            out_f,
1772            n_expert,
1773            row_bytes,
1774            expert_stride,
1775            layouts: None,
1776            macros: None,
1777        })
1778    }
1779
1780    /// Coalesce a uniform v2 overlay back into the existing stacked-slab contract without copying.
1781    /// The artifact stores one record per original expert for coverage validation, but a full-bank
1782    /// uniform arm writes those records contiguously into one file. Keeping `layouts=None` preserves
1783    /// the uniform fused kernels while `HostBuf::Mmap` keeps the >RAM artifact zero-copy.
1784    fn load_uniform_mmap_from_source(
1785        src: &dyn TensorSource,
1786        il: u32,
1787        proj: &str,
1788        n_expert: usize,
1789    ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
1790        if src
1791            .active_experts(il)
1792            .is_some_and(|mask| mask.iter().any(|&active| !active))
1793        {
1794            return Ok(None);
1795        }
1796        let mut first_map = None;
1797        let mut first_file = None;
1798        let mut base_offset = 0u64;
1799        let mut expert_stride = 0usize;
1800        let mut in_f = 0usize;
1801        let mut out_f = 0usize;
1802        let mut qtype = 0i32;
1803        let mut row_bytes = 0usize;
1804        let mut macros = vec![1.0f32; n_expert];
1805        for ex in 0..n_expert {
1806            let stem = format!("blk.{il}.ffn_{proj}_exps.{ex}");
1807            let name = format!("{stem}.weight");
1808            let Some(DiskExtent {
1809                map,
1810                file,
1811                offset,
1812                len,
1813            }) = find_expert_disk_strict(src, &name)?
1814            else {
1815                return Ok(None);
1816            };
1817            let Some(v) = src.find(&name) else {
1818                return Ok(None);
1819            };
1820            if v.ne.len() != 2 {
1821                return Ok(None);
1822            }
1823            let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
1824            let Some(cur_row_bytes) = staged_expert_row_bytes(v.ggml_type, cur_in) else {
1825                return Ok(None);
1826            };
1827            let cur_qtype = staged_expert_qtype(v.ggml_type).unwrap();
1828            if ex == 0 {
1829                base_offset = offset;
1830                expert_stride = len;
1831                in_f = cur_in;
1832                out_f = cur_out;
1833                qtype = cur_qtype;
1834                row_bytes = cur_row_bytes;
1835                first_map = Some(map);
1836                first_file = Some(file);
1837            } else if !std::sync::Arc::ptr_eq(first_map.as_ref().unwrap(), &map)
1838                || !std::sync::Arc::ptr_eq(first_file.as_ref().unwrap(), &file)
1839                || offset != base_offset + (ex * expert_stride) as u64
1840                || len != expert_stride
1841                || (cur_in, cur_out, cur_qtype, cur_row_bytes) != (in_f, out_f, qtype, row_bytes)
1842            {
1843                return Ok(None);
1844            }
1845            if let Some(scale) = src.find(&format!("{stem}.scale")) {
1846                macros[ex] = f32::from_le_bytes(scale.bytes[..4].try_into().unwrap());
1847            }
1848        }
1849        assert_eq!(expert_stride, out_f * row_bytes);
1850        let total = n_expert * expert_stride;
1851        let off = usize::try_from(base_offset)
1852            .map_err(|_| format!("uniform expert disk offset {base_offset} does not fit usize"))?;
1853        let all_one = macros.iter().all(|&scale| scale == 1.0);
1854        Ok(Some(HostExps {
1855            bytes: HostBuf::Mmap {
1856                map: first_map.unwrap(),
1857                file: first_file.unwrap(),
1858                off,
1859                len: total,
1860            },
1861            tiers: None,
1862            qtype,
1863            in_f,
1864            out_f,
1865            n_expert,
1866            row_bytes,
1867            expert_stride,
1868            layouts: None,
1869            macros: if all_one { None } else { Some(macros) },
1870        }))
1871    }
1872
1873    fn load_mixed_from_source(
1874        src: &dyn TensorSource,
1875        il: u32,
1876        proj: &str,
1877        n_expert: usize,
1878    ) -> Result<Self, Box<dyn std::error::Error>> {
1879        let mut tiers = Vec::with_capacity(n_expert);
1880        let mut layouts = Vec::with_capacity(n_expert);
1881        let mut macros = vec![1.0f32; n_expert];
1882        let mut in_f = 0usize;
1883        let mut out_f = 0usize;
1884        let active = src.active_experts(il);
1885        let mut first_active = None;
1886
1887        for ex in 0..n_expert {
1888            if active.is_some_and(|mask| !mask[ex]) {
1889                layouts.push(ExpertLayout {
1890                    offset: 0,
1891                    len: 0,
1892                    qtype: QT_F32,
1893                    row_bytes: 0,
1894                });
1895                tiers.push(HostBuf::Paged(Vec::new()));
1896                continue;
1897            }
1898            let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
1899            let stem = format!("blk.{il}.ffn_{proj}_exps.{ex}");
1900            if let Some(scale) = src.find(&format!("{stem}.scale")) {
1901                macros[ex] = f32::from_le_bytes(scale.bytes[..4].try_into().unwrap());
1902            }
1903            let (host, byte_len, qtype, row_bytes, cur_in, cur_out) = if let Some(DiskExtent {
1904                map,
1905                file,
1906                offset,
1907                len,
1908            }) =
1909                find_expert_disk_strict(src, &name)?
1910            {
1911                let v = src
1912                    .find(&name)
1913                    .unwrap_or_else(|| panic!("missing expert tensor {name}"));
1914                assert_eq!(v.ne.len(), 2, "expert {name} is not 2D (ne={:?})", v.ne);
1915                let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
1916                let row_bytes = staged_expert_row_bytes(v.ggml_type, cur_in).ok_or_else(|| {
1917                    format!("mmap expert {name} has unsupported qtype {:?}", v.ggml_type)
1918                })?;
1919                let off = usize::try_from(offset).map_err(|_| {
1920                    format!("expert {name} disk offset {offset} does not fit usize")
1921                })?;
1922                (
1923                    HostBuf::Mmap {
1924                        map,
1925                        file,
1926                        off,
1927                        len,
1928                    },
1929                    len,
1930                    staged_expert_qtype(v.ggml_type).unwrap(),
1931                    row_bytes,
1932                    cur_in,
1933                    cur_out,
1934                )
1935            } else if let Some(nv) = src.find_nvfp4_native(&name) {
1936                let bytes = memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
1937                    nv.wbytes, nv.wscale, nv.out_f, nv.in_f,
1938                );
1939                let row_bytes = nv.in_f / 64 * 36;
1940                let byte_len = bytes.len();
1941                (
1942                    HostBuf::Paged(bytes),
1943                    byte_len,
1944                    QT_NVFP4,
1945                    row_bytes,
1946                    nv.in_f,
1947                    nv.out_f,
1948                )
1949            } else {
1950                let v = src
1951                    .find(&name)
1952                    .unwrap_or_else(|| panic!("missing expert tensor {name}"));
1953                assert_eq!(v.ne.len(), 2, "expert {name} is not 2D (ne={:?})", v.ne);
1954                let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
1955                if let Some(row_bytes) = staged_expert_row_bytes(v.ggml_type, cur_in) {
1956                    let bytes = v.bytes.into_owned();
1957                    let byte_len = bytes.len();
1958                    (
1959                        HostBuf::Paged(bytes),
1960                        byte_len,
1961                        staged_expert_qtype(v.ggml_type).unwrap(),
1962                        row_bytes,
1963                        cur_in,
1964                        cur_out,
1965                    )
1966                } else {
1967                    let f32v = dequant::dequantize(v.ggml_type, &v.bytes, cur_in * cur_out);
1968                    let mut bytes = Vec::with_capacity(f32v.len() * 4);
1969                    for f in f32v {
1970                        bytes.extend_from_slice(&f.to_le_bytes());
1971                    }
1972                    let byte_len = bytes.len();
1973                    (
1974                        HostBuf::Paged(bytes),
1975                        byte_len,
1976                        QT_F32,
1977                        cur_in * 4,
1978                        cur_in,
1979                        cur_out,
1980                    )
1981                }
1982            };
1983
1984            if first_active.is_none() {
1985                in_f = cur_in;
1986                out_f = cur_out;
1987                first_active = Some(ex);
1988            } else {
1989                assert_eq!(
1990                    (cur_in, cur_out),
1991                    (in_f, out_f),
1992                    "expert {ex} dims ({cur_in},{cur_out}) != first active expert ({in_f},{out_f})"
1993                );
1994            }
1995            assert_eq!(
1996                byte_len,
1997                cur_out * row_bytes,
1998                "expert {name} bytes {byte_len} != out_f*row_bytes {}",
1999                cur_out * row_bytes
2000            );
2001            layouts.push(ExpertLayout {
2002                offset: 0,
2003                len: byte_len,
2004                qtype,
2005                row_bytes,
2006            });
2007            tiers.push(host);
2008        }
2009
2010        let first = layouts[*first_active
2011            .as_ref()
2012            .expect("expert mask pruned every expert")];
2013        let expert_stride = layouts.iter().map(|layout| layout.len).max().unwrap_or(0);
2014        let all_one = macros.iter().all(|&scale| scale == 1.0);
2015        Ok(HostExps {
2016            bytes: HostBuf::Paged(Vec::new()),
2017            tiers: Some(tiers),
2018            qtype: first.qtype,
2019            in_f,
2020            out_f,
2021            n_expert,
2022            row_bytes: first.row_bytes,
2023            expert_stride,
2024            layouts: Some(layouts),
2025            macros: if all_one { None } else { Some(macros) },
2026        })
2027    }
2028
2029    /// Host byte slice for expert `e` (the H2D DMA source). Contiguous block, offset honored.
2030    /// Resolves the per-expert tier when spilling is active (`tiers` Some), else slices the single
2031    /// Per-expert post-matmul macro-scale (1.0 when absent).
2032    #[inline]
2033    pub fn macro_scale(&self, e: usize) -> f32 {
2034        self.macros.as_ref().map(|m| m[e]).unwrap_or(1.0)
2035    }
2036
2037    #[inline]
2038    pub fn is_uniform_layout(&self) -> bool {
2039        self.layouts.is_none()
2040    }
2041
2042    #[inline]
2043    pub fn expert_layout(&self, e: usize) -> ExpertLayout {
2044        debug_assert!(
2045            e < self.n_expert,
2046            "expert index {e} >= n_expert {}",
2047            self.n_expert
2048        );
2049        self.layouts
2050            .as_ref()
2051            .map(|layouts| layouts[e])
2052            .unwrap_or(ExpertLayout {
2053                offset: e * self.expert_stride,
2054                len: self.expert_stride,
2055                qtype: self.qtype,
2056                row_bytes: self.row_bytes,
2057            })
2058    }
2059
2060    #[inline]
2061    pub fn max_expert_bytes(&self) -> usize {
2062        self.layouts
2063            .as_ref()
2064            .and_then(|layouts| layouts.iter().map(|layout| layout.len).max())
2065            .unwrap_or(self.expert_stride)
2066    }
2067
2068    /// backing store (unchanged in-RAM path). Each `tiers[e]` is exactly one expert's stride.
2069    #[inline]
2070    pub fn expert_bytes(&self, e: usize) -> &[u8] {
2071        let layout = self.expert_layout(e);
2072        match &self.tiers {
2073            Some(tiers) => {
2074                debug_assert_eq!(tiers[e].len(), layout.len);
2075                tiers[e].as_bytes()
2076            }
2077            None => &self.bytes.as_bytes()[layout.offset..layout.offset + layout.len],
2078        }
2079    }
2080
2081    /// Source-aware twin of `expert_bytes`. Per-expert tiers already point at one exact block, while
2082    /// a uniform slab needs the expert layout offset added to its base. Keeping those cases separate
2083    /// prevents expert `e` from being offset twice when a tier vector is present.
2084    #[inline]
2085    pub(crate) fn expert_source(&self, e: usize) -> ExpertSource<'_> {
2086        let layout = self.expert_layout(e);
2087        match &self.tiers {
2088            Some(tiers) => tiers[e].expert_source(0, layout.len),
2089            None => self.bytes.expert_source(layout.offset, layout.len),
2090        }
2091    }
2092
2093    /// Hint that expert `e` will be staged soon. Uniform slabs advise only this expert's window;
2094    /// mixed/pruned layouts advise the selected per-expert mmap. Returns false for resident or
2095    /// empty buffers and on unsupported kernels; callers always retain the demand-fault fallback.
2096    #[inline]
2097    pub fn prefetch_expert_pages(&self, e: usize) -> bool {
2098        let layout = self.expert_layout(e);
2099        match &self.tiers {
2100            Some(tiers) => tiers[e].advise_willneed(0, layout.len),
2101            None => self.bytes.advise_willneed(layout.offset, layout.len),
2102        }
2103    }
2104}
2105
2106#[cfg(test)]
2107mod tests {
2108    use super::{
2109        repack_nvfp4_split, unpack_nvfp4_split, ExpertKeepalive, ExpertSource, HostBuf, HostExps,
2110        QT_BF16, QT_NVFP4, QT_Q2_K, QT_Q4_K,
2111    };
2112    use memra_gguf::nvfp4_repack::{repack_modelopt_to_gguf, repack_modelopt_to_split};
2113    use memra_gguf::source::{DiskExtent, TensorSource, TensorView};
2114    use memra_gguf::{config::ModelConfig, GgmlType};
2115    use std::borrow::Cow;
2116
2117    struct MixedExpertSource {
2118        bf16: Vec<u8>,
2119        q4k: Vec<u8>,
2120    }
2121
2122    impl TensorSource for MixedExpertSource {
2123        fn config(&self) -> ModelConfig {
2124            panic!("unused by HostExps mixed-loader test")
2125        }
2126
2127        fn find(&self, name: &str) -> Option<TensorView<'_>> {
2128            let (bytes, ggml_type) = if name == "blk.0.ffn_gate_exps.0.weight" {
2129                (&self.bf16, GgmlType::BF16)
2130            } else if name == "blk.0.ffn_gate_exps.1.weight" {
2131                (&self.q4k, GgmlType::Q4_K)
2132            } else {
2133                return None;
2134            };
2135            Some(TensorView {
2136                bytes: Cow::Borrowed(bytes),
2137                ggml_type,
2138                ne: vec![256, 2],
2139            })
2140        }
2141    }
2142
2143    struct PrunedExpertSource {
2144        q2k: Vec<u8>,
2145        nvfp4: Vec<u8>,
2146        active: Vec<bool>,
2147    }
2148
2149    struct MmapExpertSource {
2150        file: std::sync::Arc<std::fs::File>,
2151        map: std::sync::Arc<memmap2::Mmap>,
2152        base_offset: usize,
2153        expert_len: usize,
2154    }
2155
2156    struct LegacyMmapExpertSource {
2157        map: std::sync::Arc<memmap2::Mmap>,
2158        expert_len: usize,
2159    }
2160
2161    impl TensorSource for MmapExpertSource {
2162        fn config(&self) -> ModelConfig {
2163            panic!("unused by HostExps mmap-loader test")
2164        }
2165        fn preserve_expert_encodings(&self) -> bool {
2166            true
2167        }
2168        fn find(&self, name: &str) -> Option<TensorView<'_>> {
2169            let ex = match name {
2170                "blk.0.ffn_gate_exps.0.weight" => 0,
2171                "blk.0.ffn_gate_exps.1.weight" => 1,
2172                _ => return None,
2173            };
2174            let off = self.base_offset + ex * self.expert_len;
2175            Some(TensorView {
2176                bytes: Cow::Borrowed(&self.map[off..off + self.expert_len]),
2177                ggml_type: GgmlType::Q2_K,
2178                ne: vec![256, 2],
2179            })
2180        }
2181        fn find_expert_disk(&self, name: &str) -> Option<DiskExtent> {
2182            let ex = match name {
2183                "blk.0.ffn_gate_exps.0.weight" => 0,
2184                "blk.0.ffn_gate_exps.1.weight" => 1,
2185                _ => return None,
2186            };
2187            Some(DiskExtent {
2188                map: self.map.clone(),
2189                file: self.file.clone(),
2190                offset: (self.base_offset + ex * self.expert_len) as u64,
2191                len: self.expert_len,
2192            })
2193        }
2194    }
2195
2196    impl TensorSource for LegacyMmapExpertSource {
2197        fn config(&self) -> ModelConfig {
2198            panic!("unused by legacy mmap guard test")
2199        }
2200        fn preserve_expert_encodings(&self) -> bool {
2201            true
2202        }
2203        fn find(&self, name: &str) -> Option<TensorView<'_>> {
2204            let ex = match name {
2205                "blk.0.ffn_gate_exps.0.weight" => 0,
2206                "blk.0.ffn_gate_exps.1.weight" => 1,
2207                _ => return None,
2208            };
2209            let off = ex * self.expert_len;
2210            Some(TensorView {
2211                bytes: Cow::Borrowed(&self.map[off..off + self.expert_len]),
2212                ggml_type: GgmlType::Q2_K,
2213                ne: vec![256, 2],
2214            })
2215        }
2216        fn find_expert_mmap(
2217            &self,
2218            name: &str,
2219        ) -> Option<(std::sync::Arc<memmap2::Mmap>, usize, usize)> {
2220            let ex = match name {
2221                "blk.0.ffn_gate_exps.0.weight" => 0,
2222                "blk.0.ffn_gate_exps.1.weight" => 1,
2223                _ => return None,
2224            };
2225            Some((self.map.clone(), ex * self.expert_len, self.expert_len))
2226        }
2227    }
2228
2229    impl TensorSource for PrunedExpertSource {
2230        fn config(&self) -> ModelConfig {
2231            panic!("unused by HostExps pruned-loader test")
2232        }
2233        fn active_experts(&self, layer: u32) -> Option<&[bool]> {
2234            (layer == 0).then_some(self.active.as_slice())
2235        }
2236        fn find(&self, name: &str) -> Option<TensorView<'_>> {
2237            let (bytes, ggml_type) = match name {
2238                "blk.0.ffn_gate_exps.0.weight" => (&self.q2k, GgmlType::Q2_K),
2239                "blk.0.ffn_gate_exps.2.weight" => (&self.nvfp4, GgmlType::NVFP4),
2240                _ => return None,
2241            };
2242            Some(TensorView {
2243                bytes: Cow::Borrowed(bytes),
2244                ggml_type,
2245                ne: vec![256, 2],
2246            })
2247        }
2248    }
2249
2250    /// A1 direct-import gate (engine side): the fused modelopt->split repack must be byte-for-byte
2251    /// the composition of the two passes it replaces (modelopt->GGUF blocks, then the A6
2252    /// split-plane repack). Also pins the split roundtrip on the same buffers.
2253    #[test]
2254    fn direct_split_equals_chained() {
2255        for (out_f, in_f) in [(1usize, 64usize), (3, 128), (5, 320), (8, 1024)] {
2256            let mut w = vec![0u8; out_f * in_f / 2];
2257            let mut s = vec![0u8; out_f * in_f / 16];
2258            for (i, b) in w.iter_mut().enumerate() {
2259                *b = ((i * 41 + 7) & 0xFF) as u8;
2260            }
2261            for (i, b) in s.iter_mut().enumerate() {
2262                *b = (0x20 + ((i * 11 + 5) % 0x50)) as u8;
2263            }
2264            let gguf = repack_modelopt_to_gguf(&w, &s, out_f, in_f);
2265            let chained = repack_nvfp4_split(&gguf, out_f);
2266            let direct = repack_modelopt_to_split(&w, &s, out_f, in_f);
2267            assert_eq!(
2268                direct, chained,
2269                "fused != chained at out_f={out_f} in_f={in_f}"
2270            );
2271            assert_eq!(
2272                unpack_nvfp4_split(&direct, out_f),
2273                gguf,
2274                "split roundtrip broken at out_f={out_f} in_f={in_f}"
2275            );
2276        }
2277    }
2278
2279    #[test]
2280    fn mixed_expert_loader_keeps_each_encoding_and_extent() {
2281        let source = MixedExpertSource {
2282            bf16: vec![0x5a; 256 * 2 * 2],
2283            q4k: vec![0xa5; 2 * 144],
2284        };
2285        let exps = HostExps::load_mixed_from_source(&source, 0, "gate", 2).unwrap();
2286        assert!(!exps.is_uniform_layout());
2287        assert_eq!(exps.max_expert_bytes(), 1024);
2288        assert_eq!(exps.expert_layout(0).qtype, QT_BF16);
2289        assert_eq!(exps.expert_layout(0).row_bytes, 512);
2290        assert_eq!(exps.expert_layout(0).len, 1024);
2291        assert_eq!(exps.expert_layout(1).qtype, QT_Q4_K);
2292        assert_eq!(exps.expert_layout(1).row_bytes, 144);
2293        assert_eq!(exps.expert_layout(1).len, 288);
2294        assert_eq!(exps.expert_bytes(0), source.bf16);
2295        assert_eq!(exps.expert_bytes(1), source.q4k);
2296        match exps.expert_source(1) {
2297            ExpertSource::Memory { bytes, .. } => assert_eq!(bytes, source.q4k),
2298            ExpertSource::Disk { .. } => panic!("paged expert unexpectedly became disk-backed"),
2299        }
2300    }
2301
2302    #[test]
2303    fn mixed_expert_loader_omits_masked_expert_bytes() {
2304        let source = PrunedExpertSource {
2305            q2k: vec![0x22; 2 * 84],
2306            nvfp4: vec![0x44; 2 * 4 * 36],
2307            active: vec![true, false, true],
2308        };
2309        let exps = HostExps::load_mixed_from_source(&source, 0, "gate", 3).unwrap();
2310        assert_eq!(exps.expert_layout(0).qtype, QT_Q2_K);
2311        assert_eq!(exps.expert_layout(0).row_bytes, 84);
2312        assert_eq!(exps.expert_layout(1).len, 0);
2313        assert_eq!(exps.expert_bytes(1), &[]);
2314        assert_eq!(exps.expert_layout(2).qtype, QT_NVFP4);
2315        assert_eq!(exps.expert_layout(2).row_bytes, 4 * 36);
2316    }
2317
2318    #[test]
2319    fn mixed_expert_loader_keeps_mmap_backing_zero_copy() {
2320        let path = std::env::temp_dir().join(format!("memra-mixed-mmap-{}", std::process::id()));
2321        let base_offset = 3usize;
2322        let expert_len = 2 * 84;
2323        let mut bytes = vec![0xE1; base_offset];
2324        bytes.extend(vec![0x31; expert_len]);
2325        bytes.extend(vec![0x72; expert_len]);
2326        std::fs::write(&path, &bytes).unwrap();
2327        let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
2328        let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
2329        let source = MmapExpertSource {
2330            file: file.clone(),
2331            map,
2332            base_offset,
2333            expert_len,
2334        };
2335        let exps = HostExps::load_mixed_from_source(&source, 0, "gate", 2).unwrap();
2336        assert!(matches!(
2337            exps.tiers.as_ref().unwrap()[0],
2338            HostBuf::Mmap { .. }
2339        ));
2340        assert!(matches!(
2341            exps.tiers.as_ref().unwrap()[1],
2342            HostBuf::Mmap { .. }
2343        ));
2344        assert_eq!(
2345            exps.expert_bytes(0),
2346            &bytes[base_offset..base_offset + expert_len]
2347        );
2348        assert_eq!(exps.expert_bytes(1), &bytes[base_offset + expert_len..]);
2349        match exps.expert_source(1) {
2350            ExpertSource::Disk {
2351                file: got_file,
2352                offset,
2353                len,
2354                fallback,
2355                keepalive,
2356            } => {
2357                assert!(std::sync::Arc::ptr_eq(got_file, &file));
2358                assert_eq!(offset, (base_offset + expert_len) as u64);
2359                assert_eq!(len, expert_len);
2360                assert_eq!(fallback, &bytes[base_offset + expert_len..]);
2361                match keepalive {
2362                    ExpertKeepalive::Mmap(owner) => {
2363                        assert!(std::sync::Arc::ptr_eq(&owner, &source.map));
2364                    }
2365                    _ => panic!("mmap expert did not retain its mmap owner"),
2366                }
2367            }
2368            ExpertSource::Memory { .. } => panic!("mixed mmap tier lost its disk extent"),
2369        }
2370        #[cfg(unix)]
2371        assert!(exps.prefetch_expert_pages(1));
2372        std::fs::remove_file(path).ok();
2373    }
2374
2375    #[test]
2376    fn tiered_expert_source_does_not_double_apply_layout_offset() {
2377        let path =
2378            std::env::temp_dir().join(format!("memra-tiered-source-offset-{}", std::process::id()));
2379        let base_offset = 7usize;
2380        let expert_len = 2 * 84;
2381        let mut bytes = vec![0xE3; base_offset];
2382        bytes.extend(vec![0x41; expert_len]);
2383        bytes.extend(vec![0x82; expert_len]);
2384        std::fs::write(&path, &bytes).unwrap();
2385        let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
2386        let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
2387        let exps = HostExps {
2388            bytes: HostBuf::Paged(Vec::new()),
2389            tiers: Some(vec![
2390                HostBuf::Mmap {
2391                    map: map.clone(),
2392                    file: file.clone(),
2393                    off: base_offset,
2394                    len: expert_len,
2395                },
2396                HostBuf::Mmap {
2397                    map,
2398                    file: file.clone(),
2399                    off: base_offset + expert_len,
2400                    len: expert_len,
2401                },
2402            ]),
2403            qtype: QT_Q2_K,
2404            in_f: 256,
2405            out_f: 2,
2406            n_expert: 2,
2407            row_bytes: 84,
2408            expert_stride: expert_len,
2409            layouts: None,
2410            macros: None,
2411        };
2412
2413        // `expert_layout(1).offset == expert_len`, but tier 1 already starts at expert 1.
2414        assert_eq!(exps.expert_layout(1).offset, expert_len);
2415        match exps.expert_source(1) {
2416            ExpertSource::Disk {
2417                offset,
2418                len,
2419                fallback,
2420                ..
2421            } => {
2422                assert_eq!(offset, (base_offset + expert_len) as u64);
2423                assert_eq!(len, expert_len);
2424                assert_eq!(fallback, &bytes[base_offset + expert_len..]);
2425            }
2426            ExpertSource::Memory { .. } => panic!("tiered mmap expert lost its disk extent"),
2427        }
2428        std::fs::remove_file(path).ok();
2429    }
2430
2431    #[test]
2432    fn legacy_mmap_source_requires_retained_file_extent() {
2433        let path =
2434            std::env::temp_dir().join(format!("memra-legacy-mmap-source-{}", std::process::id()));
2435        let expert_len = 2 * 84;
2436        std::fs::write(&path, vec![0x64; 2 * expert_len]).unwrap();
2437        let file = std::fs::File::open(&path).unwrap();
2438        let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(&file).unwrap() });
2439        let source = LegacyMmapExpertSource { map, expert_len };
2440
2441        let err = match HostExps::load_uniform_mmap_from_source(&source, 0, "gate", 2) {
2442            Ok(_) => panic!("legacy mmap-only source silently fell back instead of failing"),
2443            Err(err) => err,
2444        };
2445        let message = err.to_string();
2446        assert!(
2447            message.contains("legacy find_expert_mmap without find_expert_disk"),
2448            "{message}"
2449        );
2450        assert!(message.contains("retained Arc<File>"), "{message}");
2451        std::fs::remove_file(path).ok();
2452    }
2453
2454    #[test]
2455    fn uniform_expert_loader_coalesces_contiguous_mmap() {
2456        let path = std::env::temp_dir().join(format!("memra-uniform-mmap-{}", std::process::id()));
2457        let base_offset = 5usize;
2458        let expert_len = 2 * 84;
2459        let mut bytes = vec![0xE2; base_offset];
2460        bytes.extend(vec![0x19; expert_len]);
2461        bytes.extend(vec![0x91; expert_len]);
2462        std::fs::write(&path, &bytes).unwrap();
2463        let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
2464        let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
2465        let source = MmapExpertSource {
2466            file: file.clone(),
2467            map,
2468            base_offset,
2469            expert_len,
2470        };
2471        let exps = HostExps::load_uniform_mmap_from_source(&source, 0, "gate", 2)
2472            .unwrap()
2473            .expect("contiguous mmap should coalesce");
2474        assert!(exps.is_uniform_layout());
2475        assert!(matches!(&exps.bytes, HostBuf::Mmap { .. }));
2476        assert_eq!(exps.expert_stride, expert_len);
2477        assert_eq!(
2478            exps.expert_bytes(0),
2479            &bytes[base_offset..base_offset + expert_len]
2480        );
2481        assert_eq!(exps.expert_bytes(1), &bytes[base_offset + expert_len..]);
2482        match exps.expert_source(1) {
2483            ExpertSource::Disk {
2484                file: got_file,
2485                offset,
2486                len,
2487                fallback,
2488                ..
2489            } => {
2490                assert!(std::sync::Arc::ptr_eq(got_file, &file));
2491                assert_eq!(offset, (base_offset + expert_len) as u64);
2492                assert_eq!(len, expert_len);
2493                assert_eq!(fallback, &bytes[base_offset + expert_len..]);
2494            }
2495            ExpertSource::Memory { .. } => panic!("uniform mmap slab lost its disk extent"),
2496        }
2497        #[cfg(unix)]
2498        assert!(exps.prefetch_expert_pages(1));
2499        std::fs::remove_file(path).ok();
2500    }
2501}