Skip to main content

memra_engine/
tp.rs

1//! Tensor-parallel correctness runtime.
2//!
3//! This module is deliberately narrower than the serving runtime. It executes real rank-local
4//! E4M3 projections on distinct CUDA devices. Deterministic host-staged collectives remain the
5//! default exactness reference; an opt-in native-P2P path must reproduce the same canonical
6//! checkpoint-block program before it can advance. Neither path is product-throughput evidence.
7
8use crate::Engine;
9use crate::mmq_ffi::{DeviceExpertCsr, ExpertCsr, Fp8GroupedWorkspace};
10use crate::parallel::{PRODUCT_MAX_CARDS, STEP37_TRUNK_LAYERS};
11use cudarc::driver::{CudaEvent, CudaSlice, DeviceSlice};
12use std::ops::Range;
13
14/// Previous gate output per (rank, t), so the determ probe can report the SHAPE of a divergence
15/// (dense-ULP vs sparse-huge) and not merely that a checksum moved. Probe-only state.
16static DETERM_PREV: std::sync::OnceLock<
17    std::sync::Mutex<std::collections::HashMap<(usize, usize), Vec<f32>>>,
18> = std::sync::OnceLock::new();
19
20const FP8_BLOCK: usize = 128;
21const NATIVE_P2P_PROBE_WORDS: &[usize] = &[4096, 16_384, 262_144, 16_777_216];
22const STEP_GROUPED_FP8_EXPERTS: usize = 288;
23const STEP_GROUPED_FP8_TOP_K: usize = 8;
24const STEP_GROUPED_FP8_WIDTH: usize = 1280;
25
26fn validate_step_expert_activation_limit(limit: Option<f32>) -> Result<(), String> {
27    if let Some(limit) = limit {
28        if !limit.is_finite() || limit <= 0.0 {
29            return Err(format!(
30                "Step routed-expert activation limit must be positive and finite, got {limit}"
31            ));
32        }
33    }
34    Ok(())
35}
36
37/// Host-canonical Step routed-expert SwiGLU operation.
38///
39/// Step's final routed layers clamp the linear arm symmetrically and the SiLU arm only above.
40/// Keeping this scalar order explicit also defines the device-host-exact CUDA gate.
41/// Raw stream-ordered device copy for capture-safe cross-context seams (cudarc's slice-use
42/// tracking creates capture-illegal dependencies there). Pointers must be pre-cached with
43/// their owners' streams; bytes flow identically to the tracked copy.
44/// MEMRA_OPROJ_DIRECT=1 (o-proj direct join, default OFF until gated): peer ranks write
45/// their fused O partial OVER P2P into a root-resident buffer (UVA kernel stores), and the
46/// model engine adds the two partials itself — the root stream leaves the join entirely
47/// (no peer pull copy, no root add, no second event hop, no final 16KB ownership copy).
48/// Reduction order and kernel programs are unchanged, so the row is BIT-IDENTICAL.
49/// MEMRA_MOE_DIRECT=1 (moe direct join, default OFF until gated): the o-proj direct-join
50/// recipe on the expert combine — peer ranks' accumulators live root-side (the axpy twin
51/// register-accumulates and stores ONCE, so the P2P cost is a single 16KB store pass), and
52/// the model engine adds the two shard rows itself. Operand order matches root's add:
53/// BIT-IDENTICAL.
54/// MEMRA_ROUTES_PRESTAGE=1 (default OFF until gated): stage the shared layer input to
55/// every rank and quantize it BEFORE the router runs — neither depends on the selection,
56/// so the rank streams' pull+quantize overlaps dev0's router gemv+topk instead of chaining
57/// behind it (the router->quantize and axpy->add gap edges). Same copies, same quantize
58/// kernel, same operands: BIT-IDENTICAL.
59pub(crate) fn routes_prestage_on() -> bool {
60    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
61    *ON.get_or_init(|| std::env::var("MEMRA_ROUTES_PRESTAGE").as_deref() == Ok("1"))
62}
63
64/// MEMRA_FENCE_MEMOPS=1 (default OFF until gated): the moe direct join's two event
65/// fences become cuStreamWriteValue32/cuStreamWaitValue32 doorbells — hardware stream
66/// memops with lower signal->wake latency than cross-device cuStreamWaitEvent. Ordering:
67/// PCIe posted writes from one device arrive in order, so rank1's accumulator stores are
68/// visible before its flag write lands; e's GEQ wait then covers them. Falls back to
69/// events when the device rejects stream memops. Scheduling-only: BIT-IDENTICAL values.
70/// MEMRA_LEN_MIRROR_LAZY=1 (default OFF until gated): skip redundant per-layer 4B len
71/// htods — the local device mirror is unread in TP decode, and under FUSE_ROPE_APPEND the
72/// fused append's atomicInc owns the rank counters. Every one of those tiny copies is a
73/// compute->copy engine turnaround in the middle of the layer stream.
74/// MEMRA_RANK0_MERGE=1 (default OFF until gated): same-device rank0 rides e's stream via
75/// the runtime redirect — see decode_step_h.
76/// MEMRA_OPROJ_TAIL=1 (default OFF until gated): the o-proj direct-join add is DEFERRED —
77/// the finish arm keeps its waits, stores the two partial pointers here, and the residual
78/// add_rms_norm consumer composes mixed = a0+a1 in-register (join_add_rms_norm, verbatim
79/// program: BIT-IDENTICAL). The returned `mixed` buffer is UNWRITTEN in this mode; its
80/// only live consumer is the residual_norm_ffn seam, which takes the handoff.
81pub(crate) fn oproj_tail_on() -> bool {
82    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
83    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_TAIL").as_deref() == Ok("1"))
84}
85thread_local! {
86    static OPROJ_TAIL_PENDING: std::cell::Cell<Option<(u64, u64)>> =
87        const { std::cell::Cell::new(None) };
88}
89thread_local! {
90    /// The deferral is legal ONLY under callers whose walk flows into
91    /// residual_norm_ffn (decode_step_h / decode_step_chain arm this) — the verify
92    /// prefill reaches the same finish and would consume unwritten `mixed` otherwise
93    /// (M2-MISMATCH receipt: prefill argmax corrupted while decode stayed exact).
94    static OPROJ_TAIL_ELIGIBLE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
95}
96/// RAII eligibility scope for the o-proj tail deferral.
97pub(crate) struct OprojTailScope(());
98pub(crate) fn oproj_tail_scope() -> OprojTailScope {
99    OPROJ_TAIL_ELIGIBLE.with(|c| c.set(true));
100    OprojTailScope(())
101}
102impl Drop for OprojTailScope {
103    fn drop(&mut self) {
104        OPROJ_TAIL_ELIGIBLE.with(|c| c.set(false));
105        // A leftover un-consumed handoff must never leak across calls.
106        OPROJ_TAIL_PENDING.with(|c| c.set(None));
107    }
108}
109thread_local! {
110    /// T-COLUMN verify select: the verify driver sets the column before each per-column
111    /// attention call; decode_v2_input_qkv takes it (once) and selects from the slabs.
112    static VERIFY_TCOL: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
113}
114pub(crate) fn set_verify_tcol(c: Option<usize>) {
115    VERIFY_TCOL.with(|x| x.set(c));
116}
117pub(crate) fn take_verify_tcol() -> Option<usize> {
118    VERIFY_TCOL.with(|x| x.take())
119}
120
121/// MEMRA_TCOL_OPROJ=1 (spec verify): defer each column's o_proj out of the per-column
122/// walk — the finish seam stashes the column's `gated` rows instead of running the
123/// per-column finish choreography (rank events, P2P join, engine handoff), and one
124/// weight-amortized b4_tcol per rank + one elementwise join produce every column's
125/// `mixed` afterwards. Bit-exact per column: the tcol kernel is the t=1 b4 program per
126/// column, and the slab join adds the same operand values elementwise.
127pub(crate) fn tcol_oproj_on() -> bool {
128    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
129    *ON.get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ").as_deref() == Ok("1"))
130}
131thread_local! {
132    /// The verify driver arms the column before each per-column attention call; the
133    /// finish seam takes it (once). Stashed=true reports the defer actually happened
134    /// (the seam falls back to the normal finish when the config is ineligible).
135    static TCOL_OPROJ_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
136    static TCOL_OPROJ_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
137}
138pub(crate) fn set_tcol_oproj_defer(c: Option<usize>) {
139    TCOL_OPROJ_DEFER.with(|x| x.set(c));
140}
141pub(crate) fn take_tcol_oproj_defer() -> Option<usize> {
142    TCOL_OPROJ_DEFER.with(|x| x.take())
143}
144pub(crate) fn set_tcol_oproj_stashed() {
145    TCOL_OPROJ_STASHED.with(|x| x.set(true));
146}
147pub(crate) fn take_tcol_oproj_stashed() -> bool {
148    TCOL_OPROJ_STASHED.with(|x| x.replace(false))
149}
150
151pub(crate) fn oproj_tail_eligible() -> bool {
152    OPROJ_TAIL_ELIGIBLE.with(|c| c.get())
153}
154pub(crate) fn take_oproj_tail() -> Option<(u64, u64)> {
155    OPROJ_TAIL_PENDING.with(|c| c.take())
156}
157pub(crate) fn set_oproj_tail(v: (u64, u64)) {
158    OPROJ_TAIL_PENDING.with(|c| c.set(Some(v)));
159}
160
161pub(crate) fn rank0_merge_on() -> bool {
162    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
163    *ON.get_or_init(|| std::env::var("MEMRA_RANK0_MERGE").as_deref() == Ok("1"))
164}
165
166pub(crate) fn len_mirror_lazy_on() -> bool {
167    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
168    *ON.get_or_init(|| std::env::var("MEMRA_LEN_MIRROR_LAZY").as_deref() == Ok("1"))
169}
170
171pub(crate) fn fence_memops_on() -> bool {
172    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
173    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_MEMOPS").as_deref() == Ok("1"))
174}
175
176pub(crate) fn moe_direct_on() -> bool {
177    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
178    *ON.get_or_init(|| std::env::var("MEMRA_MOE_DIRECT").as_deref() == Ok("1"))
179}
180
181/// MEMRA_SEL_DOWN8=1: fuse the NVFP4 down sweep with the route-weight combine and run one
182/// warp per routed slot (the q8 `down8 w8` occupancy arm). Bit-identical; default OFF until
183/// receipted on this bank family.
184/// MEMRA_SEL_MIRROR=1: the per-rank routed-selection pull runs as ONE `moe_sel_w_mirror`
185/// launch instead of two 32-byte D2D copies, and when every consuming rank shares e's device
186/// the intermediate e-context staging pair is skipped entirely (the caller's sel/route_w rows
187/// are process-persistent, so the ranks read them directly). Bit-identical: same bytes, one
188/// fewer hop. Refused under the graph door, whose captured copies need the fixed staging
189/// addresses. Default OFF until receipted.
190/// MEMRA_FENCE_RANK1=1: the peer rank rings a doorbell in ROOT memory with a kernel store
191/// (`memra_ring_flag`) and the model engine waits it with a SAME-DEVICE stream memop, instead
192/// of waiting a cross-device event. Completes the half the memops receipt left open (peer
193/// memops are rejected; peer kernel stores are the direct-join mechanism). Ordering only —
194/// values are untouched. Requires MEMRA_FENCE_MEMOPS=1 (it owns the flag allocation).
195pub(crate) fn fence_rank1_on() -> bool {
196    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
197    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_RANK1").as_deref() == Ok("1"))
198}
199
200/// MEMRA_SPEC_FA2=1 (the DSpark verify lesson): the T=2 verify walk defers each column's
201/// ATTENTION CORE — the dcw arm appends the column's K/V and stashes its post-rope q and
202/// gate rows, then ONE fa_decode_dcw2 per rank walks the KV stream once for both columns
203/// (per-row causal bounds; bit-identical per row under the equal-partition guard), the
204/// per-row combine writes both gated rows, and the o_proj join runs on the TCOL slabs.
205/// ROW-TABLE RESTAGE (`MEMRA_ROWS_TAB_RESTAGE`, DEFAULT ON since this lane).
206///
207/// ON: `decode_v2_rope_fa_rows` builds the 6-word-per-row pointer table from the caller's
208/// freshly-read live cache pointers and stages it into a persistent per-rank slab before
209/// every launch. OFF (`=0`): the retired process-lifetime `rows_tabs` memo, keyed by a hash
210/// of (k pointer, base pointer, layer, t) that could not see the V or LEN pointers the
211/// entry also carried, and that nothing invalidated when a session's KV cache was dropped.
212///
213/// Default ON because the OFF arm is a proven use-after-free, not a slower correct path:
214/// on step37-flash with MEMRA_FUSE_ROPE_APPEND=1 it made speculative decoding unservable
215/// (whole non-finite verify rows, then CUDA_ERROR_ILLEGAL_ADDRESS). ON is value-neutral on
216/// every fresh lookup by construction: identical bytes reach the same kernels. Rollback
217/// seam: `MEMRA_ROWS_TAB_RESTAGE=0`.
218/// The 6-word-per-row launch table `{k, v, len, base, ctr, back}` the fused rope/append/fa
219/// kernels dereference. Pure so it can be tested: the words come from the caller's live
220/// per-row `[k, v, len, base]` pointers, `ctr` is this rank's counter slab (one shared cell
221/// for same-session rows, one cell per row otherwise) and `back` is the same-session causal
222/// step-back `t-1-r` (0 across sessions, where each row owns its own len).
223pub(crate) fn rows_tab_host(
224    parts_rank: &[[u64; 4]],
225    ctr_base: u64,
226    same_session: bool,
227    t: usize,
228) -> Vec<u64> {
229    let mut host = Vec::with_capacity(t * 6);
230    for (r, parts) in parts_rank.iter().enumerate().take(t) {
231        host.extend_from_slice(&[
232            parts[0],
233            parts[1],
234            parts[2],
235            parts[3],
236            if same_session {
237                ctr_base
238            } else {
239                ctr_base + (r as u64) * 4
240            },
241            if same_session {
242                (t - 1 - r) as u64
243            } else {
244                0u64
245            },
246        ]);
247    }
248    host
249}
250
251/// The RETIRED memo key, kept ONLY so a test can assert what it cannot see. Both historical
252/// call sites hashed a SUBSET of the pointers the table carries; this reproduces the verify
253/// site's formula verbatim.
254#[cfg(test)]
255pub(crate) fn retired_rows_tab_key(kp: u64, bp: u64, il: usize, t: usize) -> u64 {
256    kp.rotate_left(17)
257        .wrapping_add(bp)
258        .wrapping_add((il as u64) << 32)
259        .wrapping_add(t as u64)
260        .wrapping_add(1 << 63)
261}
262
263pub(crate) fn rows_tab_restage_on() -> bool {
264    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
265    *ON.get_or_init(|| std::env::var("MEMRA_ROWS_TAB_RESTAGE").as_deref() != Ok("0"))
266}
267
268/// STALE-HIT RECEIPT (`MEMRA_ROWS_TAB_STALE_SCAN`, DEFAULT OFF, diagnostic only).
269///
270/// Keeps a HOST shadow of the last table staged under each retired memo key and prints one
271/// line whenever the key repeats with different contents, naming the words that moved. It
272/// costs a host hash lookup and a small clone per rank per layer per verify round, so it is
273/// off in serving. `[rows-tab] engaged=` on the counter proves the path executes at all,
274/// which is what separates "the memo was innocent" from "the memo never ran".
275/// Rollback seam: unset it (or `=0`).
276pub(crate) fn rows_tab_stale_scan() -> bool {
277    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
278    *ON.get_or_init(|| std::env::var("MEMRA_ROWS_TAB_STALE_SCAN").as_deref() == Ok("1"))
279}
280
281pub(crate) static ROWS_TAB_ENGAGED: std::sync::atomic::AtomicU64 =
282    std::sync::atomic::AtomicU64::new(0);
283pub(crate) static ROWS_TAB_STALE: std::sync::atomic::AtomicU64 =
284    std::sync::atomic::AtomicU64::new(0);
285
286pub(crate) fn spec_fa2_on() -> bool {
287    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
288    crate::step37_door(&ENV, "MEMRA_SPEC_FA2")
289}
290thread_local! {
291    /// The verify driver arms the column before each per-column attention call; the dcw
292    /// arm takes it (once) and stashes q/gate instead of running fa+finish.
293    static SPEC_FA2_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
294    static SPEC_FA2_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
295}
296pub(crate) fn set_spec_fa2_defer(c: Option<usize>) {
297    SPEC_FA2_DEFER.with(|x| x.set(c));
298}
299pub(crate) fn take_spec_fa2_defer() -> Option<usize> {
300    SPEC_FA2_DEFER.with(|x| x.take())
301}
302pub(crate) fn set_spec_fa2_stashed() {
303    SPEC_FA2_STASHED.with(|x| x.set(true));
304}
305pub(crate) fn take_spec_fa2_stashed() -> bool {
306    SPEC_FA2_STASHED.with(|x| x.replace(false))
307}
308
309pub(crate) fn sel_mirror_on() -> bool {
310    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
311    *ON.get_or_init(|| std::env::var("MEMRA_SEL_MIRROR").as_deref() == Ok("1"))
312}
313
314/// MEMRA_STEP_NVFP4_EP2=1: whole-expert (expert-parallel) NVFP4 banks at 2 ranks — expert e
315/// lives ENTIRE on rank (e & 1) at bank slot (e >> 1), replacing the TP column/row shards
316/// (same total VRAM; both sets cannot coexist). Decode rides owner-guarded full-width
317/// sweeps with per-rank slot-ordered partial sums; the cross-rank join is unchanged.
318/// NUMERIC-CLASS door (the slot chain regroups per rank): run-gen argmax gate + battery +
319/// fresh tape, the DEV_ROUTES acceptance class.
320pub(crate) fn step_nvfp4_ep2_on() -> bool {
321    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
322    *ON.get_or_init(|| std::env::var("MEMRA_STEP_NVFP4_EP2").as_deref() == Ok("1"))
323}
324
325pub(crate) fn sel_down8_on() -> bool {
326    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
327    *ON.get_or_init(|| std::env::var("MEMRA_SEL_DOWN8").as_deref() == Ok("1"))
328}
329
330pub(crate) fn oproj_direct_on() -> bool {
331    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
332    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
333}
334
335pub(crate) fn raw_copy_bytes(
336    dst: u64,
337    src: u64,
338    bytes: usize,
339    engine: &Engine,
340) -> Result<(), Box<dyn std::error::Error>> {
341    use cudarc::driver::sys;
342    let r = unsafe {
343        sys::cuMemcpyAsync(
344            dst as sys::CUdeviceptr,
345            src as sys::CUdeviceptr,
346            bytes,
347            engine.stream().cu_stream() as sys::CUstream,
348        )
349    };
350    if r == sys::CUresult::CUDA_SUCCESS {
351        Ok(())
352    } else {
353        // MEMRA_RAW_COPY_TRACE=1: a raw D2D failure carries no call site by itself, and
354        // every slab-width bug in the t-row family surfaces here. Operands + backtrace.
355        if std::env::var("MEMRA_RAW_COPY_TRACE").as_deref() == Ok("1") {
356            eprintln!(
357                "[raw-copy-fail] dst={dst:#x} src={src:#x} bytes={bytes} {r:?}\n{}",
358                std::backtrace::Backtrace::force_capture()
359            );
360        }
361        Err(format!("raw_copy_bytes: {r:?} bytes={bytes} dst={dst:#x} src={src:#x}").into())
362    }
363}
364
365pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
366    let silu = gate / (1.0 + (-gate).exp());
367    match limit {
368        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
369        None => silu * up,
370    }
371}
372
373#[derive(Debug, Clone, PartialEq, Eq)]
374struct ExpertOwnerRoutes {
375    rank: usize,
376    selected: Vec<usize>,
377    token_rows: Vec<usize>,
378    global_pairs: Vec<usize>,
379}
380
381fn partition_expert_owner_routes(
382    expert_count: usize,
383    ranks: usize,
384    tokens: usize,
385    experts_per_token: usize,
386    selected: &[usize],
387) -> Result<Vec<ExpertOwnerRoutes>, String> {
388    if expert_count == 0
389        || ranks == 0
390        || tokens == 0
391        || experts_per_token == 0
392        || expert_count % ranks != 0
393    {
394        return Err(format!(
395            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
396             tokens={tokens} experts_per_token={experts_per_token}"
397        ));
398    }
399    let pairs = tokens
400        .checked_mul(experts_per_token)
401        .ok_or("expert-owner route count overflow")?;
402    if selected.len() != pairs {
403        return Err(format!(
404            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
405            selected.len()
406        ));
407    }
408    let per_rank = expert_count / ranks;
409    let mut owners = (0..ranks)
410        .map(|rank| ExpertOwnerRoutes {
411            rank,
412            selected: Vec::new(),
413            token_rows: Vec::new(),
414            global_pairs: Vec::new(),
415        })
416        .collect::<Vec<_>>();
417    for (pair, &expert) in selected.iter().enumerate() {
418        if expert >= expert_count {
419            return Err(format!(
420                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
421            ));
422        }
423        let rank = expert / per_rank;
424        owners[rank].selected.push(expert - rank * per_rank);
425        owners[rank].token_rows.push(pair / experts_per_token);
426        owners[rank].global_pairs.push(pair);
427    }
428    Ok(owners)
429}
430
431fn validate_step_grouped_owner_routes(
432    expert_count: usize,
433    tokens: usize,
434    selected: &[usize],
435) -> Result<usize, String> {
436    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
437        return Err(format!(
438            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
439             experts={expert_count} tokens={tokens}",
440            STEP_GROUPED_FP8_EXPERTS
441        ));
442    }
443    let pairs = tokens
444        .checked_mul(STEP_GROUPED_FP8_TOP_K)
445        .ok_or("official Step owner-grouped FP8 route count overflow")?;
446    if selected.len() != pairs {
447        return Err(format!(
448            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
449            selected.len(),
450            STEP_GROUPED_FP8_TOP_K,
451        ));
452    }
453    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
454        let mut unique = routes.to_vec();
455        unique.sort_unstable();
456        unique.dedup();
457        if unique.len() != STEP_GROUPED_FP8_TOP_K {
458            return Err(format!(
459                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
460                 {routes:?}"
461            ));
462        }
463    }
464    Ok(pairs)
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468struct WeightedRouteCombineShape {
469    pairs: usize,
470    max_pairs: usize,
471}
472
473fn validate_weighted_route_combine(
474    width: usize,
475    experts_per_token: usize,
476    max_tokens: usize,
477    tokens: usize,
478    owner_global_pairs: &[&[usize]],
479    route_weights: &[f32],
480) -> Result<WeightedRouteCombineShape, String> {
481    if width == 0
482        || experts_per_token == 0
483        || max_tokens == 0
484        || tokens == 0
485        || tokens > max_tokens
486        || width > i32::MAX as usize
487        || experts_per_token > i32::MAX as usize
488        || tokens > i32::MAX as usize
489    {
490        return Err(format!(
491            "invalid weighted route combine geometry width={width} experts_per_token=\
492             {experts_per_token} tokens={tokens}/{max_tokens}"
493        ));
494    }
495    let pairs = tokens
496        .checked_mul(experts_per_token)
497        .ok_or("weighted route combine pair count overflow")?;
498    let max_pairs = max_tokens
499        .checked_mul(experts_per_token)
500        .ok_or("weighted route combine capacity overflow")?;
501    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
502        return Err(format!(
503            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
504            route_weights.len()
505        ));
506    }
507    let mut seen = vec![false; pairs];
508    let mut observed = 0usize;
509    for pairs_for_owner in owner_global_pairs {
510        observed = observed
511            .checked_add(pairs_for_owner.len())
512            .ok_or("weighted route combine observed pair count overflow")?;
513        for &pair in *pairs_for_owner {
514            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
515                return Err(format!(
516                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
517                ));
518            }
519        }
520    }
521    if observed != pairs || seen.iter().any(|present| !present) {
522        return Err(format!(
523            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
524        ));
525    }
526    Ok(WeightedRouteCombineShape { pairs, max_pairs })
527}
528
529fn cache_rank_rows(
530    rows: &[u8],
531    tokens: usize,
532    local_token_bytes: usize,
533    ranks: usize,
534    rank: usize,
535) -> Result<Vec<u8>, String> {
536    if ranks == 0 || rank >= ranks {
537        return Err(format!(
538            "TP cache rank {rank} is outside a {ranks}-rank layout"
539        ));
540    }
541    let global_token_bytes = local_token_bytes
542        .checked_mul(ranks)
543        .ok_or("TP cache global token-byte overflow")?;
544    let expected = tokens
545        .checked_mul(global_token_bytes)
546        .ok_or("TP cache row-byte overflow")?;
547    if rows.len() != expected {
548        return Err(format!(
549            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
550            rows.len()
551        ));
552    }
553    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
554    for token in 0..tokens {
555        let start = token * global_token_bytes + rank * local_token_bytes;
556        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
557    }
558    Ok(shard)
559}
560
561fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
562    match value {
563        None | Some("") | Some("0") => Ok(false),
564        Some("1") => Ok(true),
565        Some(value) => Err(format!(
566            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
567        )),
568    }
569}
570
571pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
572    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
573}
574
575fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
576    match value {
577        None | Some("") | Some("0") => Ok(false),
578        Some("1") => Ok(true),
579        Some(value) => Err(format!(
580            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
581        )),
582    }
583}
584
585pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
586    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
587}
588
589fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
590    match value {
591        None | Some("") | Some("0") => Ok(false),
592        Some("1") => Ok(true),
593        Some(value) => Err(format!(
594            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
595        )),
596    }
597}
598
599fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
600    match value {
601        None | Some("") | Some("0") => Ok(false),
602        Some("1") => Ok(true),
603        Some(value) => Err(format!(
604            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
605        )),
606    }
607}
608
609/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
610/// host-canonical program remains the oracle until the device path carries its own gates.
611pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
612    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
613}
614
615pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
616    parse_step_ep_device_arithmetic(
617        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
618            .ok()
619            .as_deref(),
620    )
621}
622
623fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
624    match value {
625        None | Some("") | Some("0") => Ok(false),
626        Some("1") => Ok(true),
627        Some(value) => Err(format!(
628            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
629        )),
630    }
631}
632
633pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
634    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
635}
636
637fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
638    match value {
639        None | Some("") | Some("0") => Ok(false),
640        Some("1") => Ok(true),
641        Some(value) => Err(format!(
642            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
643        )),
644    }
645}
646
647/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
648/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
649/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
650/// side of the comparison).
651pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
652    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
653}
654
655fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
656    match value {
657        None | Some("") | Some("0") => Ok(false),
658        Some("1") => Ok(true),
659        Some(value) => Err(format!(
660            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
661        )),
662    }
663}
664
665fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
666    match value {
667        None | Some("") | Some("0") => Ok(false),
668        Some("1") => Ok(true),
669        Some(value) => Err(format!(
670            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
671        )),
672    }
673}
674
675/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
676/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
677/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
678pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
679    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
680}
681
682fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
683    match value {
684        None | Some("") | Some("0") => Ok(false),
685        Some("1") => Ok(true),
686        Some(value) => Err(format!(
687            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
688        )),
689    }
690}
691
692fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
693    match value {
694        None | Some("") | Some("0") => Ok(false),
695        Some("1") => Ok(true),
696        Some(value) => Err(format!(
697            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
698        )),
699    }
700}
701
702/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
703/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
704/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
705/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
706pub fn step_tp_dcw_enabled() -> Result<bool, String> {
707    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
708}
709
710/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
711/// expert program — per-layer multi-device parents built from per-rank children, launched on
712/// the model engine's stream; zero per-token node updates). Mechanism proven by
713/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
714pub fn step_tp_graph_enabled() -> Result<bool, String> {
715    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
716}
717
718/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
719/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
720/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
721pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
722    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
723}
724
725#[derive(Debug, Clone, PartialEq, Eq)]
726pub struct StepEpLayerSpec {
727    pub layer: usize,
728    pub devices: Vec<usize>,
729}
730
731pub type StepTpLayerSpec = StepEpLayerSpec;
732
733fn parse_step_layer_specs(
734    flag: &str,
735    value: Option<&str>,
736    allow_full_model: bool,
737) -> Result<Vec<StepEpLayerSpec>, String> {
738    let Some(value) = value else {
739        return Ok(Vec::new());
740    };
741    if value.is_empty() || value == "0" {
742        return Ok(Vec::new());
743    }
744
745    let mut specs = Vec::new();
746    for item in value.split(';') {
747        let (layers, devices) = item.split_once('@').ok_or_else(|| {
748            let layers = if allow_full_model {
749                "LAYER[-LAYER] or all"
750            } else {
751                "LAYER[-LAYER]"
752            };
753            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
754        })?;
755        let (first, last) = if layers == "all" {
756            if !allow_full_model {
757                return Err(format!(
758                    "{flag} does not support the full-model shorthand; assign routed layers \
759                     explicitly"
760                ));
761            }
762            (0, STEP37_TRUNK_LAYERS - 1)
763        } else {
764            match layers.split_once('-') {
765                Some((first, last)) => {
766                    let first = first
767                        .parse::<usize>()
768                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
769                    let last = last
770                        .parse::<usize>()
771                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
772                    if first > last {
773                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
774                    }
775                    if last - first + 1 > 128 {
776                        return Err(format!(
777                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
778                        ));
779                    }
780                    (first, last)
781                }
782                None => {
783                    let layer = layers
784                        .parse::<usize>()
785                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
786                    (layer, layer)
787                }
788            }
789        };
790        let devices = devices
791            .split(',')
792            .map(|device| {
793                device
794                    .parse::<usize>()
795                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
796            })
797            .collect::<Result<Vec<_>, _>>()?;
798        if !(2..=8).contains(&devices.len()) {
799            return Err(format!(
800                "{flag} requires 2..=8 devices, got {}",
801                devices.len()
802            ));
803        }
804        let mut unique = devices.clone();
805        unique.sort_unstable();
806        unique.dedup();
807        if unique.len() != devices.len() {
808            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
809        }
810        for layer in first..=last {
811            if specs
812                .iter()
813                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
814            {
815                return Err(format!("{flag} assigns layer {layer} more than once"));
816            }
817            specs.push(StepEpLayerSpec {
818                layer,
819                devices: devices.clone(),
820            });
821        }
822    }
823    Ok(specs)
824}
825
826pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
827    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
828}
829
830pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
831    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
832}
833
834pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
835    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
836}
837
838pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
839    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
840}
841
842#[derive(Clone, Copy)]
843pub struct E4m3BlockMatrix<'a> {
844    pub codes: &'a [u8],
845    pub scales: &'a [f32],
846    pub out_features: usize,
847    pub in_features: usize,
848}
849
850impl E4m3BlockMatrix<'_> {
851    fn validate(&self) -> Result<(), String> {
852        let code_count = self
853            .out_features
854            .checked_mul(self.in_features)
855            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
856        if self.codes.len() != code_count {
857            return Err(format!(
858                "E4M3 code count {} != {}x{} ({code_count})",
859                self.codes.len(),
860                self.out_features,
861                self.in_features,
862            ));
863        }
864        let scale_count =
865            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
866        if self.scales.len() != scale_count {
867            return Err(format!(
868                "E4M3 scale count {} != {scale_count} for {}x{}",
869                self.scales.len(),
870                self.out_features,
871                self.in_features,
872            ));
873        }
874        if !self
875            .scales
876            .iter()
877            .all(|scale| scale.is_finite() && *scale > 0.0)
878        {
879            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
880        }
881        Ok(())
882    }
883}
884
885#[derive(Clone, Copy)]
886pub struct E4m3ExpertBank<'a> {
887    pub codes: &'a [u8],
888    pub scales: &'a [f32],
889    pub expert_count: usize,
890    pub out_features: usize,
891    pub in_features: usize,
892}
893
894impl E4m3ExpertBank<'_> {
895    fn validate(&self) -> Result<(), String> {
896        if self.expert_count == 0 {
897            return Err("E4M3 expert bank is empty".to_string());
898        }
899        let code_stride = self
900            .out_features
901            .checked_mul(self.in_features)
902            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
903        let code_count = self
904            .expert_count
905            .checked_mul(code_stride)
906            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
907        if self.codes.len() != code_count {
908            return Err(format!(
909                "E4M3 expert code count {} != {}x{} ({code_count})",
910                self.codes.len(),
911                self.expert_count,
912                code_stride,
913            ));
914        }
915        let scale_stride =
916            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
917        let scale_count = self
918            .expert_count
919            .checked_mul(scale_stride)
920            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
921        if self.scales.len() != scale_count {
922            return Err(format!(
923                "E4M3 expert scale count {} != {}x{} ({scale_count})",
924                self.scales.len(),
925                self.expert_count,
926                scale_stride,
927            ));
928        }
929        if !self
930            .scales
931            .iter()
932            .all(|scale| scale.is_finite() && *scale > 0.0)
933        {
934            return Err(
935                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
936            );
937        }
938        Ok(())
939    }
940
941    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
942        if expert >= self.expert_count {
943            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
944        }
945        let code_stride = self.out_features * self.in_features;
946        let scale_stride =
947            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
948        Ok(E4m3BlockMatrix {
949            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
950            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
951            out_features: self.out_features,
952            in_features: self.in_features,
953        })
954    }
955}
956
957pub struct ColumnParallelResult {
958    pub gathered: Vec<f32>,
959    pub rank_outputs: Vec<Vec<f32>>,
960}
961
962pub struct RowParallelResult {
963    pub reduced: Vec<f32>,
964    pub rank_partials: Vec<Vec<f32>>,
965}
966
967#[derive(Clone, Copy)]
968pub struct Bf16Matrix<'a> {
969    pub bytes: &'a [u8],
970    pub out_features: usize,
971    pub in_features: usize,
972}
973
974impl Bf16Matrix<'_> {
975    pub fn validate(&self) -> Result<(), String> {
976        if self.out_features == 0 || self.in_features == 0 {
977            return Err("BF16 matrix dimensions must be nonzero".into());
978        }
979        let expected = self
980            .out_features
981            .checked_mul(self.in_features)
982            .and_then(|values| values.checked_mul(2))
983            .ok_or("BF16 matrix byte count overflow")?;
984        if self.bytes.len() != expected {
985            return Err(format!(
986                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
987                self.bytes.len(),
988                self.out_features,
989                self.in_features,
990            ));
991        }
992        Ok(())
993    }
994}
995
996struct ResidentE4m3Rank {
997    codes: CudaSlice<u8>,
998    scales: CudaSlice<f32>,
999    out_features: usize,
1000    in_features: usize,
1001}
1002
1003enum ResidentBf16Weight {
1004    Bf16(CudaSlice<u8>),
1005    F32(CudaSlice<f32>),
1006}
1007
1008impl ResidentBf16Weight {
1009    fn ordinal(&self) -> usize {
1010        match self {
1011            Self::Bf16(bytes) => bytes.ordinal(),
1012            Self::F32(values) => values.ordinal(),
1013        }
1014    }
1015}
1016
1017struct ResidentBf16Rank {
1018    weight: ResidentBf16Weight,
1019    out_features: usize,
1020    in_features: usize,
1021    /// q8_0 mirror built at load under MEMRA_STEP_TP_W8 (numeric-class door; the bf16 slab
1022    /// stays resident because every prefill/verify path is qualified against it).
1023    q8: Option<CudaSlice<u8>>,
1024}
1025
1026pub struct ResidentColumnParallel {
1027    ranks: Vec<ResidentE4m3Rank>,
1028    out_features: usize,
1029    in_features: usize,
1030}
1031
1032pub struct ResidentRowParallel {
1033    ranks: Vec<ResidentE4m3Rank>,
1034    out_features: usize,
1035    in_features: usize,
1036}
1037
1038pub struct ResidentBf16ColumnParallel {
1039    ranks: Vec<ResidentBf16Rank>,
1040    out_features: usize,
1041    in_features: usize,
1042    canonical_chunk_rows: Option<usize>,
1043}
1044
1045pub struct ResidentBf16RowParallel {
1046    ranks: Vec<ResidentBf16Rank>,
1047    out_features: usize,
1048    in_features: usize,
1049}
1050
1051pub struct ResidentStepBf16RowParallel {
1052    ranks: Vec<Vec<ResidentBf16Rank>>,
1053    out_features: usize,
1054    in_features: usize,
1055    canonical_chunk_cols: usize,
1056}
1057
1058/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
1059pub struct ResidentSigmoidTopKRouter {
1060    weight: CudaSlice<f32>,
1061    correction_bias: CudaSlice<f32>,
1062    active: CudaSlice<u8>,
1063    root_device: usize,
1064    input_width: usize,
1065    expert_count: usize,
1066    experts_per_token: usize,
1067    active_count: usize,
1068    scaling_factor: f32,
1069    route_norm: bool,
1070}
1071
1072pub struct SigmoidTopKHostOutput {
1073    pub logits: Vec<f32>,
1074    pub selected: Vec<u32>,
1075    pub weights: Vec<f32>,
1076}
1077
1078/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
1079pub struct ResidentReplicatedBf16SwiGlu {
1080    gate: Vec<ResidentBf16Rank>,
1081    up: Vec<ResidentBf16Rank>,
1082    down: Vec<ResidentBf16Rank>,
1083    input_width: usize,
1084    intermediate_width: usize,
1085}
1086
1087/// One token-major F32 batch replicated across a native-P2P rank group.
1088///
1089/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
1090/// substrate between independently sharded operators; it carries no model or topology claim.
1091pub struct ResidentReplicatedDeviceRows {
1092    ranks: Vec<CudaSlice<f32>>,
1093    tokens: usize,
1094    width: usize,
1095}
1096
1097impl ResidentReplicatedDeviceRows {
1098    pub fn tokens(&self) -> usize {
1099        self.tokens
1100    }
1101
1102    pub fn width(&self) -> usize {
1103        self.width
1104    }
1105
1106    pub fn ranks(&self) -> usize {
1107        self.ranks.len()
1108    }
1109}
1110
1111/// Canonical MoE output order: routed plus shared, then add the layer residual.
1112pub fn moe_residual_host(
1113    residual: &[f32],
1114    routed: &[f32],
1115    shared: &[f32],
1116) -> Result<Vec<f32>, String> {
1117    if residual.len() != routed.len() || residual.len() != shared.len() {
1118        return Err(format!(
1119            "MoE residual lengths residual={} routed={} shared={}",
1120            residual.len(),
1121            routed.len(),
1122            shared.len()
1123        ));
1124    }
1125    let ffn = routed
1126        .iter()
1127        .zip(shared)
1128        .map(|(&routed, &shared)| routed + shared)
1129        .collect::<Vec<_>>();
1130    Ok(residual
1131        .iter()
1132        .zip(ffn)
1133        .map(|(&residual, ffn)| residual + ffn)
1134        .collect())
1135}
1136
1137pub use memra_kv::{
1138    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1139};
1140
1141/// Persistent TP2/TP4/TP8 routed-expert reference.
1142///
1143/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1144/// Activations and deterministic host-staged collectives remain per invocation. This is the
1145/// correctness substrate for serving TP/EP, not product-throughput evidence.
1146pub struct ResidentTpExpert {
1147    gate: ResidentColumnParallel,
1148    up: ResidentColumnParallel,
1149    down: ResidentRowParallel,
1150    input_width: usize,
1151    expert_width: usize,
1152}
1153
1154struct ResidentE4m3ExpertBankRank {
1155    codes: CudaSlice<u8>,
1156    scales: CudaSlice<f32>,
1157    expert_range: Range<usize>,
1158    out_features: usize,
1159    in_features: usize,
1160    code_stride: usize,
1161    scale_stride: usize,
1162    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1163    /// checkpoint's global block order exactly. Other banks remain row-major.
1164    k_blocks: Option<usize>,
1165}
1166
1167struct PackedE4m3ExpertBankRank {
1168    codes: Vec<u8>,
1169    scales: Vec<f32>,
1170    expert_range: Range<usize>,
1171    out_features: usize,
1172    in_features: usize,
1173    code_stride: usize,
1174    scale_stride: usize,
1175    k_blocks: Option<usize>,
1176}
1177
1178struct ResidentEpRank {
1179    gate: ResidentE4m3ExpertBankRank,
1180    up: ResidentE4m3ExpertBankRank,
1181    down: ResidentE4m3ExpertBankRank,
1182}
1183
1184/// Persistent expert-parallel reference.
1185///
1186/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1187/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1188/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1189/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1190pub struct ResidentExpertParallel {
1191    ranks: Vec<ResidentEpRank>,
1192    expert_count: usize,
1193    input_width: usize,
1194    expert_width: usize,
1195}
1196
1197/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1198///
1199/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1200/// outside this gate-only adapter.
1201pub struct StepGroupedFp8ProjectionOutput {
1202    pub gate: Vec<f32>,
1203    pub up: Vec<f32>,
1204    pub down: Vec<f32>,
1205}
1206
1207/// Prepared official Step grouped-FP8 projection gate.
1208///
1209/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1210/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1211pub struct PreparedStepGroupedFp8Gate {
1212    device: usize,
1213    gate: ResidentE4m3ExpertBankRank,
1214    up: ResidentE4m3ExpertBankRank,
1215    down: ResidentE4m3ExpertBankRank,
1216    input: CudaSlice<f32>,
1217    route_csr: DeviceExpertCsr,
1218    down_csr: DeviceExpertCsr,
1219    gate_workspace: Fp8GroupedWorkspace,
1220    up_workspace: Fp8GroupedWorkspace,
1221    down_workspace: Fp8GroupedWorkspace,
1222    activation: CudaSlice<f32>,
1223    activation_limit: Option<f32>,
1224    tokens: usize,
1225    pairs: usize,
1226}
1227
1228impl PreparedStepGroupedFp8Gate {
1229    pub fn tokens(&self) -> usize {
1230        self.tokens
1231    }
1232
1233    pub fn pairs(&self) -> usize {
1234        self.pairs
1235    }
1236}
1237
1238struct PreparedStepGroupedExpertOwner {
1239    rank: usize,
1240    global_pairs: Vec<usize>,
1241    route_csr: DeviceExpertCsr,
1242    down_csr: DeviceExpertCsr,
1243    gate_workspace: Fp8GroupedWorkspace,
1244    up_workspace: Fp8GroupedWorkspace,
1245    down_workspace: Fp8GroupedWorkspace,
1246    activation: CudaSlice<f32>,
1247}
1248
1249struct StepGroupedExpertOwnerSchedule {
1250    global_pairs: Vec<usize>,
1251    route_csr: ExpertCsr,
1252    down_csr: ExpertCsr,
1253}
1254
1255/// Prepared official Step expert-owner grouped-FP8 projection gate.
1256///
1257/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1258/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1259/// after every owner has completed its rank-local program.
1260pub struct PreparedStepGroupedExpertParallelGate {
1261    rank_inputs: Vec<CudaSlice<f32>>,
1262    owners: Vec<PreparedStepGroupedExpertOwner>,
1263    activation_limit: Option<f32>,
1264    tokens: usize,
1265    pairs: usize,
1266    max_tokens: usize,
1267    max_pairs: usize,
1268    input_width: usize,
1269    expert_width: usize,
1270    generation: u64,
1271    executed_generation: Option<u64>,
1272    ready: bool,
1273}
1274
1275impl PreparedStepGroupedExpertParallelGate {
1276    pub fn tokens(&self) -> usize {
1277        self.tokens
1278    }
1279
1280    pub fn pairs(&self) -> usize {
1281        self.pairs
1282    }
1283
1284    pub fn max_tokens(&self) -> usize {
1285        self.max_tokens
1286    }
1287
1288    pub fn input_width(&self) -> usize {
1289        self.input_width
1290    }
1291
1292    pub fn expert_width(&self) -> usize {
1293        self.expert_width
1294    }
1295
1296    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1297        validate_step_expert_activation_limit(limit)?;
1298        self.activation_limit = limit;
1299        self.executed_generation = None;
1300        Ok(())
1301    }
1302
1303    pub fn active_owners(&self) -> usize {
1304        self.owners
1305            .iter()
1306            .filter(|owner| !owner.global_pairs.is_empty())
1307            .count()
1308    }
1309
1310    pub fn owner_pair_counts(&self) -> Vec<usize> {
1311        self.owners
1312            .iter()
1313            .map(|owner| owner.global_pairs.len())
1314            .collect()
1315    }
1316
1317    pub fn generation(&self) -> u64 {
1318        self.generation
1319    }
1320}
1321
1322struct PreparedPeerWeightedRouteOwner {
1323    token_rows: CudaSlice<i32>,
1324    slots: CudaSlice<i32>,
1325    weights: CudaSlice<f32>,
1326    active_pairs: usize,
1327}
1328
1329/// Persistent root-side weighted combine for peer-owned canonical route rows.
1330///
1331/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1332/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1333/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1334pub struct PreparedPeerWeightedRouteCombine {
1335    root_device: usize,
1336    owners: Vec<PreparedPeerWeightedRouteOwner>,
1337    peer_staging: CudaSlice<f32>,
1338    slots: CudaSlice<f32>,
1339    weights: CudaSlice<f32>,
1340    output: CudaSlice<f32>,
1341    peer_devices: Vec<usize>,
1342    peer_outputs: Vec<CudaSlice<f32>>,
1343    width: usize,
1344    experts_per_token: usize,
1345    max_tokens: usize,
1346    max_pairs: usize,
1347    tokens: usize,
1348    pairs: usize,
1349    projection_generation: u64,
1350    output_generation: Option<u64>,
1351    broadcast_generation: Option<u64>,
1352    ready: bool,
1353}
1354
1355impl PreparedPeerWeightedRouteCombine {
1356    pub fn tokens(&self) -> usize {
1357        self.tokens
1358    }
1359
1360    pub fn pairs(&self) -> usize {
1361        self.pairs
1362    }
1363
1364    pub fn owner_pair_counts(&self) -> Vec<usize> {
1365        self.owners.iter().map(|owner| owner.active_pairs).collect()
1366    }
1367
1368    pub fn distributed_ranks(&self) -> usize {
1369        1 + self.peer_outputs.len()
1370    }
1371}
1372
1373struct ResidentTpExpertBank {
1374    gate: Vec<ResidentE4m3ExpertBankRank>,
1375    up: Vec<ResidentE4m3ExpertBankRank>,
1376    down: Vec<ResidentE4m3ExpertBankRank>,
1377    expert_count: usize,
1378    input_width: usize,
1379    expert_width: usize,
1380}
1381
1382/// Persistent tensor-parallel expert bank.
1383///
1384/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1385/// input-column shard of every down projection. Activations cross deterministic host-staged
1386/// collectives on hosts where native peer copies are unavailable or corrupt.
1387pub struct ResidentTensorParallel {
1388    bank: ResidentTpExpertBank,
1389}
1390
1391/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1392///
1393/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1394/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1395/// repeated performance evidence qualify it.
1396pub struct TpE4m3HostBounce {
1397    devices: Vec<usize>,
1398    ranks: Vec<Engine>,
1399    native_p2p: bool,
1400    ep_device_arithmetic: bool,
1401    bulk_p2p: bool,
1402    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1403    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1404    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1405}
1406
1407/// Persistent workspace of the v2 rank-local decode-attention driver.
1408///
1409/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1410/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1411/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1412/// before its consumers run in the same call; nothing carries state between tokens.
1413/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1414/// fused kernels read (F32 mirror or raw checkpoint bf16).
1415pub enum StepTpGateShards<'a> {
1416    F32(&'a [crate::CudaSlice<f32>]),
1417    Bf16(&'a [crate::CudaSlice<u8>]),
1418}
1419
1420pub struct StepTpDecodeV2Ws {
1421    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1422    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1423    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1424    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1425    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1426    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1427    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1428    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1429    pub(crate) tcol_cap: usize,
1430    /// MEMRA_STEP_TP_W8 activation scratch: per-rank q8_1 quantized attention input
1431    /// ([in_f] i8 + one f32 scale pair per 32). Persistent because the alternative is an
1432    /// allocation per rank per layer per token.
1433    w8_aq: Vec<CudaSlice<i8>>,
1434    w8_ad: Vec<CudaSlice<f32>>,
1435    w8_in: usize,
1436    /// o_proj-side twin of the same scratch (its activation is the gated attention output,
1437    /// a different vector from the QKV input, so it needs its own buffers).
1438    w8o_aq: Vec<CudaSlice<i8>>,
1439    w8o_ad: Vec<CudaSlice<f32>>,
1440    w8o_in: usize,
1441    /// VERIFY-WALK q8_1 activation scratch, t columns wide (the decode scratch above is one
1442    /// row). Two sets because the QKV input and the gated attention output are different
1443    /// vectors of different widths.
1444    w8t_aq: Vec<CudaSlice<i8>>,
1445    w8t_ad: Vec<CudaSlice<f32>>,
1446    w8t_in: usize,
1447    w8t_oaq: Vec<CudaSlice<i8>>,
1448    w8t_oad: Vec<CudaSlice<f32>>,
1449    w8t_oin: usize,
1450    w8t_cap: usize,
1451    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1452    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1453    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1454    /// MEMRA_SPEC_FA2 slabs: per-rank stashed post-rope q rows ([2, local_q_dim]), gate
1455    /// rows ([2, heads/ranks]) and the two gated outputs the per-row combine writes
1456    /// ([2, local_q_dim]). Armed lazily by the first stash.
1457    pub(crate) fa2_q: Vec<CudaSlice<f32>>,
1458    pub(crate) fa2_gate: Vec<CudaSlice<f32>>,
1459    pub(crate) fa2_gated: Vec<CudaSlice<f32>>,
1460    pub(crate) fa2_cap: usize,
1461    /// T-ROW rope/append twin scratch: per-rank roped-k rows ([8, local_kv]), per-row
1462    /// last-block counters ([8]) and the per-tick position slab ([8]). Armed with the
1463    /// fa2 slabs.
1464    rope_k_t: Vec<CudaSlice<f32>>,
1465    rope_ctr_t: Vec<CudaSlice<u32>>,
1466    rope_pos_t: Vec<CudaSlice<i32>>,
1467    /// Per-rank combined 6-word row tables, keyed by the caller's (layer, session-set,
1468    /// base-arming) signature. LEGACY: only the `MEMRA_ROWS_TAB_RESTAGE=0` rollback arm
1469    /// reads this. See `rows_tab_t` for why the key cannot be made safe.
1470    rows_tabs: Vec<std::collections::HashMap<u64, CudaSlice<u64>>>,
1471    /// Per-rank PERSISTENT 6-word row-table slab ([32, 6] u64), RESTAGED from the live
1472    /// distributed cache before every launch. Replaces the `rows_tabs` memo, whose key was
1473    /// a hash of (k pointer, base pointer, layer, t) while the table it returned also
1474    /// carried the V and LEN pointers: a session whose K buffer address was recycled hit
1475    /// another session's table and the append kernel wrote its K/V through the FREED
1476    /// pointers the entry still held. Same defect and same cure as the row-table twin in
1477    /// `step35_verify_fa_rows_join` (8c8397e0b2, Hermes `11339f5cd3c132a3`), which this
1478    /// path was left out of. One 32-word htod per rank per layer replaces the map lookup;
1479    /// no allocation, and the staging is stream-ordered exactly like `rope_pos_t`.
1480    rows_tab_t: Vec<CudaSlice<u64>>,
1481    /// HOST shadow of the last table staged under each retired memo key, used ONLY by
1482    /// `MEMRA_ROWS_TAB_STALE_SCAN=1` to prove that the retired key would have handed a live
1483    /// launch another allocation's pointers. Never read by a kernel.
1484    rows_tab_shadow: Vec<std::collections::HashMap<u64, Vec<u64>>>,
1485    tcol_gated: Vec<CudaSlice<f32>>,
1486    tcol_opart: Vec<CudaSlice<f32>>,
1487    tcol_opeer: Option<CudaSlice<f32>>,
1488    tcol_omix: Option<CudaSlice<f32>>,
1489    tcol_ocap: usize,
1490    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1491    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1492    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1493    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1494    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1495    pub(crate) q: Vec<CudaSlice<f32>>,
1496    pub(crate) k: Vec<CudaSlice<f32>>,
1497    pub(crate) pos: Vec<CudaSlice<i32>>,
1498    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1499    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1500    pub(crate) gate: Vec<CudaSlice<f32>>,
1501    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1502    pub(crate) gated: Vec<CudaSlice<f32>>,
1503    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1504    o_partials: Vec<Vec<CudaSlice<f32>>>,
1505    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1506    ev_rank: Vec<CudaEvent>,
1507    // root-context buffers
1508    peer_partial: CudaSlice<f32>,
1509    reduce_a: CudaSlice<f32>,
1510    reduce_b: CudaSlice<f32>,
1511    /// Never written; the canonical zero start of the v1 add chain.
1512    zeros: CudaSlice<f32>,
1513    pub(crate) k_shadow: CudaSlice<f32>,
1514    pub(crate) v_shadow: CudaSlice<f32>,
1515    ev_refresh: CudaEvent,
1516    ev_oproj: CudaEvent,
1517    // model-engine (e) context
1518    gate_e: CudaSlice<f32>,
1519    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1520    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1521    pub(crate) h_stage: Option<CudaSlice<f32>>,
1522    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1523    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1524    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1525    /// captured/raw address it uses must be layer-invariant).
1526    attn_in: Vec<CudaSlice<f32>>,
1527    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1528    raw_h_stage: u64,
1529    raw_pos_stage: u64,
1530    raw_attn_in: Vec<u64>,
1531    raw_pos: Vec<u64>,
1532    raw_o_partial1: u64,
1533    raw_peer_partial: u64,
1534    raw_k1: u64,
1535    raw_v1: u64,
1536    raw_k_shadow: u64,
1537    raw_v_shadow: u64,
1538    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1539    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1540    /// children read same-context memory (cross-context kernel args are capture-illegal).
1541    raw_mixed_stage_e: u64,
1542    raw_reduce_a: u64,
1543    raw_shadow_stage_e: (u64, u64),
1544    ev_entry: CudaEvent,
1545    e_device: usize,
1546    // geometry pins
1547    local_q_dim: usize,
1548    local_kv_dim: usize,
1549    heads: usize,
1550    pub(crate) o_out: usize,
1551    o_block_cols: usize,
1552    blocks_per_rank: usize,
1553}
1554
1555impl TpE4m3HostBounce {
1556    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1557        Self::new_inner(devices, false, false, false, false)
1558    }
1559
1560    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1561        Self::new_inner(devices, false, true, false, false)
1562    }
1563
1564    pub fn new_native_p2p_device_arithmetic(
1565        devices: &[usize],
1566    ) -> Result<Self, Box<dyn std::error::Error>> {
1567        Self::new_inner(devices, false, true, true, false)
1568    }
1569
1570    pub(crate) fn new_configured(
1571        devices: &[usize],
1572        native_p2p: bool,
1573        ep_device_arithmetic: bool,
1574        bulk_p2p: bool,
1575    ) -> Result<Self, Box<dyn std::error::Error>> {
1576        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1577    }
1578
1579    /// Single-rank execution of the canonical checkpoint-block TP program.
1580    ///
1581    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1582    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1583    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1584        Self::new_inner(&[device], true, false, false, false)
1585    }
1586
1587    fn new_inner(
1588        devices: &[usize],
1589        allow_single_rank: bool,
1590        native_p2p: bool,
1591        ep_device_arithmetic: bool,
1592        bulk_p2p: bool,
1593    ) -> Result<Self, Box<dyn std::error::Error>> {
1594        if ep_device_arithmetic && !native_p2p {
1595            return Err("device-resident EP arithmetic requires native P2P".into());
1596        }
1597        if bulk_p2p && !native_p2p {
1598            return Err("bulk TP transport requires native P2P".into());
1599        }
1600        let minimum = if allow_single_rank { 1 } else { 2 };
1601        if !(minimum..=8).contains(&devices.len()) {
1602            return Err(format!(
1603                "TP reference requires {minimum}..=8 devices, got {}",
1604                devices.len()
1605            )
1606            .into());
1607        }
1608        let mut unique = devices.to_vec();
1609        unique.sort_unstable();
1610        unique.dedup();
1611        if unique.len() != devices.len() {
1612            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1613        }
1614        let ranks = devices
1615            .iter()
1616            .map(|&device| Engine::new(device))
1617            .collect::<Result<Vec<_>, _>>()?;
1618        if native_p2p {
1619            configure_native_p2p(&ranks, devices)?;
1620        }
1621        if allow_single_rank {
1622            eprintln!(
1623                "[tp] canonical oracle transport=local device={} performance_claim=false",
1624                devices[0]
1625            );
1626        } else if native_p2p {
1627            if ep_device_arithmetic {
1628                eprintln!(
1629                    "[tp] correctness transport=native-p2p devices={devices:?} \
1630                     native_p2p=true activation=device-host-exact \
1631                     accumulation=device-host-exact output=root-readback \
1632                     bulk_p2p={bulk_p2p} performance_claim=false"
1633                );
1634            } else {
1635                eprintln!(
1636                    "[tp] correctness transport=native-p2p devices={devices:?} \
1637                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1638                     performance_claim=false"
1639                );
1640            }
1641        } else {
1642            eprintln!(
1643                "[tp] correctness transport=host-bounce devices={devices:?} \
1644                 native_p2p=false performance_claim=false"
1645            );
1646        }
1647        Ok(Self {
1648            devices: devices.to_vec(),
1649            ranks,
1650            native_p2p,
1651            ep_device_arithmetic,
1652            bulk_p2p,
1653            decode_v2: std::sync::Mutex::new(Vec::new()),
1654        })
1655    }
1656
1657    pub fn devices(&self) -> &[usize] {
1658        &self.devices
1659    }
1660
1661    pub fn native_p2p(&self) -> bool {
1662        self.native_p2p
1663    }
1664
1665    pub fn bulk_p2p(&self) -> bool {
1666        self.bulk_p2p
1667    }
1668
1669    pub fn expert_activation_label(&self) -> &'static str {
1670        if self.ep_device_arithmetic {
1671            "device-host-exact"
1672        } else {
1673            "host-canonical"
1674        }
1675    }
1676
1677    pub fn expert_accumulation_label(&self) -> &'static str {
1678        self.expert_activation_label()
1679    }
1680
1681    pub fn expert_output_label(&self) -> &'static str {
1682        if self.ep_device_arithmetic {
1683            "root-readback"
1684        } else {
1685            "host-accumulated"
1686        }
1687    }
1688
1689    pub fn transport_label(&self) -> &'static str {
1690        if self.devices.len() == 1 {
1691            "local"
1692        } else if self.native_p2p {
1693            "native-p2p"
1694        } else {
1695            "host-bounce"
1696        }
1697    }
1698
1699    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1700        self.ranks
1701            .iter()
1702            .map(|rank| rank.ctx().name().map_err(Into::into))
1703            .collect()
1704    }
1705
1706    /// Correctness-gate access to the engine that owns one TP rank.
1707    ///
1708    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1709    /// focused gates can prove that the rank-local projection outputs remain device-resident
1710    /// through the next ownership boundary before that boundary is wired into serving.
1711    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1712        self.ranks.get(rank)
1713    }
1714
1715    pub fn allocate_tp_kv_cache(
1716        &self,
1717        kv_dim_k: usize,
1718        kv_dim_v: usize,
1719        capacity: usize,
1720    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1721        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1722    }
1723
1724    pub fn allocate_tp_swa_kv_cache(
1725        &self,
1726        kv_dim_k: usize,
1727        kv_dim_v: usize,
1728        capacity: usize,
1729        window: usize,
1730    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1731        if window == 0 {
1732            return Err("TP SWA KV window must be nonzero".into());
1733        }
1734        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1735    }
1736
1737    fn allocate_tp_kv_cache_inner(
1738        &self,
1739        kv_dim_k: usize,
1740        kv_dim_v: usize,
1741        capacity: usize,
1742        window: Option<usize>,
1743    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1744        if capacity == 0 || capacity > i32::MAX as usize {
1745            return Err(
1746                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1747            );
1748        }
1749        let tp = self.ranks.len();
1750        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1751        let physical_rows = window
1752            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1753            .unwrap_or(capacity);
1754        let k_plane_bytes = physical_rows
1755            .checked_mul(shape.k_token_bytes)
1756            .and_then(|bytes| bytes.checked_add(8))
1757            .ok_or("TP KV K plane-byte overflow")?;
1758        let v_plane_bytes = physical_rows
1759            .checked_mul(shape.v_token_bytes)
1760            .and_then(|bytes| bytes.checked_add(8))
1761            .ok_or("TP KV V plane-byte overflow")?;
1762        let mut ranks = Vec::with_capacity(tp);
1763        for engine in &self.ranks {
1764            let _main = engine.gpu.enter_main()?;
1765            ranks.push(ResidentTpKvCacheRank::new(
1766                engine.alloc_u8(k_plane_bytes)?,
1767                engine.alloc_u8(v_plane_bytes)?,
1768                engine.htod_i32(&[0])?,
1769            ));
1770        }
1771        Ok(match window {
1772            Some(window) => ResidentTpKvCache::new_swa(
1773                ranks,
1774                shape.kv_dim_k,
1775                shape.kv_dim_v,
1776                shape.k_token_bytes,
1777                shape.v_token_bytes,
1778                capacity,
1779                window,
1780            ),
1781            None => ResidentTpKvCache::new(
1782                ranks,
1783                shape.kv_dim_k,
1784                shape.kv_dim_v,
1785                shape.k_token_bytes,
1786                shape.v_token_bytes,
1787                capacity,
1788            ),
1789        })
1790    }
1791
1792    pub fn grow_tp_kv_cache(
1793        &self,
1794        source: &ResidentTpKvCache,
1795        target_capacity: usize,
1796        rows: usize,
1797    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1798        self.validate_tp_kv_cache(source)?;
1799        let plan = source.prepare_grow(target_capacity, rows)?;
1800        let ranks = self.ranks.len();
1801        let global_k = source
1802            .kv_dim_k()
1803            .checked_mul(ranks)
1804            .ok_or("TP KV grow global K dimension overflow")?;
1805        let global_v = source
1806            .kv_dim_v()
1807            .checked_mul(ranks)
1808            .ok_or("TP KV grow global V dimension overflow")?;
1809        let mut target = match source.ring_window() {
1810            Some(window) => {
1811                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1812            }
1813            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1814        };
1815        self.validate_tp_kv_cache(&target)?;
1816
1817        for (rank, engine) in self.ranks.iter().enumerate() {
1818            let _main = engine.gpu.enter_main()?;
1819            let src = source
1820                .rank(rank)
1821                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1822            let dst = target
1823                .rank_mut(rank)
1824                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1825            if plan.k_bytes() > 0 {
1826                engine.copy_u8_range_into(
1827                    dst.k_mut(),
1828                    0,
1829                    src.k(),
1830                    plan.source_row() * source.k_tok_bytes(),
1831                    plan.k_bytes(),
1832                )?;
1833            }
1834            if plan.v_bytes() > 0 {
1835                engine.copy_u8_range_into(
1836                    dst.v_mut(),
1837                    0,
1838                    src.v(),
1839                    plan.source_row() * source.v_tok_bytes(),
1840                    plan.v_bytes(),
1841                )?;
1842            }
1843        }
1844        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1845
1846        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1847        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1848        for engine in &self.ranks {
1849            let _main = engine.gpu.enter_main()?;
1850            engine.stream().synchronize()?;
1851        }
1852        let physical_copy_rows = plan.copy_rows();
1853        target.publish_grow(plan)?;
1854        eprintln!(
1855            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1856             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1857             rank_streams_synchronized=true generation_preserved=true",
1858            rows,
1859            source.capacity(),
1860            target_capacity,
1861            ranks,
1862            physical_copy_rows,
1863            source.ring_window(),
1864        );
1865        Ok(target)
1866    }
1867
1868    pub fn hydrate_tp_kv_cache(
1869        &self,
1870        cache: &mut ResidentTpKvCache,
1871        rows: usize,
1872        k_rows: &[u8],
1873        v_rows: &[u8],
1874    ) -> Result<(), Box<dyn std::error::Error>> {
1875        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1876    }
1877
1878    pub fn hydrate_tp_kv_cache_from(
1879        &self,
1880        cache: &mut ResidentTpKvCache,
1881        logical_len: usize,
1882        resident_start: usize,
1883        k_rows: &[u8],
1884        v_rows: &[u8],
1885    ) -> Result<(), Box<dyn std::error::Error>> {
1886        self.validate_tp_kv_cache(cache)?;
1887        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1888            return Err(format!(
1889                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1890                cache.committed_len(),
1891                cache.staged_len()
1892            )
1893            .into());
1894        }
1895        if resident_start > logical_len || logical_len > cache.capacity() {
1896            return Err(format!(
1897                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1898                cache.capacity(),
1899            )
1900            .into());
1901        }
1902        let rows = logical_len - resident_start;
1903        if rows > cache.physical_capacity() {
1904            return Err(format!(
1905                "TP KV hydration rows {rows} exceed physical capacity {}",
1906                cache.physical_capacity()
1907            )
1908            .into());
1909        }
1910        for rank in 0..self.ranks.len() {
1911            let k_rank =
1912                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1913            let v_rank =
1914                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1915            let engine = &self.ranks[rank];
1916            let _main = engine.gpu.enter_main()?;
1917            let rank_cache = cache
1918                .rank_mut(rank)
1919                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1920            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1921            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1922        }
1923        cache.publish_hydration(logical_len, resident_start)?;
1924        Ok(())
1925    }
1926
1927    pub fn append_tp_kv_transaction(
1928        &self,
1929        cache: &mut ResidentTpKvCache,
1930        transaction: TpKvTransaction,
1931        k_shards: &[CudaSlice<f32>],
1932        v_shards: &[CudaSlice<f32>],
1933        rows: usize,
1934    ) -> Result<(), Box<dyn std::error::Error>> {
1935        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1936    }
1937
1938    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1939    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1940    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1941    /// which land the same value the in-stream inc produced).
1942    #[allow(clippy::too_many_arguments)]
1943    pub fn append_tp_kv_transaction_inner(
1944        &self,
1945        cache: &mut ResidentTpKvCache,
1946        transaction: TpKvTransaction,
1947        k_shards: &[CudaSlice<f32>],
1948        v_shards: &[CudaSlice<f32>],
1949        rows: usize,
1950        external_rank_appends: bool,
1951    ) -> Result<(), Box<dyn std::error::Error>> {
1952        self.validate_tp_kv_cache(cache)?;
1953        let plan = cache.prepare_append(transaction, rows)?;
1954        let target = plan.target();
1955        let expected_k = rows
1956            .checked_mul(cache.kv_dim_k())
1957            .ok_or("TP KV K append size overflow")?;
1958        let expected_v = rows
1959            .checked_mul(cache.kv_dim_v())
1960            .ok_or("TP KV V append size overflow")?;
1961        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1962        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1963        if !external_rank_appends
1964            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1965        {
1966            return Err(format!(
1967                "TP KV append shard counts k={} v={} != ranks {}",
1968                k_shards.len(),
1969                v_shards.len(),
1970                self.ranks.len()
1971            )
1972            .into());
1973        }
1974        let kv_dim_k = cache.kv_dim_k();
1975        let kv_dim_v = cache.kv_dim_v();
1976        let k_tok_bytes = cache.k_tok_bytes();
1977        let v_tok_bytes = cache.v_tok_bytes();
1978        if let Some(KvRingAppend::Rebase {
1979            src_row,
1980            keep_rows,
1981            new_base,
1982            ..
1983        }) = plan.ring_append()
1984        {
1985            for rank in 0..self.ranks.len() {
1986                let engine = &self.ranks[rank];
1987                let _main = engine.gpu.enter_main()?;
1988                let rank_cache = cache
1989                    .rank_mut(rank)
1990                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1991                if keep_rows > 0 {
1992                    let k_len = keep_rows
1993                        .checked_mul(k_tok_bytes)
1994                        .ok_or("TP KV K rebase-byte overflow")?;
1995                    let v_len = keep_rows
1996                        .checked_mul(v_tok_bytes)
1997                        .ok_or("TP KV V rebase-byte overflow")?;
1998                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
1999                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
2000                    engine.copy_u8_range_into(
2001                        &mut k_tmp,
2002                        0,
2003                        rank_cache.k(),
2004                        src_row * k_tok_bytes,
2005                        k_len,
2006                    )?;
2007                    engine.copy_u8_range_into(
2008                        &mut v_tmp,
2009                        0,
2010                        rank_cache.v(),
2011                        src_row * v_tok_bytes,
2012                        v_len,
2013                    )?;
2014                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
2015                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
2016                }
2017                // dcw base mirror (graph increment A): physical row 0 now holds logical
2018                // row `new_base`; armed device mirrors track it (rebases are rare host
2019                // events, so a host set here is the whole maintenance cost).
2020                if rank_cache.base_d().is_some() {
2021                    let value = new_base as i32;
2022                    let rank_cache = cache
2023                        .rank_mut(rank)
2024                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2025                    if let Some(base_d) = rank_cache.base_d_mut() {
2026                        engine.set_i32_one(base_d, value)?;
2027                    }
2028                }
2029            }
2030        }
2031        cache.publish_append_rebase(plan)?;
2032        let write_row = plan.write_row();
2033        for rank in 0..self.ranks.len() {
2034            if external_rank_appends {
2035                break;
2036            }
2037            let engine = &self.ranks[rank];
2038            let _main = engine.gpu.enter_main()?;
2039            if k_shards[rank].len() != expected_k
2040                || v_shards[rank].len() != expected_v
2041                || k_shards[rank].ordinal() != engine.ctx().ordinal()
2042                || v_shards[rank].ordinal() != engine.ctx().ordinal()
2043            {
2044                return Err(format!(
2045                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
2046                     != expected {expected_k}/{expected_v} on device {}",
2047                    k_shards[rank].len(),
2048                    k_shards[rank].ordinal(),
2049                    v_shards[rank].len(),
2050                    v_shards[rank].ordinal(),
2051                    engine.ctx().ordinal(),
2052                )
2053                .into());
2054            }
2055            let rank_cache = cache
2056                .rank_mut(rank)
2057                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2058            let (rank_k, rank_v) = rank_cache.planes_mut();
2059            engine.append_kv_quantized_rows(
2060                &k_shards[rank],
2061                &v_shards[rank],
2062                rank_k,
2063                rank_v,
2064                write_row,
2065                rows,
2066                kv_dim_k,
2067                kv_dim_v,
2068                k_tok_bytes,
2069                v_tok_bytes,
2070                Engine::kv_fp8_on(),
2071            )?;
2072        }
2073        if !external_rank_appends {
2074            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
2075            // here would race the merged per-rank append (it reads len_d for its write row).
2076            self.set_tp_kv_len_mirrors(cache, target)?;
2077        }
2078        cache.publish_append_plan(plan)?;
2079        Ok(())
2080    }
2081
2082    pub fn commit_tp_kv_transaction(
2083        &self,
2084        cache: &mut ResidentTpKvCache,
2085        transaction: TpKvTransaction,
2086        accepted_rows: usize,
2087    ) -> Result<(), Box<dyn std::error::Error>> {
2088        self.validate_tp_kv_cache(cache)?;
2089        let target = cache.commit_target(transaction, accepted_rows)?;
2090        self.set_tp_kv_len_mirrors(cache, target)?;
2091        cache.publish_finalize(transaction, target)?;
2092        Ok(())
2093    }
2094
2095    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
2096    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
2097    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
2098    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
2099    /// counter backward mid-token.
2100    pub fn commit_tp_kv_transaction_external(
2101        &self,
2102        cache: &mut ResidentTpKvCache,
2103        transaction: TpKvTransaction,
2104        accepted_rows: usize,
2105    ) -> Result<(), Box<dyn std::error::Error>> {
2106        self.validate_tp_kv_cache(cache)?;
2107        let target = cache.commit_target(transaction, accepted_rows)?;
2108        cache.publish_finalize(transaction, target)?;
2109        Ok(())
2110    }
2111
2112    pub fn rollback_tp_kv_transaction(
2113        &self,
2114        cache: &mut ResidentTpKvCache,
2115        transaction: TpKvTransaction,
2116    ) -> Result<(), Box<dyn std::error::Error>> {
2117        self.validate_tp_kv_cache(cache)?;
2118        cache.validate_transaction(transaction)?;
2119        let target = transaction.base_len();
2120        self.set_tp_kv_len_mirrors(cache, target)?;
2121        cache.publish_finalize(transaction, target)?;
2122        Ok(())
2123    }
2124
2125    pub fn tp_kv_device_lengths(
2126        &self,
2127        cache: &ResidentTpKvCache,
2128    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
2129        self.validate_tp_kv_cache(cache)?;
2130        let mut lengths = Vec::with_capacity(self.ranks.len());
2131        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
2132            let _main = engine.gpu.enter_main()?;
2133            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
2134        }
2135        Ok(lengths)
2136    }
2137
2138    fn set_tp_kv_len_mirrors(
2139        &self,
2140        cache: &mut ResidentTpKvCache,
2141        len: usize,
2142    ) -> Result<(), Box<dyn std::error::Error>> {
2143        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
2144        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
2145            let _main = engine.gpu.enter_main()?;
2146            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
2147        }
2148        Ok(())
2149    }
2150
2151    fn validate_tp_kv_cache(
2152        &self,
2153        cache: &ResidentTpKvCache,
2154    ) -> Result<(), Box<dyn std::error::Error>> {
2155        if cache.ranks_len() != self.ranks.len() {
2156            return Err(format!(
2157                "TP KV cache ranks {} != runtime ranks {}",
2158                cache.ranks_len(),
2159                self.ranks.len()
2160            )
2161            .into());
2162        }
2163        let expected_k = cache
2164            .physical_capacity()
2165            .checked_mul(cache.k_tok_bytes())
2166            .and_then(|bytes| bytes.checked_add(8))
2167            .ok_or("TP KV K plane validation overflow")?;
2168        let expected_v = cache
2169            .physical_capacity()
2170            .checked_mul(cache.v_tok_bytes())
2171            .and_then(|bytes| bytes.checked_add(8))
2172            .ok_or("TP KV V plane validation overflow")?;
2173        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
2174            let device = engine.ctx().ordinal();
2175            if rank_cache.k().len() != expected_k
2176                || rank_cache.v().len() != expected_v
2177                || rank_cache.len_d().len() != 1
2178                || rank_cache.k().ordinal() != device
2179                || rank_cache.v().ordinal() != device
2180                || rank_cache.len_d().ordinal() != device
2181            {
2182                return Err(format!(
2183                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
2184                )
2185                .into());
2186            }
2187        }
2188        Ok(())
2189    }
2190
2191    pub fn full(
2192        &self,
2193        matrix: E4m3BlockMatrix<'_>,
2194        activations: &[f32],
2195        tokens: usize,
2196    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2197        matrix.validate()?;
2198        validate_activations(activations, tokens, matrix.in_features)?;
2199        run_rank(&self.ranks[0], matrix, activations, tokens)
2200    }
2201
2202    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2203    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2204    /// output is host-gathered in rank order.
2205    pub fn column_parallel(
2206        &self,
2207        matrix: E4m3BlockMatrix<'_>,
2208        activations: &[f32],
2209        tokens: usize,
2210    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2211        matrix.validate()?;
2212        validate_activations(activations, tokens, matrix.in_features)?;
2213        let tp = self.ranks.len();
2214        if matrix.out_features % tp != 0 {
2215            return Err(format!(
2216                "column-parallel out_features {} is not divisible by TP={tp}",
2217                matrix.out_features
2218            )
2219            .into());
2220        }
2221        let local_out = matrix.out_features / tp;
2222        if local_out % FP8_BLOCK != 0 {
2223            return Err(format!(
2224                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2225                 E4M3 scale block"
2226            )
2227            .into());
2228        }
2229
2230        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2231        let mut rank_outputs = Vec::with_capacity(tp);
2232        for (rank_index, rank) in self.ranks.iter().enumerate() {
2233            let shard = column_shard(matrix, tp, rank_index)?;
2234            let output = run_rank(rank, shard, activations, tokens)?;
2235            let row_start = rank_index * local_out;
2236            for token in 0..tokens {
2237                gathered[token * matrix.out_features + row_start
2238                    ..token * matrix.out_features + row_start + local_out]
2239                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2240            }
2241            rank_outputs.push(output);
2242        }
2243        Ok(ColumnParallelResult {
2244            gathered,
2245            rank_outputs,
2246        })
2247    }
2248
2249    pub fn upload_column_parallel(
2250        &self,
2251        matrix: E4m3BlockMatrix<'_>,
2252    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2253        matrix.validate()?;
2254        let tp = self.ranks.len();
2255        validate_column_shape(matrix, tp)?;
2256        let mut ranks = Vec::with_capacity(tp);
2257        for (rank_index, engine) in self.ranks.iter().enumerate() {
2258            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2259        }
2260        Ok(ResidentColumnParallel {
2261            ranks,
2262            out_features: matrix.out_features,
2263            in_features: matrix.in_features,
2264        })
2265    }
2266
2267    pub fn column_parallel_resident(
2268        &self,
2269        matrix: &ResidentColumnParallel,
2270        activations: &[f32],
2271        tokens: usize,
2272    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2273        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2274        validate_activations(activations, tokens, matrix.in_features)?;
2275        let local_out = matrix.out_features / self.ranks.len();
2276        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2277        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2278        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2279            let output = run_resident_rank(engine, shard, activations, tokens)?;
2280            let row_start = rank_index * local_out;
2281            for token in 0..tokens {
2282                gathered[token * matrix.out_features + row_start
2283                    ..token * matrix.out_features + row_start + local_out]
2284                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2285            }
2286            rank_outputs.push(output);
2287        }
2288        Ok(ColumnParallelResult {
2289            gathered,
2290            rank_outputs,
2291        })
2292    }
2293
2294    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2295    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2296    /// rank order.
2297    pub fn row_parallel(
2298        &self,
2299        matrix: E4m3BlockMatrix<'_>,
2300        activations: &[f32],
2301        tokens: usize,
2302    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2303        matrix.validate()?;
2304        validate_activations(activations, tokens, matrix.in_features)?;
2305        let tp = self.ranks.len();
2306        if matrix.in_features % tp != 0 {
2307            return Err(format!(
2308                "row-parallel in_features {} is not divisible by TP={tp}",
2309                matrix.in_features
2310            )
2311            .into());
2312        }
2313        let local_in = matrix.in_features / tp;
2314        if local_in % FP8_BLOCK != 0 {
2315            return Err(format!(
2316                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2317                 E4M3 scale block"
2318            )
2319            .into());
2320        }
2321
2322        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2323        let mut rank_partials = Vec::with_capacity(tp);
2324        for (rank_index, rank) in self.ranks.iter().enumerate() {
2325            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2326            let local_activations =
2327                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2328            let shard = E4m3BlockMatrix {
2329                codes: &codes,
2330                scales: &scales,
2331                out_features: matrix.out_features,
2332                in_features: local_in,
2333            };
2334            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2335            for (sum, value) in reduced.iter_mut().zip(&partial) {
2336                *sum += *value;
2337            }
2338            rank_partials.push(partial);
2339        }
2340        Ok(RowParallelResult {
2341            reduced,
2342            rank_partials,
2343        })
2344    }
2345
2346    pub fn upload_row_parallel(
2347        &self,
2348        matrix: E4m3BlockMatrix<'_>,
2349    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2350        matrix.validate()?;
2351        let tp = self.ranks.len();
2352        validate_row_shape(matrix, tp)?;
2353        let local_in = matrix.in_features / tp;
2354        let mut ranks = Vec::with_capacity(tp);
2355        for (rank_index, engine) in self.ranks.iter().enumerate() {
2356            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2357            ranks.push(upload_rank(
2358                engine,
2359                E4m3BlockMatrix {
2360                    codes: &codes,
2361                    scales: &scales,
2362                    out_features: matrix.out_features,
2363                    in_features: local_in,
2364                },
2365            )?);
2366        }
2367        Ok(ResidentRowParallel {
2368            ranks,
2369            out_features: matrix.out_features,
2370            in_features: matrix.in_features,
2371        })
2372    }
2373
2374    pub fn row_parallel_resident(
2375        &self,
2376        matrix: &ResidentRowParallel,
2377        activations: &[f32],
2378        tokens: usize,
2379    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2380        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2381        validate_activations(activations, tokens, matrix.in_features)?;
2382        let tp = self.ranks.len();
2383        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2384        let mut rank_partials = Vec::with_capacity(tp);
2385        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2386            let local_activations =
2387                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2388            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2389            for (sum, value) in reduced.iter_mut().zip(&partial) {
2390                *sum += *value;
2391            }
2392            rank_partials.push(partial);
2393        }
2394        Ok(RowParallelResult {
2395            reduced,
2396            rank_partials,
2397        })
2398    }
2399
2400    pub fn upload_bf16_column_parallel(
2401        &self,
2402        matrix: Bf16Matrix<'_>,
2403    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2404        self.upload_bf16_column_parallel_inner(matrix, None, false)
2405    }
2406
2407    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2408    pub fn upload_step_bf16_column_parallel(
2409        &self,
2410        matrix: Bf16Matrix<'_>,
2411    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2412        self.upload_step_bf16_column_parallel_inner(matrix, false)
2413    }
2414
2415    /// Load-time exact F32 expansion of a Step BF16 shard.
2416    ///
2417    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2418    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2419    pub fn upload_step_bf16_column_parallel_f32_mirror(
2420        &self,
2421        matrix: Bf16Matrix<'_>,
2422    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2423        self.upload_step_bf16_column_parallel_inner(matrix, true)
2424    }
2425
2426    fn upload_step_bf16_column_parallel_inner(
2427        &self,
2428        matrix: Bf16Matrix<'_>,
2429        f32_mirror: bool,
2430    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2431        let canonical_chunk_rows =
2432            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2433        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2434    }
2435
2436    fn upload_bf16_column_parallel_inner(
2437        &self,
2438        matrix: Bf16Matrix<'_>,
2439        canonical_chunk_rows: Option<usize>,
2440        f32_mirror: bool,
2441    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2442        matrix.validate()?;
2443        let tp = self.ranks.len();
2444        if matrix.out_features % tp != 0 {
2445            return Err(format!(
2446                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2447                matrix.out_features
2448            )
2449            .into());
2450        }
2451        let mut ranks = Vec::with_capacity(tp);
2452        for (rank, engine) in self.ranks.iter().enumerate() {
2453            ranks.push(upload_bf16_rank(
2454                engine,
2455                bf16_column_shard(matrix, tp, rank)?,
2456                f32_mirror,
2457            )?);
2458        }
2459        Ok(ResidentBf16ColumnParallel {
2460            ranks,
2461            out_features: matrix.out_features,
2462            in_features: matrix.in_features,
2463            canonical_chunk_rows,
2464        })
2465    }
2466
2467    pub fn bf16_column_parallel_resident(
2468        &self,
2469        matrix: &ResidentBf16ColumnParallel,
2470        activations: &[f32],
2471        tokens: usize,
2472    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2473        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2474        validate_activations(activations, tokens, matrix.in_features)?;
2475        let local_out = matrix.out_features / self.ranks.len();
2476        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2477        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2478        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2479            let output = run_resident_bf16_rank(
2480                engine,
2481                shard,
2482                activations,
2483                tokens,
2484                matrix.canonical_chunk_rows,
2485            )?;
2486            for token in 0..tokens {
2487                let src = &output[token * local_out..(token + 1) * local_out];
2488                let dst_start = token * matrix.out_features + rank * local_out;
2489                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2490            }
2491            rank_outputs.push(output);
2492        }
2493        Ok(ColumnParallelResult {
2494            gathered,
2495            rank_outputs,
2496        })
2497    }
2498
2499    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2500    ///
2501    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2502    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2503    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2504    /// attention and KV ownership are separate milestones.
2505    pub fn bf16_column_parallel_resident_native(
2506        &self,
2507        matrix: &ResidentBf16ColumnParallel,
2508        activations: &[f32],
2509        tokens: usize,
2510    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2511        let rank_outputs =
2512            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2513        let local_out = matrix.out_features / self.ranks.len();
2514        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2515    }
2516
2517    /// Does the serving engine live in the SAME CUDA context as this runtime's root rank?
2518    /// The device-resident input/output seams below hand raw device buffers across the
2519    /// Engine boundary, which is only addressable when both sides share the root device's
2520    /// primary context — the seam `step35_tp_qkv` keys its residency dispatch on.
2521    pub fn root_shares_ctx(&self, e: &Engine) -> bool {
2522        self.ranks
2523            .first()
2524            .is_some_and(|root| root.ctx().cu_ctx() == e.ctx().cu_ctx())
2525    }
2526
2527    /// Device-input twin of [`Self::bf16_column_parallel_resident_native`] (lane/
2528    /// hermes-perf-fixes, 2026-08-23 — the step QKV TP host-bounce finding). The activation
2529    /// arrives as a ROOT-DEVICE buffer (first `tokens * in_features` values) instead of a
2530    /// host slice, and the gathered output stays root-resident: no DtoH of the hidden state,
2531    /// no host q/k/v staging, no re-upload. BYTE-IDENTICAL to the host-canonical native arm
2532    /// by construction — the root input bytes are dtod-copied where the host arm htod'd the
2533    /// same bytes, and every kernel, peer copy, and gather order is shared.
2534    ///
2535    /// FENCES: caller must have synchronized the producer stream that wrote
2536    /// `root_activation` (the serving engine's — a DIFFERENT stream in the same context);
2537    /// this method synchronizes the root stream before returning so the caller's stream can
2538    /// consume the gathered output immediately.
2539    pub fn bf16_column_parallel_resident_native_device(
2540        &self,
2541        matrix: &ResidentBf16ColumnParallel,
2542        root_activation: &CudaSlice<f32>,
2543        tokens: usize,
2544    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2545        let rank_outputs = self.bf16_column_parallel_resident_device_shards_from_root(
2546            matrix,
2547            root_activation,
2548            tokens,
2549        )?;
2550        let local_out = matrix.out_features / self.ranks.len();
2551        let gathered = self.gather_native_column_shards_device(&rank_outputs, tokens, local_out)?;
2552        let root = &self.ranks[0];
2553        let _main = root.gpu.enter_main()?;
2554        root.stream().synchronize()?;
2555        Ok(gathered)
2556    }
2557
2558    /// Root-device-input twin of [`Self::bf16_column_parallel_resident_device_shards`]:
2559    /// the canonical activation is already resident on the root device (len >=
2560    /// `tokens * in_features`; extra tail values beyond the active prefix are ignored,
2561    /// the reused-prime-slab contract of `active_matrix_values`).
2562    pub fn bf16_column_parallel_resident_device_shards_from_root(
2563        &self,
2564        matrix: &ResidentBf16ColumnParallel,
2565        root_activation: &CudaSlice<f32>,
2566        tokens: usize,
2567    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2568        if self.ranks.len() > 1 && !self.native_p2p {
2569            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2570        }
2571        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2572        let values = tokens
2573            .checked_mul(matrix.in_features)
2574            .ok_or("device BF16 column activation size overflow")?;
2575        let root = &self.ranks[0];
2576        if tokens == 0
2577            || root_activation.len() < values
2578            || root_activation.ordinal() != root.ctx().ordinal()
2579        {
2580            return Err("device BF16 column root activation geometry mismatch".into());
2581        }
2582
2583        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2584        let root_input = {
2585            let _main = root.gpu.enter_main()?;
2586            let mut root_input = root.uninit(values)?;
2587            root.stream()
2588                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
2589            root_input
2590        };
2591        // PRODUCER FENCE (same discipline as the host-input twin): the peer broadcast
2592        // below reads this buffer from the OTHER ranks' streams while the root dtod may
2593        // still be in flight.
2594        {
2595            let _main = root.gpu.enter_main()?;
2596            root.stream().synchronize()?;
2597        }
2598        rank_inputs.push(root_input);
2599        for engine in &self.ranks[1..] {
2600            let peer_input = {
2601                let _main = engine.gpu.enter_main()?;
2602                let mut peer_input = engine.uninit(values)?;
2603                engine
2604                    .stream()
2605                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2606                peer_input
2607            };
2608            rank_inputs.push(peer_input);
2609        }
2610
2611        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2612        for rank in 0..self.ranks.len() {
2613            rank_outputs.push(run_resident_bf16_rank_device(
2614                &self.ranks[rank],
2615                &matrix.ranks[rank],
2616                &rank_inputs[rank],
2617                tokens,
2618                matrix.canonical_chunk_rows,
2619                self.bulk_p2p,
2620            )?);
2621        }
2622        Ok(rank_outputs)
2623    }
2624
2625    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2626    ///
2627    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2628    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2629    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2630    /// and cache ownership; callers must not treat its existence as serving qualification.
2631    pub fn bf16_column_parallel_resident_device_shards(
2632        &self,
2633        matrix: &ResidentBf16ColumnParallel,
2634        activations: &[f32],
2635        tokens: usize,
2636    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2637        if self.ranks.len() > 1 && !self.native_p2p {
2638            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2639        }
2640        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2641        validate_activations(activations, tokens, matrix.in_features)?;
2642
2643        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2644        let root_input = {
2645            let root = &self.ranks[0];
2646            let _main = root.gpu.enter_main()?;
2647            root.htod(activations)?
2648        };
2649        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2650        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2651        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2652        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2653        // `upload_replicated_device_rows`.
2654        {
2655            let root = &self.ranks[0];
2656            let _main = root.gpu.enter_main()?;
2657            root.stream().synchronize()?;
2658        }
2659        rank_inputs.push(root_input);
2660        for engine in &self.ranks[1..] {
2661            let peer_input = {
2662                let _main = engine.gpu.enter_main()?;
2663                let mut peer_input = engine.uninit(activations.len())?;
2664                engine
2665                    .stream()
2666                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2667                peer_input
2668            };
2669            rank_inputs.push(peer_input);
2670        }
2671
2672        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2673        for rank in 0..self.ranks.len() {
2674            rank_outputs.push(run_resident_bf16_rank_device(
2675                &self.ranks[rank],
2676                &matrix.ranks[rank],
2677                &rank_inputs[rank],
2678                tokens,
2679                matrix.canonical_chunk_rows,
2680                self.bulk_p2p,
2681            )?);
2682        }
2683        Ok(rank_outputs)
2684    }
2685
2686    /// Allocate one fixed-shape replicated batch without initializing its contents.
2687    ///
2688    /// Callers must refresh every rank before passing the batch to an operator.
2689    pub fn allocate_replicated_device_rows(
2690        &self,
2691        tokens: usize,
2692        width: usize,
2693    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2694        if self.ranks.len() > 1 && !self.native_p2p {
2695            return Err("replicated device rows require native P2P ranks".into());
2696        }
2697        let values = tokens
2698            .checked_mul(width)
2699            .ok_or("replicated device row size overflow")?;
2700        let rank_lengths = vec![values; self.ranks.len()];
2701        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2702        let mut ranks = Vec::with_capacity(self.ranks.len());
2703        for engine in &self.ranks {
2704            let _main = engine.gpu.enter_main()?;
2705            ranks.push(engine.uninit(values)?);
2706        }
2707        Ok(ResidentReplicatedDeviceRows {
2708            ranks,
2709            tokens,
2710            width,
2711        })
2712    }
2713
2714    /// Replace a fixed-shape replicated batch from a root-device source.
2715    pub fn refresh_replicated_device_rows_from_root(
2716        &self,
2717        rows: &mut ResidentReplicatedDeviceRows,
2718        source: &CudaSlice<f32>,
2719    ) -> Result<(), Box<dyn std::error::Error>> {
2720        if self.ranks.len() > 1 && !self.native_p2p {
2721            return Err("replicated device rows require native P2P ranks".into());
2722        }
2723        validate_replicated_device_rows(&self.ranks, rows)?;
2724        let root = self
2725            .ranks
2726            .first()
2727            .ok_or("replicated rows have no root rank")?;
2728        let values = replicated_device_row_source_values(
2729            rows.tokens,
2730            rows.width,
2731            source.len(),
2732            source.ordinal(),
2733            root.ctx().ordinal(),
2734        )?;
2735        let (root_rows, peer_rows) = rows
2736            .ranks
2737            .split_first_mut()
2738            .ok_or("replicated rows have no root allocation")?;
2739        {
2740            let _main = root.gpu.enter_main()?;
2741            let mut destination = root_rows.slice_mut(0..values);
2742            root.stream()
2743                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2744            root.stream().synchronize()?;
2745        }
2746        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2747            let _main = engine.gpu.enter_main()?;
2748            let mut destination = peer_rows.slice_mut(0..values);
2749            engine
2750                .stream()
2751                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2752        }
2753        Ok(())
2754    }
2755
2756    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2757    pub fn upload_replicated_device_rows(
2758        &self,
2759        rows: &[f32],
2760        tokens: usize,
2761        width: usize,
2762    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2763        if self.ranks.len() > 1 && !self.native_p2p {
2764            return Err("replicated device rows require native P2P ranks".into());
2765        }
2766        validate_activations(rows, tokens, width)?;
2767        let root = self
2768            .ranks
2769            .first()
2770            .ok_or("replicated rows have no root rank")?;
2771        let root_rows = {
2772            let _main = root.gpu.enter_main()?;
2773            root.htod(rows)?
2774        };
2775        {
2776            let _main = root.gpu.enter_main()?;
2777            root.stream().synchronize()?;
2778        }
2779        let mut ranks = Vec::with_capacity(self.ranks.len());
2780        ranks.push(root_rows);
2781        for engine in self.ranks.iter().skip(1) {
2782            let _main = engine.gpu.enter_main()?;
2783            let mut peer_rows = engine.uninit(rows.len())?;
2784            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2785            ranks.push(peer_rows);
2786        }
2787        Ok(ResidentReplicatedDeviceRows {
2788            ranks,
2789            tokens,
2790            width,
2791        })
2792    }
2793
2794    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2795    pub fn bf16_column_parallel_resident_replicated_device_shards(
2796        &self,
2797        matrix: &ResidentBf16ColumnParallel,
2798        activations: &ResidentReplicatedDeviceRows,
2799    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2800        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2801        validate_replicated_device_rows(&self.ranks, activations)?;
2802        if activations.width != matrix.in_features {
2803            return Err(format!(
2804                "replicated BF16 column input width {} != matrix width {}",
2805                activations.width, matrix.in_features
2806            )
2807            .into());
2808        }
2809        let mut outputs = Vec::with_capacity(self.ranks.len());
2810        for rank in 0..self.ranks.len() {
2811            outputs.push(run_resident_bf16_rank_device(
2812                &self.ranks[rank],
2813                &matrix.ranks[rank],
2814                &activations.ranks[rank],
2815                activations.tokens,
2816                matrix.canonical_chunk_rows,
2817                self.bulk_p2p,
2818            )?);
2819        }
2820        Ok(outputs)
2821    }
2822
2823    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2824    #[allow(clippy::too_many_arguments)]
2825    pub fn upload_sigmoid_topk_router(
2826        &self,
2827        weight: Bf16Matrix<'_>,
2828        correction_bias: &[f32],
2829        active: Option<&[bool]>,
2830        experts_per_token: usize,
2831        scaling_factor: f32,
2832        route_norm: bool,
2833    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2834        weight.validate()?;
2835        if correction_bias.len() != weight.out_features
2836            || experts_per_token == 0
2837            || experts_per_token > weight.out_features
2838            || !correction_bias.iter().all(|value| value.is_finite())
2839            || !scaling_factor.is_finite()
2840            || scaling_factor <= 0.0
2841        {
2842            return Err(format!(
2843                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2844                weight.out_features,
2845                weight.in_features,
2846                correction_bias.len(),
2847                experts_per_token,
2848            )
2849            .into());
2850        }
2851        let active_row = active
2852            .map(|mask| {
2853                if mask.len() != weight.out_features {
2854                    return Err(format!(
2855                        "sigmoid router active mask {} != experts {}",
2856                        mask.len(),
2857                        weight.out_features
2858                    ));
2859                }
2860                Ok(mask
2861                    .iter()
2862                    .map(|&enabled| u8::from(enabled))
2863                    .collect::<Vec<_>>())
2864            })
2865            .transpose()?
2866            .unwrap_or_else(|| vec![1; weight.out_features]);
2867        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2868        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2869
2870        let root = self
2871            .ranks
2872            .first()
2873            .ok_or("sigmoid router runtime has no root rank")?;
2874        let _main = root.gpu.enter_main()?;
2875        let bf16 = root.htod_bytes(weight.bytes)?;
2876        let weight_f32 = root.bf16_to_f32(
2877            &bf16.slice(0..bf16.len()),
2878            weight.out_features * weight.in_features,
2879        )?;
2880        Ok(ResidentSigmoidTopKRouter {
2881            weight: weight_f32,
2882            correction_bias: root.htod(correction_bias)?,
2883            active: root.htod_bytes(&active_row)?,
2884            root_device: root.ctx().ordinal(),
2885            input_width: weight.in_features,
2886            expert_count: weight.out_features,
2887            experts_per_token,
2888            active_count,
2889            scaling_factor,
2890            route_norm,
2891        })
2892    }
2893
2894    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2895    ///
2896    /// The logits readback exists for independent oracle comparison. This method is a correctness
2897    /// surface; a serving scheduler may retain logits and selected routes on device.
2898    pub fn sigmoid_topk_replicated_device_rows_host(
2899        &self,
2900        router: &ResidentSigmoidTopKRouter,
2901        input: &ResidentReplicatedDeviceRows,
2902    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2903        validate_replicated_device_rows(&self.ranks, input)?;
2904        if input.width != router.input_width {
2905            return Err(format!(
2906                "sigmoid router input width {} != resident width {}",
2907                input.width, router.input_width
2908            )
2909            .into());
2910        }
2911        let root = self
2912            .ranks
2913            .first()
2914            .ok_or("sigmoid router runtime has no root rank")?;
2915        let _main = root.gpu.enter_main()?;
2916        if root.ctx().ordinal() != router.root_device
2917            || router.weight.ordinal() != router.root_device
2918            || router.correction_bias.ordinal() != router.root_device
2919            || router.active.ordinal() != router.root_device
2920        {
2921            return Err("sigmoid router root residency changed".into());
2922        }
2923        let logits = root.router_gemv(
2924            &router.weight,
2925            &input.ranks[0],
2926            router.input_width,
2927            router.expert_count,
2928            input.tokens,
2929        )?;
2930        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2931            &logits,
2932            input.tokens,
2933            router.expert_count,
2934            router.experts_per_token,
2935            router.active_count,
2936            &router.correction_bias,
2937            &router.active,
2938            router.scaling_factor,
2939            router.route_norm,
2940        )?;
2941        Ok(SigmoidTopKHostOutput {
2942            logits: root.dtoh(&logits)?,
2943            selected,
2944            weights,
2945        })
2946    }
2947
2948    /// Replicate a full BF16 SwiGLU bank on every rank.
2949    pub fn upload_replicated_bf16_swiglu(
2950        &self,
2951        gate: Bf16Matrix<'_>,
2952        up: Bf16Matrix<'_>,
2953        down: Bf16Matrix<'_>,
2954    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2955        gate.validate()?;
2956        up.validate()?;
2957        down.validate()?;
2958        if gate.in_features != up.in_features
2959            || gate.out_features != up.out_features
2960            || down.in_features != gate.out_features
2961            || down.out_features != gate.in_features
2962        {
2963            return Err(format!(
2964                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2965                gate.out_features,
2966                gate.in_features,
2967                up.out_features,
2968                up.in_features,
2969                down.out_features,
2970                down.in_features,
2971            )
2972            .into());
2973        }
2974        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2975        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2976        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2977        for engine in &self.ranks {
2978            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2979            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2980            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2981        }
2982        Ok(ResidentReplicatedBf16SwiGlu {
2983            gate: gate_ranks,
2984            up: up_ranks,
2985            down: down_ranks,
2986            input_width: gate.in_features,
2987            intermediate_width: gate.out_features,
2988        })
2989    }
2990
2991    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2992    pub fn replicated_bf16_swiglu_resident_device(
2993        &self,
2994        mlp: &ResidentReplicatedBf16SwiGlu,
2995        input: &ResidentReplicatedDeviceRows,
2996        activation_limit: Option<f32>,
2997    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2998        validate_step_expert_activation_limit(activation_limit)?;
2999        validate_replicated_device_rows(&self.ranks, input)?;
3000        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
3001        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
3002        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
3003        if input.width != mlp.input_width
3004            || mlp.gate.len() != self.ranks.len()
3005            || mlp.up.len() != self.ranks.len()
3006            || mlp.down.len() != self.ranks.len()
3007        {
3008            return Err("replicated BF16 SwiGLU residency or input width changed".into());
3009        }
3010
3011        let mut outputs = Vec::with_capacity(self.ranks.len());
3012        for rank in 0..self.ranks.len() {
3013            let engine = &self.ranks[rank];
3014            let gate = run_resident_bf16_rank_device(
3015                engine,
3016                &mlp.gate[rank],
3017                &input.ranks[rank],
3018                input.tokens,
3019                None,
3020                self.bulk_p2p,
3021            )?;
3022            let up = run_resident_bf16_rank_device(
3023                engine,
3024                &mlp.up[rank],
3025                &input.ranks[rank],
3026                input.tokens,
3027                None,
3028                self.bulk_p2p,
3029            )?;
3030            let _main = engine.gpu.enter_main()?;
3031            let values = input
3032                .tokens
3033                .checked_mul(mlp.intermediate_width)
3034                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
3035            let mut activation = engine.uninit(values)?;
3036            if let Some(limit) = activation_limit {
3037                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
3038            } else {
3039                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
3040            }
3041            outputs.push(run_resident_bf16_rank_device(
3042                engine,
3043                &mlp.down[rank],
3044                &activation,
3045                input.tokens,
3046                None,
3047                self.bulk_p2p,
3048            )?);
3049        }
3050        Ok(ResidentReplicatedDeviceRows {
3051            ranks: outputs,
3052            tokens: input.tokens,
3053            width: mlp.input_width,
3054        })
3055    }
3056
3057    /// Apply the same RMS-norm row program independently on every replicated rank.
3058    pub fn rms_norm_replicated_device_rows(
3059        &self,
3060        input: &ResidentReplicatedDeviceRows,
3061        weight: &[f32],
3062        eps: f32,
3063    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3064        validate_replicated_device_rows(&self.ranks, input)?;
3065        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
3066            return Err(format!(
3067                "replicated RMS norm weight/eps {}/{} != width {}",
3068                weight.len(),
3069                eps,
3070                input.width
3071            )
3072            .into());
3073        }
3074        let mut ranks = Vec::with_capacity(self.ranks.len());
3075        for (rank, engine) in self.ranks.iter().enumerate() {
3076            let _main = engine.gpu.enter_main()?;
3077            let weight = engine.htod(weight)?;
3078            let mut output = engine.uninit(input.tokens * input.width)?;
3079            engine.rms_norm(
3080                &input.ranks[rank],
3081                &weight,
3082                &mut output,
3083                input.width,
3084                input.tokens,
3085                eps,
3086            )?;
3087            ranks.push(output);
3088        }
3089        Ok(ResidentReplicatedDeviceRows {
3090            ranks,
3091            tokens: input.tokens,
3092            width: input.width,
3093        })
3094    }
3095
3096    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
3097    pub fn add_rms_norm_replicated_device_rows(
3098        &self,
3099        input: &ResidentReplicatedDeviceRows,
3100        update: &ResidentReplicatedDeviceRows,
3101        weight: &[f32],
3102        eps: f32,
3103    ) -> Result<
3104        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
3105        Box<dyn std::error::Error>,
3106    > {
3107        validate_replicated_device_rows(&self.ranks, input)?;
3108        validate_replicated_device_rows(&self.ranks, update)?;
3109        if input.tokens != update.tokens
3110            || input.width != update.width
3111            || weight.len() != input.width
3112            || !eps.is_finite()
3113            || eps <= 0.0
3114        {
3115            return Err(format!(
3116                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
3117                input.tokens,
3118                input.width,
3119                update.tokens,
3120                update.width,
3121                weight.len(),
3122            )
3123            .into());
3124        }
3125        let values = input.tokens * input.width;
3126        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
3127        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
3128        for (rank, engine) in self.ranks.iter().enumerate() {
3129            let _main = engine.gpu.enter_main()?;
3130            let weight = engine.htod(weight)?;
3131            let mut residual = engine.uninit(values)?;
3132            let mut normalized = engine.uninit(values)?;
3133            engine.add_rms_norm(
3134                &input.ranks[rank],
3135                &update.ranks[rank],
3136                &weight,
3137                &mut residual,
3138                &mut normalized,
3139                input.width,
3140                input.tokens,
3141                eps,
3142            )?;
3143            residual_ranks.push(residual);
3144            normalized_ranks.push(normalized);
3145        }
3146        Ok((
3147            ResidentReplicatedDeviceRows {
3148                ranks: residual_ranks,
3149                tokens: input.tokens,
3150                width: input.width,
3151            },
3152            ResidentReplicatedDeviceRows {
3153                ranks: normalized_ranks,
3154                tokens: input.tokens,
3155                width: input.width,
3156            },
3157        ))
3158    }
3159
3160    pub fn collect_replicated_device_rows(
3161        &self,
3162        rows: &ResidentReplicatedDeviceRows,
3163    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
3164        validate_replicated_device_rows(&self.ranks, rows)?;
3165        let mut outputs = Vec::with_capacity(self.ranks.len());
3166        for (rank, engine) in self.ranks.iter().enumerate() {
3167            let _main = engine.gpu.enter_main()?;
3168            outputs.push(engine.dtoh(&rows.ranks[rank])?);
3169        }
3170        Ok(outputs)
3171    }
3172
3173    pub fn upload_bf16_row_parallel(
3174        &self,
3175        matrix: Bf16Matrix<'_>,
3176    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
3177        matrix.validate()?;
3178        let tp = self.ranks.len();
3179        if matrix.in_features % tp != 0 {
3180            return Err(format!(
3181                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
3182                matrix.in_features
3183            )
3184            .into());
3185        }
3186        let mut ranks = Vec::with_capacity(tp);
3187        for (rank, engine) in self.ranks.iter().enumerate() {
3188            let shard = bf16_row_shard(matrix, tp, rank)?;
3189            ranks.push(upload_bf16_rank(
3190                engine,
3191                Bf16Matrix {
3192                    bytes: &shard,
3193                    out_features: matrix.out_features,
3194                    in_features: matrix.in_features / tp,
3195                },
3196                false,
3197            )?);
3198        }
3199        Ok(ResidentBf16RowParallel {
3200            ranks,
3201            out_features: matrix.out_features,
3202            in_features: matrix.in_features,
3203        })
3204    }
3205
3206    pub fn bf16_row_parallel_resident(
3207        &self,
3208        matrix: &ResidentBf16RowParallel,
3209        activations: &[f32],
3210        tokens: usize,
3211    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3212        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3213        validate_activations(activations, tokens, matrix.in_features)?;
3214        let tp = self.ranks.len();
3215        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3216        let mut rank_partials = Vec::with_capacity(tp);
3217        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3218            let local_activations =
3219                activation_shard(activations, tokens, matrix.in_features, tp, rank);
3220            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
3221            for (sum, value) in reduced.iter_mut().zip(&partial) {
3222                *sum += value;
3223            }
3224            rank_partials.push(partial);
3225        }
3226        Ok(RowParallelResult {
3227            reduced,
3228            rank_partials,
3229        })
3230    }
3231
3232    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
3233    pub fn upload_step_bf16_row_parallel(
3234        &self,
3235        matrix: Bf16Matrix<'_>,
3236    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3237        self.upload_step_bf16_row_parallel_inner(matrix, false)
3238    }
3239
3240    pub fn upload_step_bf16_row_parallel_f32_mirror(
3241        &self,
3242        matrix: Bf16Matrix<'_>,
3243    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3244        self.upload_step_bf16_row_parallel_inner(matrix, true)
3245    }
3246
3247    fn upload_step_bf16_row_parallel_inner(
3248        &self,
3249        matrix: Bf16Matrix<'_>,
3250        f32_mirror: bool,
3251    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3252        matrix.validate()?;
3253        let tp = self.ranks.len();
3254        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
3255        let local_in = matrix.in_features / tp;
3256        let blocks_per_rank = local_in / canonical_chunk_cols;
3257        let mut ranks = Vec::with_capacity(tp);
3258        for (rank, engine) in self.ranks.iter().enumerate() {
3259            let mut blocks = Vec::with_capacity(blocks_per_rank);
3260            for block in 0..blocks_per_rank {
3261                let global_block = rank * blocks_per_rank + block;
3262                let col_start = global_block * canonical_chunk_cols;
3263                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
3264                blocks.push(upload_bf16_rank(
3265                    engine,
3266                    Bf16Matrix {
3267                        bytes: &bytes,
3268                        out_features: matrix.out_features,
3269                        in_features: canonical_chunk_cols,
3270                    },
3271                    f32_mirror,
3272                )?);
3273            }
3274            ranks.push(blocks);
3275        }
3276        Ok(ResidentStepBf16RowParallel {
3277            ranks,
3278            out_features: matrix.out_features,
3279            in_features: matrix.in_features,
3280            canonical_chunk_cols,
3281        })
3282    }
3283
3284    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
3285    ///
3286    /// Block inputs and partials cross host memory, but every partial is added on the root device
3287    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
3288    pub fn step_bf16_row_parallel_resident(
3289        &self,
3290        matrix: &ResidentStepBf16RowParallel,
3291        activations: &[f32],
3292        tokens: usize,
3293    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3294        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3295        validate_activations(activations, tokens, matrix.in_features)?;
3296        let root = &self.ranks[0];
3297        let output_len = tokens
3298            .checked_mul(matrix.out_features)
3299            .ok_or("Step BF16 row output size overflow")?;
3300        let mut reduced = {
3301            let _main = root.gpu.enter_main()?;
3302            root.htod(&vec![0.0f32; output_len])?
3303        };
3304        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3305        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3306            for (block, resident) in blocks.iter().enumerate() {
3307                let global_block = rank * blocks_per_rank + block;
3308                let input = activation_shard(
3309                    activations,
3310                    tokens,
3311                    matrix.in_features,
3312                    PRODUCT_MAX_CARDS,
3313                    global_block,
3314                );
3315                let partial =
3316                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
3317                let next = {
3318                    let _main = root.gpu.enter_main()?;
3319                    let partial = root.htod(&partial)?;
3320                    let mut next = root.uninit(output_len)?;
3321                    root.add(&reduced, &partial, &mut next, output_len)?;
3322                    next
3323                };
3324                reduced = next;
3325            }
3326        }
3327        let _main = root.gpu.enter_main()?;
3328        root.dtoh(&reduced)
3329    }
3330
3331    /// Native-P2P Step row projection with canonical global K-block reduction.
3332    ///
3333    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3334    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3335    /// replay the same eight-block order as TP1 and the host-staged oracle.
3336    pub fn step_bf16_row_parallel_resident_native(
3337        &self,
3338        matrix: &ResidentStepBf16RowParallel,
3339        activations: &[f32],
3340        tokens: usize,
3341    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3342        if self.ranks.len() > 1 && !self.native_p2p {
3343            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3344        }
3345        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3346        validate_activations(activations, tokens, matrix.in_features)?;
3347        let root = &self.ranks[0];
3348        let root_input = {
3349            let _main = root.gpu.enter_main()?;
3350            root.htod(activations)?
3351        };
3352        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3353        // from the other ranks' streams while root's clone_htod may still be in flight.
3354        {
3355            let _main = root.gpu.enter_main()?;
3356            root.stream().synchronize()?;
3357        }
3358        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3359        let _main = root.gpu.enter_main()?;
3360        root.dtoh(&reduced)
3361    }
3362
3363    /// Device-input twin of [`Self::step_bf16_row_parallel_resident_native`] (lane/
3364    /// hermes-perf-fixes, 2026-08-23): the full activation arrives as a ROOT-DEVICE buffer
3365    /// and the reduced output stays root-resident — no DtoH of the attention output, no
3366    /// host O staging, no re-upload. Byte-identical to the host-canonical arm by
3367    /// construction (same block scatter, kernels, and global TP8 reduction order; the root
3368    /// bytes are dtod-copied where the host arm htod'd the same bytes). Caller must have
3369    /// synchronized the producer stream; the root stream is synchronized before returning.
3370    pub fn step_bf16_row_parallel_resident_native_device(
3371        &self,
3372        matrix: &ResidentStepBf16RowParallel,
3373        root_activation: &CudaSlice<f32>,
3374        tokens: usize,
3375    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3376        if self.ranks.len() > 1 && !self.native_p2p {
3377            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3378        }
3379        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3380        let values = tokens
3381            .checked_mul(matrix.in_features)
3382            .ok_or("device Step BF16 row activation size overflow")?;
3383        let root = &self.ranks[0];
3384        if tokens == 0
3385            || root_activation.len() < values
3386            || root_activation.ordinal() != root.ctx().ordinal()
3387        {
3388            return Err("device Step BF16 row root activation geometry mismatch".into());
3389        }
3390        let root_input = {
3391            let _main = root.gpu.enter_main()?;
3392            let mut root_input = root.uninit(values)?;
3393            root.stream()
3394                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
3395            root.stream().synchronize()?; // producer fence, as the host-input twin
3396            root_input
3397        };
3398        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3399        let _main = root.gpu.enter_main()?;
3400        root.stream().synchronize()?;
3401        Ok(reduced)
3402    }
3403
3404    /// Shared core of the two native Step row arms above: block scatter + rank GEMMs +
3405    /// canonical global TP8-order root reduction, from a root-resident input, returning the
3406    /// root-resident reduced output. Extracted verbatim so the host and device twins cannot
3407    /// drift numerically.
3408    fn step_bf16_row_native_reduce_from_root(
3409        &self,
3410        matrix: &ResidentStepBf16RowParallel,
3411        root_input: &CudaSlice<f32>,
3412        tokens: usize,
3413    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3414        let root = &self.ranks[0];
3415        let output_len = tokens
3416            .checked_mul(matrix.out_features)
3417            .ok_or("native Step BF16 row output size overflow")?;
3418        let mut reduced = {
3419            let _main = root.gpu.enter_main()?;
3420            root.htod(&vec![0.0f32; output_len])?
3421        };
3422        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3423        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3424        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3425        let mut remote_partial_keepalive = Vec::new();
3426        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3427            for (block, resident) in blocks.iter().enumerate() {
3428                let global_block = rank * blocks_per_rank + block;
3429                let col_start = global_block * matrix.canonical_chunk_cols;
3430                let block_len = tokens
3431                    .checked_mul(matrix.canonical_chunk_cols)
3432                    .ok_or("native Step BF16 row block size overflow")?;
3433                let block_input = if self.bulk_p2p {
3434                    let root_packed = {
3435                        let _main = root.gpu.enter_main()?;
3436                        let mut root_packed = root.uninit(block_len)?;
3437                        root.copy_rows_strided(
3438                            &root_input,
3439                            &mut root_packed,
3440                            matrix.canonical_chunk_cols,
3441                            tokens,
3442                            matrix.in_features,
3443                            col_start,
3444                        )?;
3445                        root_packed
3446                    };
3447                    if rank == 0 {
3448                        root_packed
3449                    } else {
3450                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3451                        // root stream; this rank's peer read must not overtake it.
3452                        {
3453                            let _main = root.gpu.enter_main()?;
3454                            root.stream().synchronize()?;
3455                        }
3456                        let engine = &self.ranks[rank];
3457                        let _main = engine.gpu.enter_main()?;
3458                        let mut block_input = engine.uninit(block_len)?;
3459                        engine
3460                            .stream()
3461                            .memcpy_dtod(&root_packed, &mut block_input)?;
3462                        root_packed_keepalive.push(root_packed);
3463                        block_input
3464                    }
3465                } else {
3466                    let engine = &self.ranks[rank];
3467                    let _main = engine.gpu.enter_main()?;
3468                    let mut block_input = engine.uninit(block_len)?;
3469                    for token in 0..tokens {
3470                        let source_start = token * matrix.in_features + col_start;
3471                        let source = root_input
3472                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3473                        let destination_start = token * matrix.canonical_chunk_cols;
3474                        let mut destination = block_input.slice_mut(
3475                            destination_start..destination_start + matrix.canonical_chunk_cols,
3476                        );
3477                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3478                    }
3479                    block_input
3480                };
3481                let partial = run_resident_bf16_rank_device(
3482                    &self.ranks[rank],
3483                    resident,
3484                    &block_input,
3485                    tokens,
3486                    None,
3487                    self.bulk_p2p,
3488                )?;
3489                block_input_keepalive.push(block_input);
3490                let root_partial = if rank == 0 {
3491                    partial
3492                } else {
3493                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3494                    // rank's kernel on its own stream; root's peer read must not overtake it.
3495                    {
3496                        let engine = &self.ranks[rank];
3497                        let _main = engine.gpu.enter_main()?;
3498                        engine.stream().synchronize()?;
3499                    }
3500                    let _main = root.gpu.enter_main()?;
3501                    let mut peer_partial = root.uninit(output_len)?;
3502                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3503                    remote_partial_keepalive.push(partial);
3504                    peer_partial
3505                };
3506                let next = {
3507                    let _main = root.gpu.enter_main()?;
3508                    let mut next = root.uninit(output_len)?;
3509                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3510                    next
3511                };
3512                reduced = next;
3513            }
3514        }
3515        {
3516            let _main = root.gpu.enter_main()?;
3517            root.stream().synchronize()?;
3518        }
3519        drop(remote_partial_keepalive);
3520        drop(root_packed_keepalive);
3521        drop(block_input_keepalive);
3522        Ok(reduced)
3523    }
3524
3525    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3526    /// on the root device.
3527    pub fn step_bf16_row_parallel_resident_root_device(
3528        &self,
3529        matrix: &ResidentStepBf16RowParallel,
3530        rank_activations: &[CudaSlice<f32>],
3531        tokens: usize,
3532    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3533        if self.ranks.len() > 1 && !self.native_p2p {
3534            return Err(
3535                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3536            );
3537        }
3538        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3539        let local_width = matrix.in_features / self.ranks.len();
3540        let shard_len = tokens
3541            .checked_mul(local_width)
3542            .ok_or("device Step BF16 row shard size overflow")?;
3543        if tokens == 0
3544            || rank_activations.len() != self.ranks.len()
3545            || rank_activations
3546                .iter()
3547                .zip(&self.ranks)
3548                .any(|(rows, engine)| {
3549                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3550                })
3551        {
3552            return Err("device Step BF16 row activation shard geometry changed".into());
3553        }
3554
3555        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3556        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3557        let mut partials = Vec::with_capacity(self.ranks.len());
3558        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3559            if blocks.len() != blocks_per_rank {
3560                return Err(format!(
3561                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3562                    blocks.len()
3563                )
3564                .into());
3565            }
3566            let engine = &self.ranks[rank];
3567            let _main = engine.gpu.enter_main()?;
3568            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3569            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3570            for (block, resident) in blocks.iter().enumerate() {
3571                let block_len = tokens
3572                    .checked_mul(matrix.canonical_chunk_cols)
3573                    .ok_or("device Step BF16 row block size overflow")?;
3574                let mut block_input = engine.uninit(block_len)?;
3575                let local_col_start = block * matrix.canonical_chunk_cols;
3576                if self.bulk_p2p {
3577                    engine.copy_rows_strided(
3578                        &rank_activations[rank],
3579                        &mut block_input,
3580                        matrix.canonical_chunk_cols,
3581                        tokens,
3582                        local_width,
3583                        local_col_start,
3584                    )?;
3585                } else {
3586                    for token in 0..tokens {
3587                        let source_start = token * local_width + local_col_start;
3588                        let source = rank_activations[rank]
3589                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3590                        let destination_start = token * matrix.canonical_chunk_cols;
3591                        let mut destination = block_input.slice_mut(
3592                            destination_start..destination_start + matrix.canonical_chunk_cols,
3593                        );
3594                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3595                    }
3596                }
3597                let partial = run_resident_bf16_rank_device(
3598                    engine,
3599                    resident,
3600                    &block_input,
3601                    tokens,
3602                    None,
3603                    self.bulk_p2p,
3604                )?;
3605                rank_inputs.push(block_input);
3606                rank_partials.push(partial);
3607            }
3608            block_inputs.push(rank_inputs);
3609            partials.push(rank_partials);
3610        }
3611        for engine in self.ranks.iter().skip(1) {
3612            let _main = engine.gpu.enter_main()?;
3613            engine.stream().synchronize()?;
3614        }
3615
3616        let output_len = tokens
3617            .checked_mul(matrix.out_features)
3618            .ok_or("device Step BF16 row output size overflow")?;
3619        let root = &self.ranks[0];
3620        let _main = root.gpu.enter_main()?;
3621        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3622        let mut remote_partials = Vec::new();
3623        for (rank, rank_partials) in partials.into_iter().enumerate() {
3624            for partial in rank_partials {
3625                let root_partial = if rank == 0 {
3626                    partial
3627                } else {
3628                    let mut peer_partial = root.uninit(output_len)?;
3629                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3630                    remote_partials.push(partial);
3631                    peer_partial
3632                };
3633                let mut next = root.uninit(output_len)?;
3634                root.add(&reduced, &root_partial, &mut next, output_len)?;
3635                reduced = next;
3636            }
3637        }
3638        root.stream().synchronize()?;
3639        drop(remote_partials);
3640        drop(block_inputs);
3641        Ok(reduced)
3642    }
3643
3644    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3645    pub fn step_bf16_row_parallel_resident_replicated_device(
3646        &self,
3647        matrix: &ResidentStepBf16RowParallel,
3648        rank_activations: &[CudaSlice<f32>],
3649        tokens: usize,
3650    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3651        let reduced =
3652            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3653        let output_len = tokens
3654            .checked_mul(matrix.out_features)
3655            .ok_or("device Step BF16 row output size overflow")?;
3656        let mut ranks = Vec::with_capacity(self.ranks.len());
3657        ranks.push(reduced);
3658        for engine in self.ranks.iter().skip(1) {
3659            let _main = engine.gpu.enter_main()?;
3660            let mut peer_output = engine.uninit(output_len)?;
3661            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3662            ranks.push(peer_output);
3663        }
3664        Ok(ResidentReplicatedDeviceRows {
3665            ranks,
3666            tokens,
3667            width: matrix.out_features,
3668        })
3669    }
3670
3671    pub fn upload_expert(
3672        &self,
3673        gate: E4m3BlockMatrix<'_>,
3674        up: E4m3BlockMatrix<'_>,
3675        down: E4m3BlockMatrix<'_>,
3676    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3677        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3678            return Err("TP expert gate/up dimensions differ".into());
3679        }
3680        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3681            return Err(format!(
3682                "TP expert down {}x{} does not invert gate/up {}x{}",
3683                down.out_features, down.in_features, gate.out_features, gate.in_features
3684            )
3685            .into());
3686        }
3687        Ok(ResidentTpExpert {
3688            gate: self.upload_column_parallel(gate)?,
3689            up: self.upload_column_parallel(up)?,
3690            down: self.upload_row_parallel(down)?,
3691            input_width: gate.in_features,
3692            expert_width: gate.out_features,
3693        })
3694    }
3695
3696    pub fn run_expert(
3697        &self,
3698        expert: &ResidentTpExpert,
3699        input: &[f32],
3700        tokens: usize,
3701    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3702        validate_activations(input, tokens, expert.input_width)?;
3703        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3704        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3705        let activated: Vec<f32> = gate
3706            .gathered
3707            .iter()
3708            .zip(&up.gathered)
3709            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3710            .collect();
3711        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3712        Ok(self
3713            .row_parallel_resident(&expert.down, &activated, tokens)?
3714            .reduced)
3715    }
3716
3717    pub fn upload_expert_parallel(
3718        &self,
3719        gate: E4m3ExpertBank<'_>,
3720        up: E4m3ExpertBank<'_>,
3721        down: E4m3ExpertBank<'_>,
3722    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3723        gate.validate()?;
3724        up.validate()?;
3725        down.validate()?;
3726        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3727            return Err("EP gate/up/down expert counts differ".into());
3728        }
3729        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3730            return Err("EP gate/up dimensions differ".into());
3731        }
3732        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3733            return Err(format!(
3734                "EP down {}x{} does not invert gate/up {}x{}",
3735                down.out_features, down.in_features, gate.out_features, gate.in_features
3736            )
3737            .into());
3738        }
3739        if gate.expert_count % self.ranks.len() != 0 {
3740            return Err(format!(
3741                "EP expert count {} is not divisible by {} ranks",
3742                gate.expert_count,
3743                self.ranks.len()
3744            )
3745            .into());
3746        }
3747
3748        let per_rank = gate.expert_count / self.ranks.len();
3749        let mut ranks = Vec::with_capacity(self.ranks.len());
3750        for (rank, engine) in self.ranks.iter().enumerate() {
3751            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3752            ranks.push(ResidentEpRank {
3753                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3754                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3755                down: upload_expert_bank_rank(engine, down, expert_range)?,
3756            });
3757        }
3758        Ok(ResidentExpertParallel {
3759            ranks,
3760            expert_count: gate.expert_count,
3761            input_width: gate.in_features,
3762            expert_width: gate.out_features,
3763        })
3764    }
3765
3766    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3767    ///
3768    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3769    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3770    /// without routing, transport, or combine changing underneath it.
3771    #[allow(clippy::too_many_arguments)]
3772    pub fn prepare_step_grouped_fp8_gate(
3773        &self,
3774        gate: E4m3ExpertBank<'_>,
3775        up: E4m3ExpertBank<'_>,
3776        down: E4m3ExpertBank<'_>,
3777        input: &[f32],
3778        tokens: usize,
3779        selected: &[usize],
3780        activation_limit: Option<f32>,
3781    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3782        gate.validate()?;
3783        up.validate()?;
3784        down.validate()?;
3785        validate_step_expert_activation_limit(activation_limit)?;
3786        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3787            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3788            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3789        {
3790            return Err(format!(
3791                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3792                 got gate/up/down={}/{}/{}",
3793                gate.expert_count, up.expert_count, down.expert_count,
3794            )
3795            .into());
3796        }
3797        if gate.in_features != up.in_features
3798            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3799            || up.out_features != STEP_GROUPED_FP8_WIDTH
3800            || down.in_features != STEP_GROUPED_FP8_WIDTH
3801            || down.out_features != gate.in_features
3802        {
3803            return Err(format!(
3804                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3805                gate.out_features,
3806                gate.in_features,
3807                up.out_features,
3808                up.in_features,
3809                down.out_features,
3810                down.in_features,
3811            )
3812            .into());
3813        }
3814        validate_activations(input, tokens, gate.in_features)?;
3815        let pairs = tokens
3816            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3817            .ok_or("official Step grouped FP8 route count overflow")?;
3818        if selected.len() != pairs {
3819            return Err(format!(
3820                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3821                 ({pairs})",
3822                selected.len()
3823            )
3824            .into());
3825        }
3826        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3827            let mut unique = routes.to_vec();
3828            unique.sort_unstable();
3829            unique.dedup();
3830            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3831                return Err(format!(
3832                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3833                     {routes:?}"
3834                )
3835                .into());
3836            }
3837        }
3838
3839        let engine = self
3840            .ranks
3841            .first()
3842            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3843        let _main = engine.gpu.enter_main()?;
3844        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3845        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3846        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3847        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3848        let input = engine.htod(input)?;
3849        let route_csr = ExpertCsr::from_token_routes(
3850            STEP_GROUPED_FP8_EXPERTS,
3851            tokens,
3852            STEP_GROUPED_FP8_TOP_K,
3853            selected,
3854        )?
3855        .upload(engine)?;
3856        let pair_rows = (0..pairs).collect::<Vec<_>>();
3857        let down_csr =
3858            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3859                .upload(engine)?;
3860        let gate_workspace =
3861            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3862        let up_workspace =
3863            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3864        let down_workspace =
3865            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3866        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3867        Ok(PreparedStepGroupedFp8Gate {
3868            device: engine.ctx().ordinal(),
3869            gate,
3870            up,
3871            down,
3872            input,
3873            route_csr,
3874            down_csr,
3875            gate_workspace,
3876            up_workspace,
3877            down_workspace,
3878            activation,
3879            activation_limit,
3880            tokens,
3881            pairs,
3882        })
3883    }
3884
3885    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3886    pub fn run_step_grouped_fp8_gate(
3887        &self,
3888        plan: &mut PreparedStepGroupedFp8Gate,
3889    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3890        let engine = self
3891            .ranks
3892            .first()
3893            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3894        if engine.ctx().ordinal() != plan.device {
3895            return Err(format!(
3896                "official Step grouped FP8 plan device {} != rank-zero device {}",
3897                plan.device,
3898                engine.ctx().ordinal()
3899            )
3900            .into());
3901        }
3902        let _main = engine.gpu.enter_main()?;
3903
3904        plan.gate_workspace.quantize(engine, &plan.input)?;
3905        plan.gate_workspace.project(
3906            engine,
3907            &plan.gate.codes,
3908            &plan.gate.scales,
3909            &plan.route_csr,
3910            plan.gate.code_stride,
3911            plan.gate.scale_stride,
3912            1.0,
3913        )?;
3914        plan.up_workspace.quantize(engine, &plan.input)?;
3915        plan.up_workspace.project(
3916            engine,
3917            &plan.up.codes,
3918            &plan.up.scales,
3919            &plan.route_csr,
3920            plan.up.code_stride,
3921            plan.up.scale_stride,
3922            1.0,
3923        )?;
3924        if let Some(limit) = plan.activation_limit {
3925            engine.silu_clamped_mul_host_expf(
3926                plan.gate_workspace.output(),
3927                plan.up_workspace.output(),
3928                limit,
3929                &mut plan.activation,
3930                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3931            )?;
3932        } else {
3933            engine.silu_mul_host_expf(
3934                plan.gate_workspace.output(),
3935                plan.up_workspace.output(),
3936                &mut plan.activation,
3937                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3938            )?;
3939        }
3940        plan.down_workspace.quantize(engine, &plan.activation)?;
3941        plan.down_workspace.project(
3942            engine,
3943            &plan.down.codes,
3944            &plan.down.scales,
3945            &plan.down_csr,
3946            plan.down.code_stride,
3947            plan.down.scale_stride,
3948            1.0,
3949        )?;
3950
3951        Ok(StepGroupedFp8ProjectionOutput {
3952            gate: engine.dtoh(plan.gate_workspace.output())?,
3953            up: engine.dtoh(plan.up_workspace.output())?,
3954            down: engine.dtoh(plan.down_workspace.output())?,
3955        })
3956    }
3957
3958    pub fn prepare_step_grouped_expert_parallel_gate(
3959        &self,
3960        experts: &ResidentExpertParallel,
3961        input: &[f32],
3962        tokens: usize,
3963        selected: &[usize],
3964        activation_limit: Option<f32>,
3965    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3966        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3967            experts,
3968            input,
3969            tokens,
3970            selected,
3971            activation_limit,
3972            tokens,
3973        )
3974    }
3975
3976    #[allow(clippy::too_many_arguments)]
3977    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3978        &self,
3979        experts: &ResidentExpertParallel,
3980        input: &[f32],
3981        tokens: usize,
3982        selected: &[usize],
3983        activation_limit: Option<f32>,
3984        max_tokens: usize,
3985    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3986        if !self.native_p2p || !self.ep_device_arithmetic {
3987            return Err(
3988                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3989            );
3990        }
3991        validate_step_expert_activation_limit(activation_limit)?;
3992        validate_ep_residency(&self.ranks, experts)?;
3993        validate_activations(input, tokens, experts.input_width)?;
3994        if max_tokens < tokens || max_tokens > i32::MAX as usize {
3995            return Err(format!(
3996                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
3997            )
3998            .into());
3999        }
4000        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
4001            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
4002        {
4003            return Err(format!(
4004                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
4005                STEP_GROUPED_FP8_EXPERTS,
4006                STEP_GROUPED_FP8_WIDTH,
4007                experts.expert_count,
4008                experts.expert_width,
4009            )
4010            .into());
4011        }
4012        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4013        let max_pairs = max_tokens
4014            .checked_mul(STEP_GROUPED_FP8_TOP_K)
4015            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
4016        let input_capacity = max_tokens
4017            .checked_mul(experts.input_width)
4018            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
4019
4020        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
4021        for engine in &self.ranks {
4022            let _main = engine.gpu.enter_main()?;
4023            rank_inputs.push(engine.uninit(input_capacity)?);
4024        }
4025
4026        let mut owners = Vec::with_capacity(self.ranks.len());
4027        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
4028            if rank.gate.expert_range != rank.up.expert_range
4029                || rank.gate.expert_range != rank.down.expert_range
4030            {
4031                return Err(format!(
4032                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
4033                    owner_rank
4034                )
4035                .into());
4036            }
4037            let local_experts = rank.gate.expert_range.len();
4038            let engine = &self.ranks[owner_rank];
4039            let _main = engine.gpu.enter_main()?;
4040            let route_csr =
4041                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
4042            let down_csr =
4043                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
4044            let gate_workspace = Fp8GroupedWorkspace::new(
4045                engine,
4046                experts.input_width,
4047                experts.expert_width,
4048                max_tokens,
4049                max_pairs,
4050            )?;
4051            let up_workspace = Fp8GroupedWorkspace::new(
4052                engine,
4053                experts.input_width,
4054                experts.expert_width,
4055                max_tokens,
4056                max_pairs,
4057            )?;
4058            let down_workspace = Fp8GroupedWorkspace::new(
4059                engine,
4060                experts.expert_width,
4061                experts.input_width,
4062                max_pairs,
4063                max_pairs,
4064            )?;
4065            let activation = engine.uninit(
4066                max_pairs
4067                    .checked_mul(experts.expert_width)
4068                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
4069            )?;
4070            owners.push(PreparedStepGroupedExpertOwner {
4071                rank: owner_rank,
4072                global_pairs: Vec::new(),
4073                route_csr,
4074                down_csr,
4075                gate_workspace,
4076                up_workspace,
4077                down_workspace,
4078                activation,
4079            });
4080        }
4081
4082        let mut plan = PreparedStepGroupedExpertParallelGate {
4083            rank_inputs,
4084            owners,
4085            activation_limit,
4086            tokens: 0,
4087            pairs: 0,
4088            max_tokens,
4089            max_pairs,
4090            input_width: experts.input_width,
4091            expert_width: experts.expert_width,
4092            generation: 0,
4093            executed_generation: None,
4094            ready: false,
4095        };
4096        self.refresh_step_grouped_expert_parallel_gate(
4097            experts, &mut plan, input, tokens, selected,
4098        )?;
4099        Ok(plan)
4100    }
4101
4102    fn prepare_step_grouped_expert_parallel_refresh(
4103        &self,
4104        experts: &ResidentExpertParallel,
4105        plan: &PreparedStepGroupedExpertParallelGate,
4106        tokens: usize,
4107        selected: &[usize],
4108    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
4109    {
4110        validate_ep_residency(&self.ranks, experts)?;
4111        if plan.rank_inputs.len() != self.ranks.len()
4112            || plan.owners.len() != self.ranks.len()
4113            || plan.input_width != experts.input_width
4114            || plan.expert_width != experts.expert_width
4115            || tokens > plan.max_tokens
4116        {
4117            return Err(format!(
4118                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
4119                 input={}/{} expert={}/{} tokens={}/{}",
4120                plan.rank_inputs.len(),
4121                self.ranks.len(),
4122                plan.owners.len(),
4123                self.ranks.len(),
4124                plan.input_width,
4125                experts.input_width,
4126                plan.expert_width,
4127                experts.expert_width,
4128                tokens,
4129                plan.max_tokens,
4130            )
4131            .into());
4132        }
4133        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4134        if pairs > plan.max_pairs {
4135            return Err(format!(
4136                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
4137                plan.max_pairs
4138            )
4139            .into());
4140        }
4141        let next_generation = plan
4142            .generation
4143            .checked_add(1)
4144            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4145        let owner_routes = partition_expert_owner_routes(
4146            experts.expert_count,
4147            self.ranks.len(),
4148            tokens,
4149            STEP_GROUPED_FP8_TOP_K,
4150            selected,
4151        )?;
4152        let mut schedules = Vec::with_capacity(self.ranks.len());
4153        for routes in owner_routes {
4154            if routes.selected.is_empty() {
4155                schedules.push(None);
4156                continue;
4157            }
4158            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
4159            let local_pairs = routes.selected.len();
4160            let route_csr = ExpertCsr::from_pair_rows(
4161                local_experts,
4162                tokens,
4163                &routes.selected,
4164                &routes.token_rows,
4165            )?;
4166            let down_rows = (0..local_pairs).collect::<Vec<_>>();
4167            let down_csr = ExpertCsr::from_pair_rows(
4168                local_experts,
4169                local_pairs,
4170                &routes.selected,
4171                &down_rows,
4172            )?;
4173            schedules.push(Some(StepGroupedExpertOwnerSchedule {
4174                global_pairs: routes.global_pairs,
4175                route_csr,
4176                down_csr,
4177            }));
4178        }
4179        Ok((pairs, next_generation, schedules))
4180    }
4181
4182    fn commit_step_grouped_expert_parallel_refresh(
4183        &self,
4184        plan: &mut PreparedStepGroupedExpertParallelGate,
4185        tokens: usize,
4186        pairs: usize,
4187        next_generation: u64,
4188        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
4189    ) -> Result<(), Box<dyn std::error::Error>> {
4190        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
4191            let engine = &self.ranks[owner.rank];
4192            let _main = engine.gpu.enter_main()?;
4193            if let Some(schedule) = schedule {
4194                owner.route_csr.refresh(engine, &schedule.route_csr)?;
4195                owner.down_csr.refresh(engine, &schedule.down_csr)?;
4196                owner.global_pairs = schedule.global_pairs;
4197            } else {
4198                owner.route_csr.clear();
4199                owner.down_csr.clear();
4200                owner.global_pairs.clear();
4201            }
4202        }
4203        plan.tokens = tokens;
4204        plan.pairs = pairs;
4205        plan.generation = next_generation;
4206        plan.ready = true;
4207        Ok(())
4208    }
4209
4210    pub fn refresh_step_grouped_expert_parallel_gate(
4211        &self,
4212        experts: &ResidentExpertParallel,
4213        plan: &mut PreparedStepGroupedExpertParallelGate,
4214        input: &[f32],
4215        tokens: usize,
4216        selected: &[usize],
4217    ) -> Result<(), Box<dyn std::error::Error>> {
4218        validate_activations(input, tokens, experts.input_width)?;
4219        let (pairs, next_generation, schedules) =
4220            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4221
4222        plan.ready = false;
4223        plan.executed_generation = None;
4224        {
4225            let root = &self.ranks[0];
4226            let _main = root.gpu.enter_main()?;
4227            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
4228            root.stream().memcpy_htod(input, &mut destination)?;
4229            root.stream().synchronize()?;
4230        }
4231        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4232        let root_input = &root_inputs[0];
4233        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4234            let engine = &self.ranks[rank + 1];
4235            let _main = engine.gpu.enter_main()?;
4236            let mut destination = peer_input.slice_mut(0..input.len());
4237            engine
4238                .stream()
4239                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
4240        }
4241        self.commit_step_grouped_expert_parallel_refresh(
4242            plan,
4243            tokens,
4244            pairs,
4245            next_generation,
4246            schedules,
4247        )
4248    }
4249
4250    /// Refresh routes and inputs from an already-resident rank-zero activation.
4251    ///
4252    /// The caller must order the source producer before this call. The root copy is completed
4253    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
4254    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
4255        &self,
4256        experts: &ResidentExpertParallel,
4257        plan: &mut PreparedStepGroupedExpertParallelGate,
4258        input: &CudaSlice<f32>,
4259        tokens: usize,
4260        selected: &[usize],
4261    ) -> Result<(), Box<dyn std::error::Error>> {
4262        let input_values = tokens
4263            .checked_mul(experts.input_width)
4264            .ok_or("Step owner-grouped FP8 input size overflow")?;
4265        let root = self
4266            .ranks
4267            .first()
4268            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
4269        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
4270            return Err(format!(
4271                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
4272                 device {}",
4273                input.len(),
4274                input.ordinal(),
4275                input_values,
4276                root.ctx().ordinal(),
4277            )
4278            .into());
4279        }
4280        let (pairs, next_generation, schedules) =
4281            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4282
4283        plan.ready = false;
4284        plan.executed_generation = None;
4285        {
4286            let _main = root.gpu.enter_main()?;
4287            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
4288            root.stream()
4289                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
4290            root.stream().synchronize()?;
4291        }
4292        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4293        let root_input = &root_inputs[0];
4294        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4295            let engine = &self.ranks[rank + 1];
4296            let _main = engine.gpu.enter_main()?;
4297            let mut destination = peer_input.slice_mut(0..input_values);
4298            engine
4299                .stream()
4300                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
4301        }
4302        self.commit_step_grouped_expert_parallel_refresh(
4303            plan,
4304            tokens,
4305            pairs,
4306            next_generation,
4307            schedules,
4308        )
4309    }
4310
4311    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
4312    ///
4313    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
4314    /// and combine result, so callers must refresh combine metadata before executing again.
4315    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
4316        &self,
4317        experts: &ResidentExpertParallel,
4318        plan: &mut PreparedStepGroupedExpertParallelGate,
4319        input: &ResidentReplicatedDeviceRows,
4320    ) -> Result<(), Box<dyn std::error::Error>> {
4321        validate_ep_residency(&self.ranks, experts)?;
4322        validate_replicated_device_rows(&self.ranks, input)?;
4323        if !plan.ready
4324            || input.tokens != plan.tokens
4325            || input.width != plan.input_width
4326            || input.tokens > plan.max_tokens
4327            || plan.rank_inputs.len() != self.ranks.len()
4328            || plan.owners.len() != self.ranks.len()
4329            || plan.input_width != experts.input_width
4330            || plan.expert_width != experts.expert_width
4331        {
4332            return Err("Step owner-grouped replicated input geometry changed".into());
4333        }
4334        let values = input
4335            .tokens
4336            .checked_mul(input.width)
4337            .ok_or("Step owner-grouped replicated input size overflow")?;
4338        let next_generation = plan
4339            .generation
4340            .checked_add(1)
4341            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4342        plan.ready = false;
4343        plan.executed_generation = None;
4344        for (rank, engine) in self.ranks.iter().enumerate() {
4345            let _main = engine.gpu.enter_main()?;
4346            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
4347            engine
4348                .stream()
4349                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
4350        }
4351        plan.generation = next_generation;
4352        plan.ready = true;
4353        Ok(())
4354    }
4355
4356    pub fn execute_step_grouped_expert_parallel_gate(
4357        &self,
4358        experts: &ResidentExpertParallel,
4359        plan: &mut PreparedStepGroupedExpertParallelGate,
4360    ) -> Result<(), Box<dyn std::error::Error>> {
4361        validate_ep_residency(&self.ranks, experts)?;
4362        if !plan.ready
4363            || plan.rank_inputs.len() != self.ranks.len()
4364            || plan.owners.len() != self.ranks.len()
4365            || plan.input_width != experts.input_width
4366            || plan.expert_width != experts.expert_width
4367        {
4368            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
4369        }
4370        plan.executed_generation = None;
4371
4372        for owner in &mut plan.owners {
4373            if owner.global_pairs.is_empty() {
4374                continue;
4375            }
4376            let engine = &self.ranks[owner.rank];
4377            let bank = &experts.ranks[owner.rank];
4378            let _main = engine.gpu.enter_main()?;
4379            let local_pairs = owner.global_pairs.len();
4380            owner.gate_workspace.quantize_for_shape(
4381                engine,
4382                &plan.rank_inputs[owner.rank],
4383                plan.tokens,
4384                local_pairs,
4385            )?;
4386            owner.gate_workspace.project(
4387                engine,
4388                &bank.gate.codes,
4389                &bank.gate.scales,
4390                &owner.route_csr,
4391                bank.gate.code_stride,
4392                bank.gate.scale_stride,
4393                1.0,
4394            )?;
4395            owner.up_workspace.quantize_for_shape(
4396                engine,
4397                &plan.rank_inputs[owner.rank],
4398                plan.tokens,
4399                local_pairs,
4400            )?;
4401            owner.up_workspace.project(
4402                engine,
4403                &bank.up.codes,
4404                &bank.up.scales,
4405                &owner.route_csr,
4406                bank.up.code_stride,
4407                bank.up.scale_stride,
4408                1.0,
4409            )?;
4410        }
4411        for owner in &mut plan.owners {
4412            if owner.global_pairs.is_empty() {
4413                continue;
4414            }
4415            let engine = &self.ranks[owner.rank];
4416            let _main = engine.gpu.enter_main()?;
4417            let values = owner.global_pairs.len() * plan.expert_width;
4418            if let Some(limit) = plan.activation_limit {
4419                engine.silu_clamped_mul_host_expf(
4420                    owner.gate_workspace.output(),
4421                    owner.up_workspace.output(),
4422                    limit,
4423                    &mut owner.activation,
4424                    values,
4425                )?;
4426            } else {
4427                engine.silu_mul_host_expf(
4428                    owner.gate_workspace.output(),
4429                    owner.up_workspace.output(),
4430                    &mut owner.activation,
4431                    values,
4432                )?;
4433            }
4434        }
4435        for owner in &mut plan.owners {
4436            if owner.global_pairs.is_empty() {
4437                continue;
4438            }
4439            let engine = &self.ranks[owner.rank];
4440            let bank = &experts.ranks[owner.rank];
4441            let _main = engine.gpu.enter_main()?;
4442            let local_pairs = owner.global_pairs.len();
4443            owner.down_workspace.quantize_for_shape(
4444                engine,
4445                &owner.activation,
4446                local_pairs,
4447                local_pairs,
4448            )?;
4449            owner.down_workspace.project(
4450                engine,
4451                &bank.down.codes,
4452                &bank.down.scales,
4453                &owner.down_csr,
4454                bank.down.code_stride,
4455                bank.down.scale_stride,
4456                1.0,
4457            )?;
4458        }
4459        plan.executed_generation = Some(plan.generation);
4460        Ok(())
4461    }
4462
4463    pub fn collect_step_grouped_expert_parallel_gate(
4464        &self,
4465        plan: &PreparedStepGroupedExpertParallelGate,
4466    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4467        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4468            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4469        }
4470        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4471        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4472        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4473        for owner in &plan.owners {
4474            if owner.global_pairs.is_empty() {
4475                continue;
4476            }
4477            let engine = &self.ranks[owner.rank];
4478            let _main = engine.gpu.enter_main()?;
4479            let owner_gate = engine.dtoh_view(
4480                &owner
4481                    .gate_workspace
4482                    .output()
4483                    .slice(0..owner.gate_workspace.output_len()),
4484            )?;
4485            let owner_up = engine.dtoh_view(
4486                &owner
4487                    .up_workspace
4488                    .output()
4489                    .slice(0..owner.up_workspace.output_len()),
4490            )?;
4491            let owner_down = engine.dtoh_view(
4492                &owner
4493                    .down_workspace
4494                    .output()
4495                    .slice(0..owner.down_workspace.output_len()),
4496            )?;
4497            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4498                let local_expert = local_pair * plan.expert_width;
4499                let global_expert = global_pair * plan.expert_width;
4500                gate[global_expert..global_expert + plan.expert_width]
4501                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4502                up[global_expert..global_expert + plan.expert_width]
4503                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4504
4505                let local_hidden = local_pair * plan.input_width;
4506                let global_hidden = global_pair * plan.input_width;
4507                down[global_hidden..global_hidden + plan.input_width]
4508                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4509            }
4510        }
4511        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4512    }
4513
4514    pub fn run_step_grouped_expert_parallel_gate(
4515        &self,
4516        experts: &ResidentExpertParallel,
4517        plan: &mut PreparedStepGroupedExpertParallelGate,
4518    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4519        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4520        self.collect_step_grouped_expert_parallel_gate(plan)
4521    }
4522
4523    pub fn prepare_step_grouped_expert_parallel_combine(
4524        &self,
4525        plan: &PreparedStepGroupedExpertParallelGate,
4526        route_weights: &[f32],
4527    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4528        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4529            return Err(
4530                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4531            );
4532        }
4533        let owner_pairs = plan
4534            .owners
4535            .iter()
4536            .map(|owner| owner.global_pairs.as_slice())
4537            .collect::<Vec<_>>();
4538        let shape = validate_weighted_route_combine(
4539            plan.input_width,
4540            STEP_GROUPED_FP8_TOP_K,
4541            plan.max_tokens,
4542            plan.tokens,
4543            &owner_pairs,
4544            route_weights,
4545        )?;
4546        if shape.max_pairs != plan.max_pairs {
4547            return Err(format!(
4548                "Step owner-grouped combine capacity {} != projection capacity {}",
4549                shape.max_pairs, plan.max_pairs
4550            )
4551            .into());
4552        }
4553        let root = self
4554            .ranks
4555            .first()
4556            .ok_or("Step owner-grouped combine has no root rank")?;
4557        let slot_values = shape
4558            .max_pairs
4559            .checked_mul(plan.input_width)
4560            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4561        let output_values = plan
4562            .max_tokens
4563            .checked_mul(plan.input_width)
4564            .ok_or("Step owner-grouped combine output capacity overflow")?;
4565        let (root_device, owners, peer_staging, slots, weights, output) = {
4566            let _main = root.gpu.enter_main()?;
4567            let mut owners = Vec::with_capacity(plan.owners.len());
4568            for _ in &plan.owners {
4569                owners.push(PreparedPeerWeightedRouteOwner {
4570                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4571                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4572                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4573                    active_pairs: 0,
4574                });
4575            }
4576            (
4577                root.ctx().ordinal(),
4578                owners,
4579                root.uninit(slot_values)?,
4580                root.uninit(slot_values)?,
4581                root.uninit(shape.max_pairs)?,
4582                root.uninit(output_values)?,
4583            )
4584        };
4585        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4586        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4587        for engine in self.ranks.iter().skip(1) {
4588            let _main = engine.gpu.enter_main()?;
4589            peer_devices.push(engine.ctx().ordinal());
4590            peer_outputs.push(engine.uninit(output_values)?);
4591        }
4592        let mut combine = PreparedPeerWeightedRouteCombine {
4593            root_device,
4594            owners,
4595            peer_staging,
4596            slots,
4597            weights,
4598            output,
4599            peer_devices,
4600            peer_outputs,
4601            width: plan.input_width,
4602            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4603            max_tokens: plan.max_tokens,
4604            max_pairs: shape.max_pairs,
4605            tokens: 0,
4606            pairs: 0,
4607            projection_generation: 0,
4608            output_generation: None,
4609            broadcast_generation: None,
4610            ready: false,
4611        };
4612        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4613        Ok(combine)
4614    }
4615
4616    pub fn refresh_step_grouped_expert_parallel_combine(
4617        &self,
4618        plan: &PreparedStepGroupedExpertParallelGate,
4619        combine: &mut PreparedPeerWeightedRouteCombine,
4620        route_weights: &[f32],
4621    ) -> Result<(), Box<dyn std::error::Error>> {
4622        let output_capacity = combine
4623            .max_tokens
4624            .checked_mul(combine.width)
4625            .ok_or("Step owner-grouped combine output capacity overflow")?;
4626        if !plan.ready
4627            || combine.owners.len() != plan.owners.len()
4628            || combine.peer_devices.len() + 1 != self.ranks.len()
4629            || combine.peer_outputs.len() + 1 != self.ranks.len()
4630            || combine.width != plan.input_width
4631            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4632            || combine.max_tokens != plan.max_tokens
4633            || combine.max_pairs != plan.max_pairs
4634            || combine.output.len() < output_capacity
4635            || combine
4636                .peer_outputs
4637                .iter()
4638                .any(|output| output.len() < output_capacity)
4639        {
4640            return Err("Step owner-grouped combine/projection geometry changed".into());
4641        }
4642        if self
4643            .ranks
4644            .iter()
4645            .skip(1)
4646            .zip(&combine.peer_devices)
4647            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4648        {
4649            return Err("Step owner-grouped combine peer devices changed".into());
4650        }
4651        let owner_pairs = plan
4652            .owners
4653            .iter()
4654            .map(|owner| owner.global_pairs.as_slice())
4655            .collect::<Vec<_>>();
4656        let shape = validate_weighted_route_combine(
4657            combine.width,
4658            combine.experts_per_token,
4659            combine.max_tokens,
4660            plan.tokens,
4661            &owner_pairs,
4662            route_weights,
4663        )?;
4664        if shape.max_pairs != combine.max_pairs {
4665            return Err("Step owner-grouped combine capacity changed during refresh".into());
4666        }
4667        let metadata = owner_pairs
4668            .iter()
4669            .map(|pairs| {
4670                let token_rows = pairs
4671                    .iter()
4672                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4673                    .collect::<Vec<_>>();
4674                let slots = pairs
4675                    .iter()
4676                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4677                    .collect::<Vec<_>>();
4678                let weights = pairs
4679                    .iter()
4680                    .map(|&pair| route_weights[pair])
4681                    .collect::<Vec<_>>();
4682                (token_rows, slots, weights)
4683            })
4684            .collect::<Vec<_>>();
4685
4686        combine.ready = false;
4687        combine.output_generation = None;
4688        combine.broadcast_generation = None;
4689        let root = self
4690            .ranks
4691            .first()
4692            .ok_or("Step owner-grouped combine has no root rank")?;
4693        let _main = root.gpu.enter_main()?;
4694        if root.ctx().ordinal() != combine.root_device {
4695            return Err(format!(
4696                "Step owner-grouped combine root device changed {} != {}",
4697                root.ctx().ordinal(),
4698                combine.root_device
4699            )
4700            .into());
4701        }
4702        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4703            if token_rows.is_empty() {
4704                owner.active_pairs = 0;
4705                continue;
4706            }
4707            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4708            root.htod_i32_into(&mut owner.slots, &slots)?;
4709            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4710            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4711            owner.active_pairs = token_rows.len();
4712        }
4713        combine.tokens = plan.tokens;
4714        combine.pairs = shape.pairs;
4715        combine.projection_generation = plan.generation;
4716        combine.ready = true;
4717        Ok(())
4718    }
4719
4720    pub fn execute_step_grouped_expert_parallel_combine(
4721        &self,
4722        plan: &PreparedStepGroupedExpertParallelGate,
4723        combine: &mut PreparedPeerWeightedRouteCombine,
4724    ) -> Result<(), Box<dyn std::error::Error>> {
4725        if !plan.ready
4726            || plan.executed_generation != Some(plan.generation)
4727            || !combine.ready
4728            || combine.tokens != plan.tokens
4729            || combine.pairs != plan.pairs
4730            || combine.width != plan.input_width
4731            || combine.owners.len() != plan.owners.len()
4732            || combine.projection_generation != plan.generation
4733        {
4734            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4735        }
4736        combine.output_generation = None;
4737        combine.broadcast_generation = None;
4738        for owner in &plan.owners {
4739            if owner.rank == 0 || owner.global_pairs.is_empty() {
4740                continue;
4741            }
4742            let engine = &self.ranks[owner.rank];
4743            let _main = engine.gpu.enter_main()?;
4744            engine.stream().synchronize()?;
4745        }
4746        let root = self
4747            .ranks
4748            .first()
4749            .ok_or("Step owner-grouped combine has no root rank")?;
4750        let _main = root.gpu.enter_main()?;
4751        if root.ctx().ordinal() != combine.root_device {
4752            return Err("Step owner-grouped combine is not resident on the root device".into());
4753        }
4754        for (index, owner) in plan.owners.iter().enumerate() {
4755            let metadata = &combine.owners[index];
4756            if owner.global_pairs.len() != metadata.active_pairs {
4757                return Err(format!(
4758                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4759                    owner.global_pairs.len(),
4760                    metadata.active_pairs
4761                )
4762                .into());
4763            }
4764            if metadata.active_pairs == 0 {
4765                continue;
4766            }
4767            let values = metadata
4768                .active_pairs
4769                .checked_mul(combine.width)
4770                .ok_or("Step owner-grouped combine peer value count overflow")?;
4771            if owner.rank == 0 {
4772                root.scatter_slot(
4773                    owner.down_workspace.output(),
4774                    &metadata.token_rows,
4775                    &metadata.slots,
4776                    &metadata.weights,
4777                    &mut combine.slots,
4778                    &mut combine.weights,
4779                    combine.width,
4780                    combine.experts_per_token,
4781                    metadata.active_pairs,
4782                )?;
4783            } else {
4784                let source = owner.down_workspace.output().slice(0..values);
4785                let mut destination = combine.peer_staging.slice_mut(0..values);
4786                root.stream().memcpy_dtod(&source, &mut destination)?;
4787                root.scatter_slot(
4788                    &combine.peer_staging,
4789                    &metadata.token_rows,
4790                    &metadata.slots,
4791                    &metadata.weights,
4792                    &mut combine.slots,
4793                    &mut combine.weights,
4794                    combine.width,
4795                    combine.experts_per_token,
4796                    metadata.active_pairs,
4797                )?;
4798            }
4799        }
4800        root.reduce_slots_host(
4801            &combine.slots,
4802            &combine.weights,
4803            &mut combine.output,
4804            combine.width,
4805            combine.experts_per_token,
4806            combine.tokens,
4807        )?;
4808        combine.output_generation = Some(plan.generation);
4809        Ok(())
4810    }
4811
4812    pub fn collect_step_grouped_expert_parallel_combine(
4813        &self,
4814        plan: &PreparedStepGroupedExpertParallelGate,
4815        combine: &PreparedPeerWeightedRouteCombine,
4816    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4817        if !plan.ready
4818            || combine.output_generation != Some(plan.generation)
4819            || combine.projection_generation != plan.generation
4820        {
4821            return Err("Step owner-grouped combine output is stale or has not executed".into());
4822        }
4823        let root = self
4824            .ranks
4825            .first()
4826            .ok_or("Step owner-grouped combine has no root rank")?;
4827        let _main = root.gpu.enter_main()?;
4828        if root.ctx().ordinal() != combine.root_device {
4829            return Err("Step owner-grouped combine is not resident on the root device".into());
4830        }
4831        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4832    }
4833
4834    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4835    ///
4836    /// The persistent combine buffer remains reusable by the next route generation; the returned
4837    /// allocation follows the serving runtime's ordinary transient-output ownership.
4838    pub fn copy_step_grouped_expert_parallel_combine_root(
4839        &self,
4840        plan: &PreparedStepGroupedExpertParallelGate,
4841        combine: &PreparedPeerWeightedRouteCombine,
4842        destination: &Engine,
4843    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4844        if !plan.ready
4845            || combine.output_generation != Some(plan.generation)
4846            || combine.projection_generation != plan.generation
4847        {
4848            return Err("Step owner-grouped combine output is stale or has not executed".into());
4849        }
4850        let root = self
4851            .ranks
4852            .first()
4853            .ok_or("Step owner-grouped combine has no root rank")?;
4854        if root.ctx().ordinal() != combine.root_device
4855            || destination.ctx().ordinal() != combine.root_device
4856        {
4857            return Err(format!(
4858                "Step owner-grouped combine root/destination devices {}/{} != {}",
4859                root.ctx().ordinal(),
4860                destination.ctx().ordinal(),
4861                combine.root_device,
4862            )
4863            .into());
4864        }
4865        let values = combine
4866            .tokens
4867            .checked_mul(combine.width)
4868            .ok_or("Step owner-grouped combine copy size overflow")?;
4869        {
4870            let _main = root.gpu.enter_main()?;
4871            root.stream().synchronize()?;
4872        }
4873        let _main = destination.gpu.enter_main()?;
4874        let mut output = destination.uninit(values)?;
4875        destination
4876            .stream()
4877            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4878        Ok(output)
4879    }
4880
4881    pub fn broadcast_step_grouped_expert_parallel_combine(
4882        &self,
4883        plan: &PreparedStepGroupedExpertParallelGate,
4884        combine: &mut PreparedPeerWeightedRouteCombine,
4885    ) -> Result<(), Box<dyn std::error::Error>> {
4886        if !plan.ready
4887            || combine.output_generation != Some(plan.generation)
4888            || combine.projection_generation != plan.generation
4889            || combine.peer_devices.len() + 1 != self.ranks.len()
4890            || combine.peer_outputs.len() + 1 != self.ranks.len()
4891        {
4892            return Err("Step owner-grouped combine output cannot be broadcast".into());
4893        }
4894        combine.broadcast_generation = None;
4895        let values = combine
4896            .tokens
4897            .checked_mul(combine.width)
4898            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4899        {
4900            let root = self
4901                .ranks
4902                .first()
4903                .ok_or("Step owner-grouped combine has no root rank")?;
4904            let _main = root.gpu.enter_main()?;
4905            if root.ctx().ordinal() != combine.root_device {
4906                return Err("Step owner-grouped combine root device changed".into());
4907            }
4908            root.stream().synchronize()?;
4909        }
4910        let source = &combine.output;
4911        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4912            let engine = &self.ranks[index + 1];
4913            let _main = engine.gpu.enter_main()?;
4914            if engine.ctx().ordinal() != combine.peer_devices[index] {
4915                return Err(format!(
4916                    "Step owner-grouped combine peer {} device changed",
4917                    index + 1
4918                )
4919                .into());
4920            }
4921            let mut destination = destination_buffer.slice_mut(0..values);
4922            engine
4923                .stream()
4924                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4925        }
4926        combine.broadcast_generation = Some(plan.generation);
4927        Ok(())
4928    }
4929
4930    pub fn collect_step_grouped_expert_parallel_broadcast(
4931        &self,
4932        plan: &PreparedStepGroupedExpertParallelGate,
4933        combine: &PreparedPeerWeightedRouteCombine,
4934    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4935        if !plan.ready
4936            || combine.output_generation != Some(plan.generation)
4937            || combine.broadcast_generation != Some(plan.generation)
4938            || combine.peer_outputs.len() + 1 != self.ranks.len()
4939        {
4940            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4941        }
4942        let values = combine
4943            .tokens
4944            .checked_mul(combine.width)
4945            .ok_or("Step owner-grouped combine collection size overflow")?;
4946        let mut outputs = Vec::with_capacity(self.ranks.len());
4947        {
4948            let root = &self.ranks[0];
4949            let _main = root.gpu.enter_main()?;
4950            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4951        }
4952        for (index, output) in combine.peer_outputs.iter().enumerate() {
4953            let engine = &self.ranks[index + 1];
4954            let _main = engine.gpu.enter_main()?;
4955            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4956        }
4957        Ok(outputs)
4958    }
4959
4960    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4961    pub fn finish_step_grouped_expert_parallel_layer(
4962        &self,
4963        plan: &PreparedStepGroupedExpertParallelGate,
4964        combine: &PreparedPeerWeightedRouteCombine,
4965        shared: &ResidentReplicatedDeviceRows,
4966        residual: &ResidentReplicatedDeviceRows,
4967    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4968        validate_replicated_device_rows(&self.ranks, shared)?;
4969        validate_replicated_device_rows(&self.ranks, residual)?;
4970        if !plan.ready
4971            || plan.executed_generation != Some(plan.generation)
4972            || combine.output_generation != Some(plan.generation)
4973            || combine.broadcast_generation != Some(plan.generation)
4974            || combine.projection_generation != plan.generation
4975            || combine.peer_outputs.len() + 1 != self.ranks.len()
4976            || shared.tokens != combine.tokens
4977            || residual.tokens != combine.tokens
4978            || shared.width != combine.width
4979            || residual.width != combine.width
4980        {
4981            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4982        }
4983        let values = combine
4984            .tokens
4985            .checked_mul(combine.width)
4986            .ok_or("Step full-layer output size overflow")?;
4987        let mut ranks = Vec::with_capacity(self.ranks.len());
4988        for rank in 0..self.ranks.len() {
4989            let engine = &self.ranks[rank];
4990            let _main = engine.gpu.enter_main()?;
4991            let routed = if rank == 0 {
4992                &combine.output
4993            } else {
4994                &combine.peer_outputs[rank - 1]
4995            };
4996            let mut ffn = engine.uninit(values)?;
4997            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
4998            let mut output = engine.uninit(values)?;
4999            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
5000            ranks.push(output);
5001        }
5002        Ok(ResidentReplicatedDeviceRows {
5003            ranks,
5004            tokens: combine.tokens,
5005            width: combine.width,
5006        })
5007    }
5008
5009    pub fn run_step_grouped_expert_parallel_combine(
5010        &self,
5011        plan: &PreparedStepGroupedExpertParallelGate,
5012        combine: &mut PreparedPeerWeightedRouteCombine,
5013    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5014        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
5015        self.collect_step_grouped_expert_parallel_combine(plan, combine)
5016    }
5017
5018    pub fn upload_tensor_parallel(
5019        &self,
5020        gate: E4m3ExpertBank<'_>,
5021        up: E4m3ExpertBank<'_>,
5022        down: E4m3ExpertBank<'_>,
5023    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
5024        gate.validate()?;
5025        up.validate()?;
5026        down.validate()?;
5027        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
5028            return Err("TP gate/up/down expert counts differ".into());
5029        }
5030        if gate.in_features != up.in_features || gate.out_features != up.out_features {
5031            return Err("TP gate/up dimensions differ".into());
5032        }
5033        if down.in_features != gate.out_features || down.out_features != gate.in_features {
5034            return Err(format!(
5035                "TP down {}x{} does not invert gate/up {}x{}",
5036                down.out_features, down.in_features, gate.out_features, gate.in_features
5037            )
5038            .into());
5039        }
5040        let tp = self.ranks.len();
5041        validate_column_bank_shape(gate, tp)?;
5042        validate_column_bank_shape(up, tp)?;
5043        validate_row_bank_shape(down, tp)?;
5044
5045        let mut gate_ranks = Vec::with_capacity(tp);
5046        let mut up_ranks = Vec::with_capacity(tp);
5047        let mut down_ranks = Vec::with_capacity(tp);
5048        for (rank, engine) in self.ranks.iter().enumerate() {
5049            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
5050            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
5051            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
5052        }
5053        Ok(ResidentTensorParallel {
5054            bank: ResidentTpExpertBank {
5055                gate: gate_ranks,
5056                up: up_ranks,
5057                down: down_ranks,
5058                expert_count: gate.expert_count,
5059                input_width: gate.in_features,
5060                expert_width: gate.out_features,
5061            },
5062        })
5063    }
5064
5065    pub fn run_tensor_parallel_routes(
5066        &self,
5067        experts: &ResidentTensorParallel,
5068        input: &[f32],
5069        tokens: usize,
5070        selected: &[usize],
5071        route_weights: &[f32],
5072        experts_per_token: usize,
5073    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5074        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
5075        validate_activations(input, tokens, experts.bank.input_width)?;
5076        let pairs = tokens
5077            .checked_mul(experts_per_token)
5078            .ok_or("TP route count overflow")?;
5079        if selected.len() != pairs || route_weights.len() != pairs {
5080            return Err(format!(
5081                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
5082                 {experts_per_token} ({pairs})",
5083                selected.len(),
5084                route_weights.len(),
5085            )
5086            .into());
5087        }
5088        if !route_weights.iter().all(|weight| weight.is_finite()) {
5089            return Err("TP route weights contain a non-finite value".into());
5090        }
5091
5092        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
5093        for token in 0..tokens {
5094            let input_row =
5095                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
5096            for slot in 0..experts_per_token {
5097                let pair = token * experts_per_token + slot;
5098                let expert = selected[pair];
5099                if expert >= experts.bank.expert_count {
5100                    return Err(format!(
5101                        "TP selected expert {expert} outside 0..{}",
5102                        experts.bank.expert_count
5103                    )
5104                    .into());
5105                }
5106                let down = if self.native_p2p {
5107                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
5108                } else {
5109                    let gate =
5110                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
5111                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
5112                    let activated: Vec<f32> = gate
5113                        .iter()
5114                        .zip(&up)
5115                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5116                        .collect();
5117                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
5118                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
5119                };
5120                let weight = route_weights[pair];
5121                for (sum, value) in output
5122                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
5123                    .iter_mut()
5124                    .zip(down)
5125                {
5126                    *sum += weight * value;
5127                }
5128            }
5129        }
5130        Ok(output)
5131    }
5132
5133    fn run_column_bank_expert(
5134        &self,
5135        ranks: &[ResidentE4m3ExpertBankRank],
5136        expert: usize,
5137        input: &[f32],
5138    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5139        let local_out = ranks
5140            .first()
5141            .ok_or("TP column bank has no ranks")?
5142            .out_features;
5143        let mut gathered = vec![0.0f32; local_out * ranks.len()];
5144        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5145            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
5146            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
5147        }
5148        Ok(gathered)
5149    }
5150
5151    fn run_row_bank_expert(
5152        &self,
5153        ranks: &[ResidentE4m3ExpertBankRank],
5154        expert: usize,
5155        input: &[f32],
5156    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5157        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
5158        if input.len() != local_in * ranks.len() {
5159            return Err(format!(
5160                "TP row input {} != {} ranks x {local_in}",
5161                input.len(),
5162                ranks.len()
5163            )
5164            .into());
5165        }
5166        let out_features = ranks[0].out_features;
5167        let mut reduced = vec![0.0f32; out_features];
5168        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5169            let blocks = bank
5170                .k_blocks
5171                .ok_or("TP row bank is not packed in native K-block order")?;
5172            if blocks * FP8_BLOCK != local_in {
5173                return Err(format!(
5174                    "TP row bank has {blocks} blocks but local input width is {local_in}"
5175                )
5176                .into());
5177            }
5178            for block in 0..blocks {
5179                let global_start = rank * local_in + block * FP8_BLOCK;
5180                let partial = run_resident_bank_expert_block(
5181                    engine,
5182                    bank,
5183                    expert,
5184                    block,
5185                    &input[global_start..global_start + FP8_BLOCK],
5186                )?;
5187                for (sum, value) in reduced.iter_mut().zip(partial) {
5188                    *sum += value;
5189                }
5190            }
5191        }
5192        Ok(reduced)
5193    }
5194
5195    fn run_tensor_parallel_expert_native(
5196        &self,
5197        bank: &ResidentTpExpertBank,
5198        expert: usize,
5199        input: &[f32],
5200    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5201        if !self.native_p2p || self.ranks.len() < 2 {
5202            return Err("native TP expert execution requires at least two P2P ranks".into());
5203        }
5204        let local_out = bank
5205            .gate
5206            .first()
5207            .ok_or("native TP gate bank has no ranks")?
5208            .out_features;
5209        if local_out * self.ranks.len() != bank.expert_width {
5210            return Err(format!(
5211                "native TP gate shards {}x{local_out} != expert width {}",
5212                self.ranks.len(),
5213                bank.expert_width
5214            )
5215            .into());
5216        }
5217
5218        // The caller's routed input is already host-canonical. Upload once on rank zero, then
5219        // broadcast over peer copies so no other rank receives a host-staged duplicate.
5220        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
5221        let root_input = {
5222            let root = &self.ranks[0];
5223            let _main = root.gpu.enter_main()?;
5224            root.htod(input)?
5225        };
5226        rank_inputs.push(root_input);
5227        for engine in &self.ranks[1..] {
5228            let peer_input = {
5229                let _main = engine.gpu.enter_main()?;
5230                let mut peer_input = engine.uninit(input.len())?;
5231                engine
5232                    .stream()
5233                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
5234                peer_input
5235            };
5236            rank_inputs.push(peer_input);
5237        }
5238
5239        let mut gate_shards = Vec::with_capacity(self.ranks.len());
5240        let mut up_shards = Vec::with_capacity(self.ranks.len());
5241        for rank in 0..self.ranks.len() {
5242            gate_shards.push(run_resident_bank_expert_device(
5243                &self.ranks[rank],
5244                &bank.gate[rank],
5245                expert,
5246                &rank_inputs[rank],
5247                1,
5248            )?);
5249            up_shards.push(run_resident_bank_expert_device(
5250                &self.ranks[rank],
5251                &bank.up[rank],
5252                expert,
5253                &rank_inputs[rank],
5254                1,
5255            )?);
5256        }
5257
5258        // Preserve the established canonical activation program for the first native transport
5259        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
5260        // executes on host. A later device-activation increment must earn its own exactness gate.
5261        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
5262        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
5263        let activated = gate
5264            .iter()
5265            .zip(&up)
5266            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5267            .collect::<Vec<_>>();
5268        debug_assert_eq!(activated.len(), bank.expert_width);
5269
5270        let root_activated = {
5271            let root = &self.ranks[0];
5272            let _main = root.gpu.enter_main()?;
5273            root.htod(&activated)?
5274        };
5275        let mut rank_activated = Vec::with_capacity(self.ranks.len());
5276        for (rank, engine) in self.ranks.iter().enumerate() {
5277            let start = rank * local_out;
5278            let source = root_activated.slice(start..start + local_out);
5279            let local = {
5280                let _main = engine.gpu.enter_main()?;
5281                let mut local = engine.uninit(local_out)?;
5282                engine.stream().memcpy_dtod(&source, &mut local)?;
5283                local
5284            };
5285            rank_activated.push(local);
5286        }
5287
5288        let out_features = bank
5289            .down
5290            .first()
5291            .ok_or("native TP down bank has no ranks")?
5292            .out_features;
5293        let mut reduced = {
5294            let root = &self.ranks[0];
5295            let _main = root.gpu.enter_main()?;
5296            root.htod(&vec![0.0f32; out_features])?
5297        };
5298        let mut remote_partial_keepalive = Vec::new();
5299        for rank in 0..self.ranks.len() {
5300            let down = &bank.down[rank];
5301            let blocks = down
5302                .k_blocks
5303                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
5304            if blocks * FP8_BLOCK != local_out {
5305                return Err(format!(
5306                    "native TP rank {rank} has {blocks} blocks but local activation width is \
5307                     {local_out}"
5308                )
5309                .into());
5310            }
5311            for block in 0..blocks {
5312                let start = block * FP8_BLOCK;
5313                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
5314                let partial = run_resident_bank_expert_block_device(
5315                    &self.ranks[rank],
5316                    down,
5317                    expert,
5318                    block,
5319                    &input_block,
5320                )?;
5321                let root_partial = if rank == 0 {
5322                    partial
5323                } else {
5324                    let root = &self.ranks[0];
5325                    let _main = root.gpu.enter_main()?;
5326                    let mut peer_partial = root.uninit(out_features)?;
5327                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
5328                    remote_partial_keepalive.push(partial);
5329                    peer_partial
5330                };
5331                let next = {
5332                    let root = &self.ranks[0];
5333                    let _main = root.gpu.enter_main()?;
5334                    let mut next = root.uninit(out_features)?;
5335                    root.add(&reduced, &root_partial, &mut next, out_features)?;
5336                    next
5337                };
5338                reduced = next;
5339            }
5340        }
5341        let output = {
5342            let root = &self.ranks[0];
5343            let _main = root.gpu.enter_main()?;
5344            root.dtoh(&reduced)?
5345        };
5346        drop(remote_partial_keepalive);
5347        Ok(output)
5348    }
5349
5350    /// Gather token-major rank-local columns into one canonical root-device matrix.
5351    pub fn gather_native_column_shards_device(
5352        &self,
5353        shards: &[CudaSlice<f32>],
5354        tokens: usize,
5355        local_out: usize,
5356    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5357        let shard_len = tokens
5358            .checked_mul(local_out)
5359            .ok_or("native TP gather shard size overflow")?;
5360        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
5361            return Err("native TP gather shard geometry mismatch".into());
5362        }
5363        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
5364        // the other ranks' streams; without fencing those producers the copy can read a partial
5365        // kernel output.
5366        for engine in &self.ranks[1..] {
5367            let _main = engine.gpu.enter_main()?;
5368            engine.stream().synchronize()?;
5369        }
5370        let root = &self.ranks[0];
5371        let _main = root.gpu.enter_main()?;
5372        let global_out = shards
5373            .len()
5374            .checked_mul(local_out)
5375            .ok_or("native TP gather output width overflow")?;
5376        let gathered_len = tokens
5377            .checked_mul(global_out)
5378            .ok_or("native TP gather output size overflow")?;
5379        let mut gathered = root.uninit(gathered_len)?;
5380        if self.bulk_p2p {
5381            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
5382            if shards.len() > 1 {
5383                let mut staging = root.uninit(shard_len)?;
5384                for (rank, shard) in shards.iter().enumerate().skip(1) {
5385                    root.stream().memcpy_dtod(shard, &mut staging)?;
5386                    root.place_rows_strided(
5387                        &staging,
5388                        &mut gathered,
5389                        local_out,
5390                        tokens,
5391                        global_out,
5392                        rank * local_out,
5393                    )?;
5394                }
5395            }
5396        } else {
5397            for token in 0..tokens {
5398                for (rank, shard) in shards.iter().enumerate() {
5399                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5400                    let start = token * global_out + rank * local_out;
5401                    let mut destination = gathered.slice_mut(start..start + local_out);
5402                    root.stream().memcpy_dtod(&source, &mut destination)?;
5403                }
5404            }
5405        }
5406        Ok(gathered)
5407    }
5408
5409    pub fn gather_native_column_shards(
5410        &self,
5411        shards: &[CudaSlice<f32>],
5412        tokens: usize,
5413        local_out: usize,
5414    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5415        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5416        let root = &self.ranks[0];
5417        let _main = root.gpu.enter_main()?;
5418        root.dtoh(&gathered)
5419    }
5420
5421    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5422        &self.decode_v2
5423    }
5424
5425    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5426    /// return the index of the matching one. Attention geometry varies across the trunk
5427    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5428    /// handful exist per model, never one per layer.
5429    ///
5430    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5431    /// holds per residency class, and only the mirror class has no per-call weight expansion
5432    /// to hide allocation churn behind.
5433    pub(crate) fn decode_v2_ensure(
5434        &self,
5435        e: &Engine,
5436        q_m: &ResidentBf16ColumnParallel,
5437        k_m: &ResidentBf16ColumnParallel,
5438        v_m: &ResidentBf16ColumnParallel,
5439        o_m: &ResidentStepBf16RowParallel,
5440        heads: usize,
5441    ) -> Result<usize, Box<dyn std::error::Error>> {
5442        if self.ranks.len() > 1 && !self.native_p2p {
5443            return Err("step TP decode v2 requires native P2P ranks".into());
5444        }
5445        let ranks = self.ranks.len();
5446        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5447        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5448        // traffic), so bf16 residency is accepted when that door is on.
5449        let fused_door = step_tp_qkv_fused_enabled()?;
5450        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5451            ResidentBf16Weight::F32(_) => true,
5452            ResidentBf16Weight::Bf16(_) => fused_door,
5453        };
5454        for matrix in [q_m, k_m, v_m] {
5455            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5456            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5457                return Err("step TP decode v2 QKV geometry mismatch".into());
5458            }
5459            for rank in &matrix.ranks {
5460                if !arm_ok(&rank.weight) {
5461                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5462                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5463                        .into());
5464                }
5465            }
5466        }
5467        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5468        for blocks in &o_m.ranks {
5469            for block in blocks {
5470                if !arm_ok(&block.weight) {
5471                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5472                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5473                        .into());
5474                }
5475            }
5476        }
5477        if v_m.out_features != k_m.out_features
5478            || o_m.in_features != q_m.out_features
5479            || heads == 0
5480            || heads % ranks != 0
5481        {
5482            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5483        }
5484        let local_q_dim = q_m.out_features / ranks;
5485        let local_kv_dim = k_m.out_features / ranks;
5486        let o_out = o_m.out_features;
5487        let o_block_cols = o_m.canonical_chunk_cols;
5488        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5489        if blocks_per_rank == 0
5490            || o_m
5491                .ranks
5492                .iter()
5493                .any(|blocks| blocks.len() != blocks_per_rank)
5494            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5495        {
5496            return Err("step TP decode v2 O canonical block grid mismatch".into());
5497        }
5498
5499        let mut guard = self
5500            .decode_v2
5501            .lock()
5502            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5503        if let Some(index) = guard.iter().position(|ws| {
5504            ws.local_q_dim == local_q_dim
5505                && ws.local_kv_dim == local_kv_dim
5506                && ws.heads == heads
5507                && ws.o_out == o_out
5508                && ws.o_block_cols == o_block_cols
5509                && ws.blocks_per_rank == blocks_per_rank
5510                && ws.e_device == e.ctx().ordinal()
5511                && ws.q.len() == ranks
5512        }) {
5513            return Ok(index);
5514        }
5515
5516        let mut q_raw = Vec::with_capacity(ranks);
5517        let mut k_raw = Vec::with_capacity(ranks);
5518        let mut v_raw = Vec::with_capacity(ranks);
5519        let mut q = Vec::with_capacity(ranks);
5520        let mut k = Vec::with_capacity(ranks);
5521        let mut pos = Vec::with_capacity(ranks);
5522        let mut gate = Vec::with_capacity(ranks);
5523        let mut attn_out = Vec::with_capacity(ranks);
5524        let mut gated = Vec::with_capacity(ranks);
5525        let mut fuse_ctr = Vec::with_capacity(ranks);
5526        let mut o_partials = Vec::with_capacity(ranks);
5527        let mut ev_rank = Vec::with_capacity(ranks);
5528        let direct_join = oproj_direct_on();
5529        for (rank, engine) in self.ranks.iter().enumerate() {
5530            let _main = engine.gpu.enter_main()?;
5531            q_raw.push(engine.uninit(local_q_dim)?);
5532            k_raw.push(engine.uninit(local_kv_dim)?);
5533            v_raw.push(engine.uninit(local_kv_dim)?);
5534            q.push(engine.uninit(local_q_dim)?);
5535            k.push(engine.uninit(local_kv_dim)?);
5536            pos.push(engine.htod_i32(&[0])?);
5537            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5538            gate.push(engine.uninit(heads / ranks)?);
5539            attn_out.push(engine.uninit(local_q_dim)?);
5540            gated.push(engine.uninit(local_q_dim)?);
5541            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5542            for _ in 0..blocks_per_rank {
5543                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5544                // stores land there over P2P (UVA) and no pull copy is needed.
5545                if direct_join && rank != 0 {
5546                    let root = &self.ranks[0];
5547                    let _root_main = root.gpu.enter_main()?;
5548                    rank_partials.push(root.uninit(o_out)?);
5549                } else {
5550                    rank_partials.push(engine.uninit(o_out)?);
5551                }
5552            }
5553            o_partials.push(rank_partials);
5554            ev_rank.push(engine.ctx().new_event(None)?);
5555        }
5556        let root = &self.ranks[0];
5557        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5558            let _main = root.gpu.enter_main()?;
5559            (
5560                root.uninit(o_out)?,
5561                root.uninit(o_out)?,
5562                root.uninit(o_out)?,
5563                root.htod(&vec![0.0f32; o_out])?,
5564                root.uninit(ranks * local_kv_dim)?,
5565                root.uninit(ranks * local_kv_dim)?,
5566                root.ctx().new_event(None)?,
5567                root.ctx().new_event(None)?,
5568            )
5569        };
5570        let (gate_e, ev_entry) = {
5571            let _main = e.gpu.enter_main()?;
5572            (e.uninit(heads)?, e.ctx().new_event(None)?)
5573        };
5574        let raw_attn_in = Vec::new();
5575        let raw_pos = Vec::new();
5576        guard.push(StepTpDecodeV2Ws {
5577            tcol_q: Vec::new(),
5578            tcol_k: Vec::new(),
5579            tcol_v: Vec::new(),
5580            tcol_g: Vec::new(),
5581            tcol_in: Vec::new(),
5582            tcol_cap: 0,
5583            w8_aq: Vec::new(),
5584            w8_ad: Vec::new(),
5585            w8_in: 0,
5586            w8o_aq: Vec::new(),
5587            w8o_ad: Vec::new(),
5588            w8o_in: 0,
5589            w8t_aq: Vec::new(),
5590            w8t_ad: Vec::new(),
5591            w8t_in: 0,
5592            w8t_oaq: Vec::new(),
5593            w8t_oad: Vec::new(),
5594            w8t_oin: 0,
5595            w8t_cap: 0,
5596            fa2_q: Vec::new(),
5597            fa2_gate: Vec::new(),
5598            fa2_gated: Vec::new(),
5599            fa2_cap: 0,
5600            rope_k_t: Vec::new(),
5601            rope_ctr_t: Vec::new(),
5602            rope_pos_t: Vec::new(),
5603            rows_tabs: Vec::new(),
5604            rows_tab_t: Vec::new(),
5605            rows_tab_shadow: Vec::new(),
5606            tcol_gated: Vec::new(),
5607            tcol_opart: Vec::new(),
5608            tcol_opeer: None,
5609            tcol_omix: None,
5610            tcol_ocap: 0,
5611            q_raw,
5612            k_raw,
5613            v_raw,
5614            q,
5615            k,
5616            pos,
5617            fuse_ctr,
5618            gate,
5619            attn_out,
5620            gated,
5621            o_partials,
5622            ev_rank,
5623            peer_partial,
5624            reduce_a,
5625            reduce_b,
5626            zeros,
5627            k_shadow,
5628            v_shadow,
5629            ev_refresh,
5630            ev_oproj,
5631            gate_e,
5632            attn_in: Vec::new(),
5633            h_stage: None,
5634            pos_stage: None,
5635            raw_h_stage: 0,
5636            raw_pos_stage: 0,
5637            raw_attn_in,
5638            raw_pos,
5639            raw_o_partial1: 0,
5640            raw_peer_partial: 0,
5641            raw_k1: 0,
5642            raw_v1: 0,
5643            raw_k_shadow: 0,
5644            raw_v_shadow: 0,
5645            raw_mixed_stage_e: 0,
5646            raw_reduce_a: 0,
5647            raw_shadow_stage_e: (0, 0),
5648            ev_entry,
5649            e_device: e.ctx().ordinal(),
5650            local_q_dim,
5651            local_kv_dim,
5652            heads,
5653            o_out,
5654            o_block_cols,
5655            blocks_per_rank,
5656        });
5657        eprintln!(
5658            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5659             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5660             residency=persistent ordering=evented performance_claim=false"
5661        );
5662        Ok(guard.len() - 1)
5663    }
5664
5665    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5666    /// all into the persistent workspace, ordered by events instead of host syncs.
5667    ///
5668    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5669    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5670    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5671    /// previous layer's outputs was queued on `e`'s stream before this record).
5672    #[allow(clippy::too_many_arguments)]
5673    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5674    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5675    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5676    /// column vs the t=1 kernel by construction.
5677    #[allow(clippy::too_many_arguments)]
5678    pub fn decode_v2_input_qkv_tcol(
5679        &self,
5680        ws_index: usize,
5681        e: &Engine,
5682        h_t: &CudaSlice<f32>,
5683        t: usize,
5684        q_m: &ResidentBf16ColumnParallel,
5685        k_m: &ResidentBf16ColumnParallel,
5686        v_m: &ResidentBf16ColumnParallel,
5687        gate_shards: Option<StepTpGateShards<'_>>,
5688    ) -> Result<(), Box<dyn std::error::Error>> {
5689        let ranks = self.ranks.len();
5690        let mut guard = self
5691            .decode_v2
5692            .lock()
5693            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5694        let ws = guard
5695            .get_mut(ws_index)
5696            .ok_or("step TP decode v2 workspace index out of range")?;
5697        let in_f = q_m.in_features;
5698        if h_t.len() < t * in_f || t == 0 || t > 32 {
5699            return Err("decode_v2_input_qkv_tcol geometry".into());
5700        }
5701        // Lazily arm the slabs to capacity.
5702        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5703            ws.tcol_q.clear();
5704            ws.tcol_k.clear();
5705            ws.tcol_v.clear();
5706            ws.tcol_g.clear();
5707            ws.tcol_in.clear();
5708            for engine in &self.ranks {
5709                let _m = engine.gpu.enter_main()?;
5710                ws.tcol_q.push(engine.uninit(32 * ws.local_q_dim)?);
5711                ws.tcol_k.push(engine.uninit(32 * ws.local_kv_dim)?);
5712                ws.tcol_v.push(engine.uninit(32 * ws.local_kv_dim)?);
5713                ws.tcol_g
5714                    .push(engine.uninit(32 * (ws.heads / ranks).max(1))?);
5715                ws.tcol_in.push(engine.uninit(32 * in_f)?);
5716            }
5717            ws.tcol_cap = 32;
5718        }
5719        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5720        use cudarc::driver::DevicePtr;
5721        let raw_src = {
5722            let _main = e.gpu.enter_main()?;
5723            let stream = e.stream();
5724            let (p, _g) = h_t.device_ptr(&stream);
5725            ws.ev_entry.record(&stream)?;
5726            p as u64
5727        };
5728        for rank in 0..ranks {
5729            let engine = &self.ranks[rank];
5730            let _main = engine.gpu.enter_main()?;
5731            engine.stream().wait(&ws.ev_entry)?;
5732            let raw_dst = {
5733                let stream = engine.stream();
5734                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5735                p as u64
5736            };
5737            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5738            let out_g = match &gate_shards {
5739                Some(_) => ws.heads / ranks,
5740                None => 0,
5741            };
5742            match (
5743                &q_m.ranks[rank].weight,
5744                &k_m.ranks[rank].weight,
5745                &v_m.ranks[rank].weight,
5746            ) {
5747                (
5748                    ResidentBf16Weight::Bf16(wq),
5749                    ResidentBf16Weight::Bf16(wk),
5750                    ResidentBf16Weight::Bf16(wv),
5751                ) => {
5752                    let wg = match &gate_shards {
5753                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5754                        Some(StepTpGateShards::F32(_)) => {
5755                            return Err(
5756                                "tcol verify: gate shard class does not match bf16 QKV".into()
5757                            );
5758                        }
5759                        None => wq,
5760                    };
5761                    let StepTpDecodeV2Ws {
5762                        tcol_q,
5763                        tcol_k,
5764                        tcol_v,
5765                        tcol_g,
5766                        tcol_in,
5767                        local_q_dim,
5768                        local_kv_dim,
5769                        w8t_aq,
5770                        w8t_ad,
5771                        w8t_in,
5772                        w8t_cap,
5773                        ..
5774                    } = &mut *ws;
5775                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5776                    // column — separates driver bugs from tcol-kernel bugs.
5777                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5778                    let refk = *REFK
5779                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5780                    if refk {
5781                        let lq = *local_q_dim;
5782                        let lkv = *local_kv_dim;
5783                        let mut hrow = engine.uninit(in_f)?;
5784                        let mut qr = engine.uninit(lq)?;
5785                        let mut kr = engine.uninit(lkv)?;
5786                        let mut vr = engine.uninit(lkv)?;
5787                        let mut gr = engine.uninit(out_g.max(1))?;
5788                        for c in 0..t {
5789                            {
5790                                let mut dst = hrow.slice_mut(0..in_f);
5791                                engine.stream().memcpy_dtod(
5792                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5793                                    &mut dst,
5794                                )?;
5795                            }
5796                            engine.matvec_bf16_qkvg_into(
5797                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5798                                lq, lkv, out_g,
5799                            )?;
5800                            let stream = engine.stream();
5801                            {
5802                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5803                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5804                            }
5805                            {
5806                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5807                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5808                            }
5809                            {
5810                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5811                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5812                            }
5813                            if out_g > 0 {
5814                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5815                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5816                            }
5817                        }
5818                    } else if crate::step_tp_w8_on()
5819                        && q_m.ranks[rank].q8.is_some()
5820                        && k_m.ranks[rank].q8.is_some()
5821                        && v_m.ranks[rank].q8.is_some()
5822                        && in_f % 32 == 0
5823                    {
5824                        // MEMRA_STEP_TP_W8 on the VERIFY walk. nsys put the bf16 tcol QKV at
5825                        // 12.3% of spec GPU time and the bf16 tcol o_proj at 24.8% — the door
5826                        // had only ever replaced the DECODE kernels, so 37% of the verify still
5827                        // streamed bf16 weights. One q8 launch over all t columns; the gate rows
5828                        // stay bf16 as on the decode side.
5829                        if *w8t_in != in_f || *w8t_cap < t || w8t_aq.len() != ranks {
5830                            w8t_aq.clear();
5831                            w8t_ad.clear();
5832                            for e_rank in &self.ranks {
5833                                let _m = e_rank.gpu.enter_main()?;
5834                                w8t_aq.push(e_rank.alloc_i8_uninit(32 * in_f)?);
5835                                w8t_ad.push(e_rank.alloc_uninit::<f32>(32 * (in_f / 32))?);
5836                            }
5837                            *w8t_in = in_f;
5838                            *w8t_cap = 32;
5839                        }
5840                        engine.quantize_q8_1_into(
5841                            &tcol_in[rank],
5842                            t,
5843                            in_f,
5844                            &mut w8t_aq[rank],
5845                            &mut w8t_ad[rank],
5846                        )?;
5847                        engine.qmatvec_q8_0_qkv_rp_t_into(
5848                            q_m.ranks[rank].q8.as_ref().unwrap(),
5849                            k_m.ranks[rank].q8.as_ref().unwrap(),
5850                            v_m.ranks[rank].q8.as_ref().unwrap(),
5851                            &w8t_aq[rank],
5852                            &w8t_ad[rank],
5853                            &mut tcol_q[rank],
5854                            &mut tcol_k[rank],
5855                            &mut tcol_v[rank],
5856                            in_f,
5857                            *local_q_dim,
5858                            *local_kv_dim,
5859                            t,
5860                        )?;
5861                        if out_g > 0 {
5862                            engine.matvec_bf16_rows_into(
5863                                wg,
5864                                &tcol_in[rank],
5865                                &mut tcol_g[rank],
5866                                in_f,
5867                                out_g,
5868                                t,
5869                            )?;
5870                        }
5871                    } else {
5872                        engine.matvec_bf16_qkvg_tcol_into(
5873                            wq,
5874                            wk,
5875                            wv,
5876                            wg,
5877                            &tcol_in[rank],
5878                            &mut tcol_q[rank],
5879                            &mut tcol_k[rank],
5880                            &mut tcol_v[rank],
5881                            &mut tcol_g[rank],
5882                            in_f,
5883                            *local_q_dim,
5884                            *local_kv_dim,
5885                            out_g,
5886                            t,
5887                        )?;
5888                    }
5889                }
5890                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5891            }
5892        }
5893        Ok(())
5894    }
5895
5896    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5897    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5898    /// skipped — so it requires the same doors that arm dictate that finish shape.
5899    pub(crate) fn decode_v2_oproj_tcol_eligible(
5900        &self,
5901        ws: &StepTpDecodeV2Ws,
5902        o_m: &ResidentStepBf16RowParallel,
5903    ) -> bool {
5904        self.ranks.len() == 2
5905            && ws.blocks_per_rank == 4
5906            && step_tp_qkv_fused_enabled().unwrap_or(false)
5907            && no_local_shadow_on()
5908            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5909            && o_m
5910                .ranks
5911                .iter()
5912                .flatten()
5913                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5914    }
5915
5916    /// MEMRA_SPEC_FA2 stash: copy this column's per-rank post-rope q and gate rows into
5917    /// the fa2 slabs (rank-stream ordered behind the rope/append that produced them), and
5918    /// give `e` the same anti-dependency wait the skipped finish provided (next column's
5919    /// h/pos re-staging must not overtake this column's rank pulls).
5920    pub(crate) fn decode_v2_stash_fa2(
5921        &self,
5922        ws: &mut StepTpDecodeV2Ws,
5923        e: &Engine,
5924        col: usize,
5925    ) -> Result<(), Box<dyn std::error::Error>> {
5926        let ranks = self.ranks.len();
5927        if col >= 32 {
5928            return Err("decode_v2_stash_fa2 column out of range".into());
5929        }
5930        let lq = ws.local_q_dim;
5931        let lg = (ws.heads / ranks).max(1);
5932        if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks || ws.rows_tab_t.len() != ranks {
5933            ws.fa2_q.clear();
5934            ws.fa2_gate.clear();
5935            ws.fa2_gated.clear();
5936            ws.rope_k_t.clear();
5937            ws.rope_ctr_t.clear();
5938            ws.rope_pos_t.clear();
5939            ws.rows_tab_t.clear();
5940            for engine in &self.ranks {
5941                let _m = engine.gpu.enter_main()?;
5942                ws.fa2_q.push(engine.uninit(32 * lq)?);
5943                ws.fa2_gate.push(engine.uninit(32 * lg)?);
5944                ws.fa2_gated.push(engine.uninit(32 * lq)?);
5945                ws.rope_k_t.push(engine.uninit(32 * ws.local_kv_dim)?);
5946                ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
5947                ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
5948                ws.rows_tab_t
5949                    .push(engine.stream().clone_htod(&[0u64; 32 * 6])?);
5950            }
5951            ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
5952            ws.fa2_cap = 32;
5953        }
5954        for rank in 0..ranks {
5955            let engine = &self.ranks[rank];
5956            let _main = engine.gpu.enter_main()?;
5957            {
5958                let mut dst = ws.fa2_q[rank].slice_mut(col * lq..(col + 1) * lq);
5959                engine
5960                    .stream()
5961                    .memcpy_dtod(&ws.q[rank].slice(0..lq), &mut dst)?;
5962            }
5963            {
5964                let mut dst = ws.fa2_gate[rank].slice_mut(col * lg..(col + 1) * lg);
5965                engine
5966                    .stream()
5967                    .memcpy_dtod(&ws.gate[rank].slice(0..lg), &mut dst)?;
5968            }
5969            ws.ev_rank[rank].record(&engine.stream())?;
5970        }
5971        {
5972            let _main = e.gpu.enter_main()?;
5973            for ev in ws.ev_rank.iter() {
5974                e.stream().wait(ev)?;
5975            }
5976        }
5977        Ok(())
5978    }
5979
5980    /// MEMRA_SPEC_FA2 join: after BOTH verify columns stashed (their appends landed in
5981    /// rank-stream order), run ONE fa_decode_dcw2 per rank over the shared KV stream —
5982    /// two query rows, per-row causal bounds, per-row combine+gate — then land the two
5983    /// gated rows in the o-tcol slabs and reuse the weight-amortized o_proj join.
5984    /// Returns the [2, o_out] `mixed` slab on `e`. The caller's precheck enforced the
5985    /// equal-partition guard (boundary rounds never arm the defer).
5986    #[allow(clippy::too_many_arguments)]
5987    pub(crate) fn decode_v2_spec_fa2_join(
5988        &self,
5989        ws_index: usize,
5990        e: &Engine,
5991        o_m: &ResidentStepBf16RowParallel,
5992        kv: &ResidentTpKvCache,
5993        head_dim: usize,
5994        window: usize,
5995        bucket_max: usize,
5996        scale: f32,
5997    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5998        let ranks = self.ranks.len();
5999        // Engagement receipt: a vacuous gate (precheck never passing) must be visible.
6000        static ONCE: std::sync::Once = std::sync::Once::new();
6001        ONCE.call_once(|| eprintln!("[spec-fa2] joined T=2 attention ENGAGED"));
6002        {
6003            let mut guard = self
6004                .decode_v2
6005                .lock()
6006                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6007            let ws = guard
6008                .get_mut(ws_index)
6009                .ok_or("step TP decode v2 workspace index out of range")?;
6010            if ws.fa2_cap < 2 || ws.fa2_q.len() != ranks {
6011                return Err("spec fa2 join without stashed columns".into());
6012            }
6013            let lq = ws.local_q_dim;
6014            let local_heads = (ws.heads / ranks).max(1);
6015            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6016            let capacity = kv.physical_capacity();
6017            let (k_tok_bytes, v_tok_bytes) = (kv.k_tok_bytes(), kv.v_tok_bytes());
6018            // Arm the o-tcol slabs if the oproj door never ran this boot (same shapes).
6019            if ws.tcol_ocap < 2 || ws.tcol_gated.len() != ranks {
6020                ws.tcol_gated.clear();
6021                ws.tcol_opart.clear();
6022                for engine in &self.ranks {
6023                    let _m = engine.gpu.enter_main()?;
6024                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6025                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6026                }
6027                let root = &self.ranks[0];
6028                let _m = root.gpu.enter_main()?;
6029                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6030                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6031                ws.tcol_ocap = 32;
6032            }
6033            for rank in 0..ranks {
6034                let engine = &self.ranks[rank];
6035                let _main = engine.gpu.enter_main()?;
6036                let rank_cache = kv
6037                    .rank(rank)
6038                    .ok_or("spec fa2 join lost its KV cache rank")?;
6039                let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes);
6040                let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes);
6041                {
6042                    let StepTpDecodeV2Ws {
6043                        fa2_q,
6044                        fa2_gate,
6045                        fa2_gated,
6046                        ..
6047                    } = &mut *ws;
6048                    engine.fa_decode_dcw2(
6049                        &fa2_q[rank],
6050                        &k_ring,
6051                        &v_ring,
6052                        &mut fa2_gated[rank],
6053                        head_dim,
6054                        local_heads,
6055                        local_kv_heads,
6056                        rank_cache.len_d(),
6057                        rank_cache.base_d(),
6058                        window,
6059                        bucket_max,
6060                        scale,
6061                        k_tok_bytes,
6062                        v_tok_bytes,
6063                        &fa2_gate[rank],
6064                    )?;
6065                }
6066                // Both gated rows are contiguous [2, lq] — exactly columns 0..2 of the
6067                // o-tcol slab layout. One dtod, in rank-stream order behind the fa.
6068                let StepTpDecodeV2Ws {
6069                    fa2_gated,
6070                    tcol_gated,
6071                    ..
6072                } = &mut *ws;
6073                let mut dst = tcol_gated[rank].slice_mut(0..2 * lq);
6074                engine
6075                    .stream()
6076                    .memcpy_dtod(&fa2_gated[rank].slice(0..2 * lq), &mut dst)?;
6077            }
6078        }
6079        self.decode_v2_oproj_tcol(ws_index, e, o_m, 2)
6080    }
6081
6082    /// FULL T-ROW ATTENTION PASS over per-row session tables (batched serving): reads
6083    /// the tcol raw-projection slabs, runs ONE rope/append rows launch + ONE fa rows
6084    /// launch + ONE combine per rank (gate straight from the tcol gate slab), then the
6085    /// o_proj tcol join — the whole per-row attention loop in 3 launches/rank/layer.
6086    /// Per-(row, head) programs are the t=1 kernels verbatim; each row appends to and
6087    /// attends its OWN session. `session_parts[rank][row]` = {k_plane, v_plane, len_ptr,
6088    /// base_ptr}; `tab_keys[rank]` keys the per-rank combined-table cache (caller folds
6089    /// layer + session-set + base-arming into it); `stage_pos` stages the position slab
6090    /// (positions are constant across layers within a tick — stage on the first layer).
6091    #[allow(clippy::too_many_arguments)]
6092    pub(crate) fn decode_v2_rope_fa_rows(
6093        &self,
6094        ws_index: usize,
6095        e: &Engine,
6096        o_m: &ResidentStepBf16RowParallel,
6097        session_parts: &[Vec<[u64; 4]>],
6098        tab_keys: &[u64],
6099        positions: &[i32],
6100        stage_pos: bool,
6101        same_session: bool,
6102        q_norms: &[CudaSlice<f32>],
6103        k_norms: &[CudaSlice<f32>],
6104        rope_freqs: &[Option<&crate::CudaSlice<f32>>],
6105        t: usize,
6106        head_dim: usize,
6107        n_rot: usize,
6108        window: usize,
6109        max_ns: usize,
6110        scale: f32,
6111        k_tok_bytes: usize,
6112        v_tok_bytes: usize,
6113        eps: f32,
6114        rope_base: f32,
6115    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6116        use cudarc::driver::DevicePtr;
6117        let ranks = self.ranks.len();
6118        if session_parts.len() != ranks || tab_keys.len() != ranks || positions.len() < t {
6119            return Err("rope fa rows geometry".into());
6120        }
6121        {
6122            let mut guard = self
6123                .decode_v2
6124                .lock()
6125                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6126            let ws = guard
6127                .get_mut(ws_index)
6128                .ok_or("step TP decode v2 workspace index out of range")?;
6129            if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
6130                return Err("rope fa rows without tcol slabs".into());
6131            }
6132            let lq = ws.local_q_dim;
6133            let lkv = ws.local_kv_dim;
6134            let lg = (ws.heads / ranks).max(1);
6135            let local_heads = (ws.heads / ranks).max(1);
6136            let local_kv_heads = (lkv / head_dim).max(1);
6137            // Arm the fa2/rope slabs (shared with the stash path).
6138            if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks || ws.rows_tab_t.len() != ranks {
6139                ws.fa2_q.clear();
6140                ws.fa2_gate.clear();
6141                ws.fa2_gated.clear();
6142                ws.rope_k_t.clear();
6143                ws.rope_ctr_t.clear();
6144                ws.rope_pos_t.clear();
6145                ws.rows_tab_t.clear();
6146                for engine in &self.ranks {
6147                    let _m = engine.gpu.enter_main()?;
6148                    ws.fa2_q.push(engine.uninit(32 * lq)?);
6149                    ws.fa2_gate.push(engine.uninit(32 * lg)?);
6150                    ws.fa2_gated.push(engine.uninit(32 * lq)?);
6151                    ws.rope_k_t.push(engine.uninit(32 * lkv)?);
6152                    ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
6153                    ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
6154                    ws.rows_tab_t
6155                        .push(engine.stream().clone_htod(&[0u64; 32 * 6])?);
6156                }
6157                ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
6158                ws.fa2_cap = 32;
6159            }
6160            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6161                ws.tcol_gated.clear();
6162                ws.tcol_opart.clear();
6163                for engine in &self.ranks {
6164                    let _m = engine.gpu.enter_main()?;
6165                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6166                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6167                }
6168                let root = &self.ranks[0];
6169                let _m = root.gpu.enter_main()?;
6170                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6171                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6172                ws.tcol_ocap = 32;
6173            }
6174            for rank in 0..ranks {
6175                let engine = &self.ranks[rank];
6176                let _main = engine.gpu.enter_main()?;
6177                if stage_pos {
6178                    let host: Vec<i32> = positions[..t].to_vec();
6179                    let mut view = ws.rope_pos_t[rank].slice_mut(0..t);
6180                    engine.stream().memcpy_htod(&host, &mut view)?;
6181                }
6182                // Combined 6-word table {k, v, len, base, ctr, back}; ctr = this rank's
6183                // per-row counter slab. Built from the pointers the CALLER just read off
6184                // the live distributed cache, and RESTAGED into a persistent slab before
6185                // every launch (MEMRA_ROWS_TAB_RESTAGE, default ON).
6186                //
6187                // The `rows_tabs` memo this replaces was keyed by a hash of
6188                // (k pointer, base pointer, layer, t) but the table it handed back ALSO
6189                // carried the V and LEN pointers, and nothing invalidated it when a
6190                // session's KV cache was dropped. A later session whose K buffer landed on
6191                // a recycled address therefore hit a dead entry, and
6192                // `qk_norm_rope_append_inc_dcw_rows` WROTE this session's K/V rows through
6193                // the freed V/len pointers it still held while `fa_decode_dcw_rows` read
6194                // them back: a whole non-finite row when the freed pages were re-mapped,
6195                // CUDA_ERROR_ILLEGAL_ADDRESS when they were not. The row-table twin in
6196                // `step35_verify_fa_rows_join` was cured of exactly this in 8c8397e0b2
6197                // ("a process-lifetime map cannot prove allocation generation", Hermes
6198                // `11339f5cd3c132a3`); this fused rope+append+fa path was left out of it,
6199                // and MEMRA_FUSE_ROPE_APPEND=1 makes it the arm that actually runs.
6200                let ctr_base = {
6201                    let s = engine.stream();
6202                    let (p, _g) = ws.rope_ctr_t[rank].device_ptr(&s);
6203                    p as u64
6204                };
6205                let host = rows_tab_host(&session_parts[rank], ctr_base, same_session, t);
6206                // STALE-HIT RECEIPT (MEMRA_ROWS_TAB_STALE_SCAN=1, default OFF): replay the
6207                // retired key against the contents we are about to stage. `engaged` proves
6208                // this path executes at all; `STALE` proves the retired memo would have
6209                // handed a live launch another allocation's pointers, and names which word
6210                // moved. Diagnostic only: it never feeds a kernel.
6211                if rows_tab_stale_scan() {
6212                    if ws.rows_tab_shadow.len() != ranks {
6213                        ws.rows_tab_shadow = (0..ranks).map(|_| Default::default()).collect();
6214                    }
6215                    let n = ROWS_TAB_ENGAGED.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
6216                    if let Some(prev) = ws.rows_tab_shadow[rank].get(&tab_keys[rank]) {
6217                        if prev != &host {
6218                            let words = ["k", "v", "len", "base", "ctr", "back"];
6219                            let moved: Vec<String> = (0..host.len())
6220                                .filter(|&i| prev.get(i) != Some(&host[i]))
6221                                .map(|i| format!("{}[row{}]", words[i % 6], i / 6))
6222                                .collect();
6223                            let stale =
6224                                ROWS_TAB_STALE.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
6225                            eprintln!(
6226                                "[rows-tab] STALE #{stale} lookup #{n} rank={rank} t={t} key={:#018x} moved={}: the retired memo would have launched this row on another allocation's pointers",
6227                                tab_keys[rank],
6228                                moved.join(",")
6229                            );
6230                        }
6231                    }
6232                    ws.rows_tab_shadow[rank].insert(tab_keys[rank], host.clone());
6233                }
6234                let legacy_memo = !rows_tab_restage_on();
6235                if legacy_memo && !ws.rows_tabs[rank].contains_key(&tab_keys[rank]) {
6236                    let tab = engine.stream().clone_htod(&host)?;
6237                    ws.rows_tabs[rank].insert(tab_keys[rank], tab);
6238                }
6239                if !legacy_memo {
6240                    let mut view = ws.rows_tab_t[rank].slice_mut(0..t * 6);
6241                    engine.stream().memcpy_htod(&host, &mut view)?;
6242                }
6243                let StepTpDecodeV2Ws {
6244                    tcol_q,
6245                    tcol_k,
6246                    tcol_v,
6247                    tcol_g,
6248                    fa2_q,
6249                    fa2_gated,
6250                    rope_k_t,
6251                    rope_pos_t,
6252                    rows_tabs,
6253                    rows_tab_t,
6254                    ..
6255                } = &mut *ws;
6256                let tab = if legacy_memo {
6257                    rows_tabs[rank]
6258                        .get(&tab_keys[rank])
6259                        .ok_or("rows tab memo lost its entry")?
6260                } else {
6261                    &rows_tab_t[rank]
6262                };
6263                engine.qk_norm_rope_append_inc_dcw_rows(
6264                    &tcol_q[rank],
6265                    &tcol_k[rank],
6266                    &tcol_v[rank],
6267                    &q_norms[rank],
6268                    &k_norms[rank],
6269                    &mut fa2_q[rank],
6270                    &mut rope_k_t[rank],
6271                    tab,
6272                    &rope_pos_t[rank],
6273                    same_session,
6274                    t,
6275                    lkv,
6276                    lkv,
6277                    k_tok_bytes,
6278                    v_tok_bytes,
6279                    head_dim,
6280                    n_rot,
6281                    local_heads,
6282                    local_kv_heads,
6283                    eps,
6284                    rope_base,
6285                    1.0,
6286                    rope_freqs[rank],
6287                )?;
6288                engine.fa_decode_dcw_rows(
6289                    &fa2_q[rank],
6290                    tab,
6291                    &mut fa2_gated[rank],
6292                    t,
6293                    head_dim,
6294                    local_heads,
6295                    local_kv_heads,
6296                    window,
6297                    max_ns,
6298                    scale,
6299                    k_tok_bytes,
6300                    v_tok_bytes,
6301                    &tcol_g[rank],
6302                )?;
6303                let StepTpDecodeV2Ws {
6304                    fa2_gated,
6305                    tcol_gated,
6306                    ..
6307                } = &mut *ws;
6308                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6309                engine
6310                    .stream()
6311                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6312            }
6313        }
6314        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6315    }
6316
6317    /// T-ROW fa join over per-row session tables (the per-session distributed-KV
6318    /// primitive): after all t rows stashed q+gate (their appends landed in rank-stream
6319    /// order), ONE fa_decode_dcw_rows per rank walks every row's own ring with its own
6320    /// geometry — bit-identical per row to its per-row launch — then the o_proj tcol
6321    /// join lands the [t, o_out] `mixed` slab on `e`. `tabs[rank]` is the pre-staged
6322    /// device table on that rank.
6323    #[allow(clippy::too_many_arguments)]
6324    pub(crate) fn decode_v2_fa_rows_join(
6325        &self,
6326        ws_index: usize,
6327        e: &Engine,
6328        o_m: &ResidentStepBf16RowParallel,
6329        tabs: &[&crate::CudaSlice<u64>],
6330        t: usize,
6331        head_dim: usize,
6332        window: usize,
6333        max_ns: usize,
6334        scale: f32,
6335        k_tok_bytes: usize,
6336        v_tok_bytes: usize,
6337    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6338        let ranks = self.ranks.len();
6339        if tabs.len() != ranks {
6340            return Err("fa rows join needs one table per rank".into());
6341        }
6342        {
6343            let mut guard = self
6344                .decode_v2
6345                .lock()
6346                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6347            let ws = guard
6348                .get_mut(ws_index)
6349                .ok_or("step TP decode v2 workspace index out of range")?;
6350            if ws.fa2_cap < t || ws.fa2_q.len() != ranks {
6351                return Err("fa rows join without stashed rows".into());
6352            }
6353            let lq = ws.local_q_dim;
6354            let local_heads = (ws.heads / ranks).max(1);
6355            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6356            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6357                ws.tcol_gated.clear();
6358                ws.tcol_opart.clear();
6359                for engine in &self.ranks {
6360                    let _m = engine.gpu.enter_main()?;
6361                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6362                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6363                }
6364                let root = &self.ranks[0];
6365                let _m = root.gpu.enter_main()?;
6366                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6367                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6368                ws.tcol_ocap = 32;
6369            }
6370            for rank in 0..ranks {
6371                let engine = &self.ranks[rank];
6372                let _main = engine.gpu.enter_main()?;
6373                {
6374                    let StepTpDecodeV2Ws {
6375                        fa2_q,
6376                        fa2_gate,
6377                        fa2_gated,
6378                        ..
6379                    } = &mut *ws;
6380                    engine.fa_decode_dcw_rows(
6381                        &fa2_q[rank],
6382                        tabs[rank],
6383                        &mut fa2_gated[rank],
6384                        t,
6385                        head_dim,
6386                        local_heads,
6387                        local_kv_heads,
6388                        window,
6389                        max_ns,
6390                        scale,
6391                        k_tok_bytes,
6392                        v_tok_bytes,
6393                        &fa2_gate[rank],
6394                    )?;
6395                }
6396                let StepTpDecodeV2Ws {
6397                    fa2_gated,
6398                    tcol_gated,
6399                    ..
6400                } = &mut *ws;
6401                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6402                engine
6403                    .stream()
6404                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6405            }
6406        }
6407        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6408    }
6409
6410    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
6411    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
6412    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
6413    /// every column afterwards.
6414    pub(crate) fn decode_v2_stash_gated(
6415        &self,
6416        ws: &mut StepTpDecodeV2Ws,
6417        e: &Engine,
6418        col: usize,
6419    ) -> Result<(), Box<dyn std::error::Error>> {
6420        let ranks = self.ranks.len();
6421        // 32, not 8: the slabs below have been 32 rows since the slab-width fix, and the walk now
6422        // runs chunks up to t=32 (the w=16 arm died here on a guard three widths staler than its
6423        // own allocation, 2026-08-27).
6424        if col >= 32 {
6425            return Err("decode_v2_stash_gated column out of range".into());
6426        }
6427        let lq = ws.local_q_dim;
6428        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
6429            ws.tcol_gated.clear();
6430            ws.tcol_opart.clear();
6431            for engine in &self.ranks {
6432                let _m = engine.gpu.enter_main()?;
6433                ws.tcol_gated.push(engine.uninit(32 * lq)?);
6434                ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6435            }
6436            let root = &self.ranks[0];
6437            let _m = root.gpu.enter_main()?;
6438            ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6439            ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6440            ws.tcol_ocap = 32;
6441        }
6442        for rank in 0..ranks {
6443            let engine = &self.ranks[rank];
6444            let _main = engine.gpu.enter_main()?;
6445            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
6446            engine
6447                .stream()
6448                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
6449            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
6450            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
6451            // Record each rank here and make e wait — same protection, no o_proj work.
6452            ws.ev_rank[rank].record(&engine.stream())?;
6453        }
6454        {
6455            let _main = e.gpu.enter_main()?;
6456            for ev in ws.ev_rank.iter() {
6457                e.stream().wait(ev)?;
6458            }
6459        }
6460        Ok(())
6461    }
6462
6463    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
6464    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
6465    /// partial slab, one elementwise slab add on the root (independent elements — each
6466    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
6467    /// lands on `e`. Returns [t, o_out] on the model engine.
6468    pub(crate) fn decode_v2_oproj_tcol(
6469        &self,
6470        ws_index: usize,
6471        e: &Engine,
6472        o_m: &ResidentStepBf16RowParallel,
6473        t: usize,
6474    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6475        let ranks = self.ranks.len();
6476        let mut guard = self
6477            .decode_v2
6478            .lock()
6479            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6480        let ws = guard
6481            .get_mut(ws_index)
6482            .ok_or("step TP decode v2 workspace index out of range")?;
6483        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 32 || ws.tcol_ocap < t {
6484            return Err("decode_v2_oproj_tcol geometry".into());
6485        }
6486        for rank in 0..ranks {
6487            let engine = &self.ranks[rank];
6488            let _main = engine.gpu.enter_main()?;
6489            let mut weights = Vec::with_capacity(4);
6490            for block in 0..4 {
6491                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6492                    return Err("tcol o_proj requires bf16-resident O blocks".into());
6493                };
6494                weights.push(weight);
6495            }
6496            {
6497                let StepTpDecodeV2Ws {
6498                    tcol_gated,
6499                    tcol_opart,
6500                    local_q_dim,
6501                    o_block_cols,
6502                    o_out,
6503                    w8t_oaq,
6504                    w8t_oad,
6505                    w8t_oin,
6506                    w8t_cap,
6507                    ..
6508                } = &mut *ws;
6509                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
6510                // kernel per column — separates choreography bugs from tcol-kernel bugs.
6511                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6512                let refk = *REFK
6513                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
6514                if refk {
6515                    let lq = *local_q_dim;
6516                    let mut xr = engine.uninit(lq)?;
6517                    let mut yr = engine.uninit(*o_out)?;
6518                    for c in 0..t {
6519                        {
6520                            let mut dst = xr.slice_mut(0..lq);
6521                            engine.stream().memcpy_dtod(
6522                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
6523                                &mut dst,
6524                            )?;
6525                        }
6526                        engine.matvec_bf16_b4_into(
6527                            [weights[0], weights[1], weights[2], weights[3]],
6528                            &xr,
6529                            &mut yr,
6530                            *o_block_cols,
6531                            *o_out,
6532                        )?;
6533                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
6534                        engine
6535                            .stream()
6536                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
6537                    }
6538                } else if crate::step_tp_w8_on()
6539                    && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some())
6540                    && (4 * *o_block_cols) % 32 == 0
6541                {
6542                    // The verify walk's biggest single kernel: bf16 tcol o_proj was 24.8% of
6543                    // spec GPU time. Same planar q8_0 mirrors the decode arm uses, one launch
6544                    // over all t columns.
6545                    let in_f = 4 * *o_block_cols;
6546                    if *w8t_oin != in_f || *w8t_cap < t || w8t_oaq.len() != ranks {
6547                        w8t_oaq.clear();
6548                        w8t_oad.clear();
6549                        for e_rank in &self.ranks {
6550                            let _m = e_rank.gpu.enter_main()?;
6551                            w8t_oaq.push(e_rank.alloc_i8_uninit(32 * in_f)?);
6552                            w8t_oad.push(e_rank.alloc_uninit::<f32>(32 * (in_f / 32))?);
6553                        }
6554                        *w8t_oin = in_f;
6555                        *w8t_cap = (*w8t_cap).max(32);
6556                    }
6557                    engine.quantize_q8_1_into(
6558                        &tcol_gated[rank],
6559                        t,
6560                        in_f,
6561                        &mut w8t_oaq[rank],
6562                        &mut w8t_oad[rank],
6563                    )?;
6564                    engine.qmatvec_q8_0_b4_rp_t_into(
6565                        [
6566                            o_m.ranks[rank][0].q8.as_ref().unwrap(),
6567                            o_m.ranks[rank][1].q8.as_ref().unwrap(),
6568                            o_m.ranks[rank][2].q8.as_ref().unwrap(),
6569                            o_m.ranks[rank][3].q8.as_ref().unwrap(),
6570                        ],
6571                        &w8t_oaq[rank],
6572                        &w8t_oad[rank],
6573                        &mut tcol_opart[rank],
6574                        *o_block_cols,
6575                        *o_out,
6576                        t,
6577                    )?;
6578                } else {
6579                    engine.matvec_bf16_b4_tcol_into(
6580                        [weights[0], weights[1], weights[2], weights[3]],
6581                        &tcol_gated[rank],
6582                        &mut tcol_opart[rank],
6583                        *o_block_cols,
6584                        *o_out,
6585                        t,
6586                    )?;
6587                }
6588            }
6589            if rank != 0 {
6590                ws.ev_rank[rank].record(&engine.stream())?;
6591            }
6592        }
6593        let root = &self.ranks[0];
6594        {
6595            let _main = root.gpu.enter_main()?;
6596            for ev in ws.ev_rank.iter().skip(1) {
6597                root.stream().wait(ev)?;
6598            }
6599            {
6600                let StepTpDecodeV2Ws {
6601                    tcol_opart,
6602                    tcol_opeer,
6603                    tcol_omix,
6604                    o_out,
6605                    ..
6606                } = &mut *ws;
6607                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
6608                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
6609                {
6610                    let mut dst = opeer.slice_mut(0..t * *o_out);
6611                    root.stream()
6612                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
6613                }
6614                // Elementwise over the whole slab: per element identical to the per-column
6615                // direct-join add (independent lanes, same operand values).
6616                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
6617            }
6618            ws.ev_oproj.record(&root.stream())?;
6619        }
6620        let _main = e.gpu.enter_main()?;
6621        e.stream().wait(&ws.ev_oproj)?;
6622        let mut out = e.uninit(t * ws.o_out)?;
6623        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
6624        e.stream().memcpy_dtod(
6625            &omix.slice(0..t * ws.o_out),
6626            &mut out.slice_mut(0..t * ws.o_out),
6627        )?;
6628        Ok(out)
6629    }
6630
6631    pub(crate) fn decode_v2_input_qkv(
6632        &self,
6633        ws: &mut StepTpDecodeV2Ws,
6634        e: &Engine,
6635        h: &CudaSlice<f32>,
6636        pos_d: &CudaSlice<i32>,
6637        gate_raw: Option<&CudaSlice<f32>>,
6638        gate_shards: Option<StepTpGateShards<'_>>,
6639        decode_input: &mut ResidentReplicatedDeviceRows,
6640        q_m: &ResidentBf16ColumnParallel,
6641        k_m: &ResidentBf16ColumnParallel,
6642        v_m: &ResidentBf16ColumnParallel,
6643        q_norm: &[CudaSlice<f32>],
6644        k_norm: &[CudaSlice<f32>],
6645        head_dim: usize,
6646        n_rot: usize,
6647        rope_base: f32,
6648        rope_freqs: &[Option<&CudaSlice<f32>>],
6649        rms_eps: f32,
6650        defer_norm_rope: bool,
6651        tcol_col: Option<usize>,
6652    ) -> Result<(), Box<dyn std::error::Error>> {
6653        let ranks = self.ranks.len();
6654        validate_replicated_device_rows(&self.ranks, decode_input)?;
6655        if decode_input.tokens != 1
6656            || decode_input.width != q_m.in_features
6657            || pos_d.len() != 1
6658            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
6659            || gate_raw.is_none() != gate_shards.is_some()
6660            || gate_shards.as_ref().is_some_and(|shards| match shards {
6661                StepTpGateShards::F32(shards) => shards.len() != ranks,
6662                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
6663            })
6664            || q_norm.len() != ranks
6665            || k_norm.len() != ranks
6666            || rope_freqs.len() != ranks
6667            || e.ctx().ordinal() != ws.e_device
6668        {
6669            return Err("step TP decode v2 input geometry mismatch".into());
6670        }
6671
6672        let qkv_fused = step_tp_qkv_fused_enabled()?;
6673        if gate_shards.is_some() && !qkv_fused {
6674            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
6675        }
6676        let values = decode_input.width;
6677        if h.len() != values {
6678            return Err(format!(
6679                "step TP decode v2 hidden width {} != replicated width {values}",
6680                h.len()
6681            )
6682            .into());
6683        }
6684
6685        if qkv_fused {
6686            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
6687            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
6688            // from the stages on its own stream — exactly the shape graph capture wraps.
6689            if ws.h_stage.is_none() {
6690                use cudarc::driver::DevicePtr;
6691                let _main = e.gpu.enter_main()?;
6692                let h_stage = e.uninit(values)?;
6693                let pos_stage = e.htod_i32(&[0])?;
6694                {
6695                    let stream = e.stream();
6696                    let (hp, _g0) = h_stage.device_ptr(&stream);
6697                    let (pp, _g1) = pos_stage.device_ptr(&stream);
6698                    ws.raw_h_stage = hp as u64;
6699                    ws.raw_pos_stage = pp as u64;
6700                }
6701                ws.h_stage = Some(h_stage);
6702                ws.pos_stage = Some(pos_stage);
6703                for rank in 0..ranks {
6704                    use cudarc::driver::DevicePtr;
6705                    let engine = &self.ranks[rank];
6706                    let _rmain = engine.gpu.enter_main()?;
6707                    let attn_in = engine.uninit(values)?;
6708                    let (dp, pp) = {
6709                        let stream = engine.stream();
6710                        let (dp, _g2) = attn_in.device_ptr(&stream);
6711                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
6712                        (dp as u64, pp as u64)
6713                    };
6714                    ws.raw_attn_in.push(dp);
6715                    ws.raw_pos.push(pp);
6716                    ws.attn_in.push(attn_in);
6717                }
6718                {
6719                    use cudarc::driver::DevicePtr;
6720                    let root = &self.ranks[0];
6721                    let _rmain = root.gpu.enter_main()?;
6722                    let stream = root.stream();
6723                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
6724                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
6725                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
6726                    ws.raw_peer_partial = a as u64;
6727                    ws.raw_k_shadow = b as u64;
6728                    ws.raw_v_shadow = c as u64;
6729                }
6730                {
6731                    use cudarc::driver::DevicePtr;
6732                    let rank1 = &self.ranks[1];
6733                    let _rmain = rank1.gpu.enter_main()?;
6734                    let stream = rank1.stream();
6735                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
6736                    let (b, _g) = ws.k[1].device_ptr(&stream);
6737                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
6738                    ws.raw_o_partial1 = a as u64;
6739                    ws.raw_k1 = b as u64;
6740                    ws.raw_v1 = c as u64;
6741                }
6742            }
6743            {
6744                let _main = e.gpu.enter_main()?;
6745                {
6746                    // (Always staged: a tcol column below the dcw floor falls back to the
6747                    // normal fused arm, which reads h through this stage.)
6748                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
6749                    let mut dst = h_stage.slice_mut(0..values);
6750                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
6751                }
6752                {
6753                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
6754                    let mut dst = pos_stage.slice_mut(0..1);
6755                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
6756                }
6757                ws.ev_entry.record(&e.stream())?;
6758            }
6759            for rank in 0..ranks {
6760                let engine = &self.ranks[rank];
6761                let _main = engine.gpu.enter_main()?;
6762                engine.stream().wait(&ws.ev_entry)?;
6763            }
6764        } else {
6765            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
6766            {
6767                let _main = e.gpu.enter_main()?;
6768                if let Some(gate_raw) = gate_raw {
6769                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
6770                    e.stream()
6771                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
6772                }
6773                ws.ev_entry.record(&e.stream())?;
6774            }
6775            {
6776                let root = &self.ranks[0];
6777                let _main = root.gpu.enter_main()?;
6778                root.stream().wait(&ws.ev_entry)?;
6779                let mut destination = decode_input.ranks[0].slice_mut(0..values);
6780                root.stream()
6781                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
6782                ws.ev_refresh.record(&root.stream())?;
6783            }
6784            for rank in 1..ranks {
6785                let engine = &self.ranks[rank];
6786                let _main = engine.gpu.enter_main()?;
6787                engine.stream().wait(&ws.ev_refresh)?;
6788                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
6789                let mut destination = peer_rows[0].slice_mut(0..values);
6790                engine
6791                    .stream()
6792                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
6793            }
6794        }
6795        for rank in 0..ranks {
6796            self.decode_v2_input_qkv_rank(
6797                ws,
6798                pos_d,
6799                decode_input,
6800                q_m,
6801                k_m,
6802                v_m,
6803                q_norm,
6804                k_norm,
6805                head_dim,
6806                n_rot,
6807                rope_base,
6808                rope_freqs,
6809                rms_eps,
6810                gate_shards.as_ref(),
6811                qkv_fused,
6812                defer_norm_rope,
6813                rank,
6814                tcol_col,
6815            )?;
6816        }
6817        Ok(())
6818    }
6819
6820    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
6821    /// per-device issue unit the whole-token graph captures on that rank's stream.
6822    #[allow(clippy::too_many_arguments)]
6823    pub(crate) fn decode_v2_input_qkv_rank(
6824        &self,
6825        ws: &mut StepTpDecodeV2Ws,
6826        pos_d: &CudaSlice<i32>,
6827        decode_input: &mut ResidentReplicatedDeviceRows,
6828        q_m: &ResidentBf16ColumnParallel,
6829        k_m: &ResidentBf16ColumnParallel,
6830        v_m: &ResidentBf16ColumnParallel,
6831        q_norm: &[CudaSlice<f32>],
6832        k_norm: &[CudaSlice<f32>],
6833        head_dim: usize,
6834        n_rot: usize,
6835        rope_base: f32,
6836        rope_freqs: &[Option<&CudaSlice<f32>>],
6837        rms_eps: f32,
6838        gate_shards: Option<&StepTpGateShards<'_>>,
6839        qkv_fused: bool,
6840        defer_norm_rope: bool,
6841        rank: usize,
6842        tcol_col: Option<usize>,
6843    ) -> Result<(), Box<dyn std::error::Error>> {
6844        let ranks = self.ranks.len();
6845        let local_heads = ws.local_q_dim / head_dim;
6846        let local_kv_heads = ws.local_kv_dim / head_dim;
6847        let engine = &self.ranks[rank];
6848        let _main = engine.gpu.enter_main()?;
6849        let ws_e_device = ws.e_device;
6850        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
6851        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
6852        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
6853        // below exactly as in the t=1 program.
6854        if qkv_fused && tcol_col.is_some() {
6855            let c = tcol_col.expect("checked");
6856            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
6857                return Err("tcol select without precompute".into());
6858            }
6859            // The select skips the matvec but NOT the position: rope/append below still
6860            // read this rank's pos buffer, which only the (skipped) stage path fills for
6861            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
6862            if engine.ctx().ordinal() != ws_e_device {
6863                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6864            }
6865            let StepTpDecodeV2Ws {
6866                tcol_q,
6867                tcol_k,
6868                tcol_v,
6869                tcol_g,
6870                q_raw,
6871                k_raw,
6872                v_raw,
6873                gate,
6874                local_q_dim,
6875                local_kv_dim,
6876                heads,
6877                ..
6878            } = &mut *ws;
6879            let lg = *heads / ranks;
6880            let stream = engine.stream();
6881            {
6882                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
6883                stream.memcpy_dtod(
6884                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
6885                    &mut dst,
6886                )?;
6887            }
6888            {
6889                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
6890                stream.memcpy_dtod(
6891                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6892                    &mut dst,
6893                )?;
6894            }
6895            {
6896                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
6897                stream.memcpy_dtod(
6898                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6899                    &mut dst,
6900                )?;
6901            }
6902            if lg > 0 {
6903                let mut dst = gate[rank].slice_mut(0..lg);
6904                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
6905            }
6906            if !defer_norm_rope {
6907                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
6908                // fall through and recompute this column's QKV from the REAL h row — the
6909                // caller always passes it. The slab copies above are dead stores.
6910            } else {
6911                return Ok(());
6912            }
6913        }
6914        if qkv_fused {
6915            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
6916            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
6917            // SHARING e's device reads the stages directly — same context (probed), ordering
6918            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
6919            let same_dev = engine.ctx().ordinal() == ws.e_device;
6920            if !same_dev {
6921                raw_copy_bytes(
6922                    ws.raw_attn_in[rank],
6923                    ws.raw_h_stage,
6924                    q_m.in_features * 4,
6925                    engine,
6926                )?;
6927                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6928            }
6929            let StepTpDecodeV2Ws {
6930                q_raw,
6931                k_raw,
6932                v_raw,
6933                gate,
6934                gate_e,
6935                attn_in,
6936                h_stage,
6937                heads,
6938                local_q_dim,
6939                local_kv_dim,
6940                w8_aq,
6941                w8_ad,
6942                w8_in,
6943                ..
6944            } = &mut *ws;
6945            let input_ref: &CudaSlice<f32> = if same_dev {
6946                h_stage
6947                    .as_ref()
6948                    .ok_or("step TP decode v2 stage not armed")?
6949            } else {
6950                &attn_in[rank]
6951            };
6952            match (
6953                &q_m.ranks[rank].weight,
6954                &k_m.ranks[rank].weight,
6955                &v_m.ranks[rank].weight,
6956            ) {
6957                (
6958                    ResidentBf16Weight::F32(wq),
6959                    ResidentBf16Weight::F32(wk),
6960                    ResidentBf16Weight::F32(wv),
6961                ) => {
6962                    let (wg, out_g) = match &gate_shards {
6963                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
6964                        Some(StepTpGateShards::Bf16(_)) => {
6965                            return Err("step TP decode v2 gate shard class does not \
6966                                            match the F32 projections"
6967                                .into());
6968                        }
6969                        // out_g = 0: the kernel never reads wg; any resident buffer works.
6970                        None => (&*gate_e, 0),
6971                    };
6972                    engine.matvec_f32_qkv_into(
6973                        wq,
6974                        wk,
6975                        wv,
6976                        wg,
6977                        input_ref,
6978                        &mut q_raw[rank],
6979                        &mut k_raw[rank],
6980                        &mut v_raw[rank],
6981                        &mut gate[rank],
6982                        q_m.in_features,
6983                        *local_q_dim,
6984                        *local_kv_dim,
6985                        out_g,
6986                    )?;
6987                }
6988                (
6989                    ResidentBf16Weight::Bf16(wq),
6990                    ResidentBf16Weight::Bf16(wk),
6991                    ResidentBf16Weight::Bf16(wv),
6992                ) => {
6993                    let (wg, out_g) = match &gate_shards {
6994                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
6995                        Some(StepTpGateShards::F32(_)) => {
6996                            return Err("step TP decode v2 gate shard class does not \
6997                                            match the bf16 projections"
6998                                .into());
6999                        }
7000                        None => (wq, 0),
7001                    };
7002                    // MEMRA_STEP_TP_W8: q8_0 weights + q8_1 activation through mmvq instead of
7003                    // the fused bf16 qkvg. NUMERIC CLASS (int8 dp4a with per-32 scales, not a
7004                    // bf16 fma chain) — argmax-gated, never a bit-tape flip. Q, K and V each
7005                    // get their own launch because the fused kernel has no q8 twin; the gate
7006                    // rows stay bf16 (32 rows, ~0.3 MB, nothing to win and one less class to
7007                    // qualify). Measured motive: 23.0 us bf16 -> 14.0 us q8 at this shape.
7008                    let in_f = q_m.in_features;
7009                    let q8_ready = crate::step_tp_w8_on()
7010                        && q_m.ranks[rank].q8.is_some()
7011                        && k_m.ranks[rank].q8.is_some()
7012                        && v_m.ranks[rank].q8.is_some();
7013                    if q8_ready {
7014                        if *w8_in != in_f || w8_aq.len() != ranks {
7015                            w8_aq.clear();
7016                            w8_ad.clear();
7017                            for e_rank in &self.ranks {
7018                                let _m = e_rank.gpu.enter_main()?;
7019                                w8_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
7020                                w8_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
7021                            }
7022                            *w8_in = in_f;
7023                        }
7024                        engine.quantize_q8_1_into(
7025                            input_ref,
7026                            1,
7027                            in_f,
7028                            &mut w8_aq[rank],
7029                            &mut w8_ad[rank],
7030                        )?;
7031                        // ONE launch over the stacked q/k/v rows. The three-call version
7032                        // measured 79.52 vs 80.72 tok/s — SLOWER than the bf16 fused kernel —
7033                        // because three launches plus the activation quantize cost more than
7034                        // the halved weight bytes save. Bit-identical to those three calls.
7035                        engine.qmatvec_q8_0_qkv_rp_into(
7036                            q_m.ranks[rank].q8.as_ref().unwrap(),
7037                            k_m.ranks[rank].q8.as_ref().unwrap(),
7038                            v_m.ranks[rank].q8.as_ref().unwrap(),
7039                            &w8_aq[rank],
7040                            &w8_ad[rank],
7041                            &mut q_raw[rank],
7042                            &mut k_raw[rank],
7043                            &mut v_raw[rank],
7044                            in_f,
7045                            *local_q_dim,
7046                            *local_kv_dim,
7047                        )?;
7048                        if out_g > 0 {
7049                            engine.matvec_bf16_into(wg, input_ref, &mut gate[rank], in_f, out_g)?;
7050                        }
7051                    } else {
7052                        engine.matvec_bf16_qkvg_into(
7053                            wq,
7054                            wk,
7055                            wv,
7056                            wg,
7057                            input_ref,
7058                            &mut q_raw[rank],
7059                            &mut k_raw[rank],
7060                            &mut v_raw[rank],
7061                            &mut gate[rank],
7062                            q_m.in_features,
7063                            *local_q_dim,
7064                            *local_kv_dim,
7065                            out_g,
7066                        )?;
7067                    }
7068                }
7069                _ => {
7070                    return Err("step TP decode v2 QKV projections mix residency classes".into());
7071                }
7072            }
7073        } else {
7074            for (matrix, local_out, raw) in [
7075                (q_m, ws.local_q_dim, &mut ws.q_raw),
7076                (k_m, ws.local_kv_dim, &mut ws.k_raw),
7077                (v_m, ws.local_kv_dim, &mut ws.v_raw),
7078            ] {
7079                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
7080                    return Err("step TP decode v2 lost its F32 projection residency".into());
7081                };
7082                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
7083                engine.linear_f32_resident_canonical_rows_t1_into(
7084                    &decode_input.ranks[rank],
7085                    values_w,
7086                    &mut raw[rank],
7087                    matrix.in_features,
7088                    local_out,
7089                    chunk_rows,
7090                )?;
7091            }
7092        }
7093        if qkv_fused && defer_norm_rope {
7094            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
7095        } else if qkv_fused {
7096            // Fused norm+rope: one launch; the position comes from the rank-local staged
7097            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
7098            let StepTpDecodeV2Ws {
7099                q_raw,
7100                k_raw,
7101                q,
7102                k,
7103                pos,
7104                pos_stage,
7105                ..
7106            } = &mut *ws;
7107            let same_dev = engine.ctx().ordinal() == ws_e_device;
7108            let pos_ref: &CudaSlice<i32> = if same_dev {
7109                pos_stage
7110                    .as_ref()
7111                    .ok_or("step TP decode v2 pos stage not armed")?
7112            } else {
7113                &pos[rank]
7114            };
7115            engine.qk_norm_rope_into(
7116                &q_raw[rank],
7117                &k_raw[rank],
7118                &q_norm[rank],
7119                &k_norm[rank],
7120                &mut q[rank],
7121                &mut k[rank],
7122                pos_ref,
7123                head_dim,
7124                n_rot,
7125                local_heads,
7126                local_kv_heads,
7127                rms_eps,
7128                rope_base,
7129                1.0,
7130                rope_freqs[rank],
7131            )?;
7132        } else {
7133            engine.rms_norm(
7134                &ws.q_raw[rank],
7135                &q_norm[rank],
7136                &mut ws.q[rank],
7137                head_dim,
7138                local_heads,
7139                rms_eps,
7140            )?;
7141            engine.rms_norm(
7142                &ws.k_raw[rank],
7143                &k_norm[rank],
7144                &mut ws.k[rank],
7145                head_dim,
7146                local_kv_heads,
7147                rms_eps,
7148            )?;
7149            {
7150                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
7151                engine
7152                    .stream()
7153                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
7154            }
7155            engine.rope_neox2(
7156                &mut ws.q[rank],
7157                &mut ws.k[rank],
7158                &ws.pos[rank],
7159                head_dim,
7160                n_rot,
7161                local_heads,
7162                local_kv_heads,
7163                1,
7164                rope_base,
7165                1.0,
7166                rope_freqs[rank],
7167            )?;
7168        }
7169        if gate_shards.is_none() {
7170            let gate_start = rank * (ws.heads / ranks);
7171            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
7172            engine.stream().memcpy_dtod(
7173                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
7174                &mut gate_dst,
7175            )?;
7176        }
7177        Ok(())
7178    }
7179
7180    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
7181    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
7182    /// eager caller; graphs order via parent edges instead).
7183    pub(crate) fn decode_v2_finish_rank_partial(
7184        &self,
7185        ws: &mut StepTpDecodeV2Ws,
7186        o_m: &ResidentStepBf16RowParallel,
7187        o_fused: bool,
7188        rank: usize,
7189    ) -> Result<(), Box<dyn std::error::Error>> {
7190        let engine = &self.ranks[rank];
7191        let _main = engine.gpu.enter_main()?;
7192        if o_fused {
7193            let StepTpDecodeV2Ws {
7194                gated,
7195                o_partials,
7196                o_block_cols,
7197                o_out,
7198                w8o_aq,
7199                w8o_ad,
7200                w8o_in,
7201                ..
7202            } = &mut *ws;
7203            let all_f32 = o_m.ranks[rank]
7204                .iter()
7205                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
7206            if all_f32 {
7207                let mut weights = Vec::with_capacity(4);
7208                for block in 0..4 {
7209                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
7210                        unreachable!("all_f32 checked above");
7211                    };
7212                    weights.push(weight);
7213                }
7214                engine.matvec_f32_b4_into(
7215                    [weights[0], weights[1], weights[2], weights[3]],
7216                    &gated[rank],
7217                    &mut o_partials[rank][0],
7218                    *o_block_cols,
7219                    *o_out,
7220                )?;
7221            } else if crate::step_tp_w8_on() && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some()) {
7222                // MEMRA_STEP_TP_W8, o_proj half: quantize the gated attention output once and
7223                // run all four HEAD_SPLIT blocks in one q8 launch. Measured motive: bf16 b4 is
7224                // 24.2 us/layer against 11.7 for the q8 shape — the largest decode line left
7225                // after the QKV arm banked +2.9%.
7226                let in_f = 4 * *o_block_cols;
7227                if *w8o_in != in_f || w8o_aq.len() != self.ranks.len() {
7228                    w8o_aq.clear();
7229                    w8o_ad.clear();
7230                    for e_rank in &self.ranks {
7231                        let _m = e_rank.gpu.enter_main()?;
7232                        w8o_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
7233                        w8o_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
7234                    }
7235                    *w8o_in = in_f;
7236                }
7237                engine.quantize_q8_1_into(
7238                    &gated[rank],
7239                    1,
7240                    in_f,
7241                    &mut w8o_aq[rank],
7242                    &mut w8o_ad[rank],
7243                )?;
7244                engine.qmatvec_q8_0_b4_rp_into(
7245                    [
7246                        o_m.ranks[rank][0].q8.as_ref().unwrap(),
7247                        o_m.ranks[rank][1].q8.as_ref().unwrap(),
7248                        o_m.ranks[rank][2].q8.as_ref().unwrap(),
7249                        o_m.ranks[rank][3].q8.as_ref().unwrap(),
7250                    ],
7251                    &w8o_aq[rank],
7252                    &w8o_ad[rank],
7253                    &mut o_partials[rank][0],
7254                    *o_block_cols,
7255                    *o_out,
7256                )?;
7257            } else {
7258                let mut weights = Vec::with_capacity(4);
7259                for block in 0..4 {
7260                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
7261                        return Err("step TP decode v2 O projections mix residency classes".into());
7262                    };
7263                    weights.push(weight);
7264                }
7265                engine.matvec_bf16_b4_into(
7266                    [weights[0], weights[1], weights[2], weights[3]],
7267                    &gated[rank],
7268                    &mut o_partials[rank][0],
7269                    *o_block_cols,
7270                    *o_out,
7271                )?;
7272            }
7273        } else {
7274            for block in 0..ws.blocks_per_rank {
7275                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
7276                    return Err("step TP decode v2 lost its F32 O residency".into());
7277                };
7278                let x =
7279                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
7280                let w = weight.slice(0..weight.len());
7281                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
7282                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
7283            }
7284        }
7285        Ok(())
7286    }
7287
7288    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
7289    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
7290    ///
7291    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
7292    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
7293    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
7294    /// rank's blocks, one `add` per block.
7295    pub(crate) fn decode_v2_finish(
7296        &self,
7297        ws: &mut StepTpDecodeV2Ws,
7298        e: &Engine,
7299        o_m: &ResidentStepBf16RowParallel,
7300    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7301        let ranks = self.ranks.len();
7302        if e.ctx().ordinal() != ws.e_device {
7303            return Err("step TP decode v2 finish engine changed".into());
7304        }
7305        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
7306        // (in-order canonical block accumulation per element) and a single peer-copy + add on
7307        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
7308        // numeric-class door and gate as the fused QKV projection.
7309        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
7310
7311        // Per-rank O block partials on the owning rank's stream (serial after the attention
7312        // kernels the driver queued there), then the rank-done event for root's peer reads.
7313        for rank in 0..ranks {
7314            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
7315            if rank == 0 {
7316                // root == rank0: its own stream order covers the partial; only peers need
7317                // the record/wait pair (host-op diet, matches the routes-arm skip).
7318                continue;
7319            }
7320            let engine = &self.ranks[rank];
7321            let _main = engine.gpu.enter_main()?;
7322            ws.ev_rank[rank].record(&engine.stream())?;
7323        }
7324
7325        // Root reduce in canonical order + shadow gathers, all on the root stream.
7326        let root = &self.ranks[0];
7327        #[allow(unused_assignments)]
7328        let mut final_in_a = false;
7329        {
7330            let _main = root.gpu.enter_main()?;
7331            for ev in ws.ev_rank.iter().skip(1) {
7332                root.stream().wait(ev)?;
7333            }
7334            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
7335                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
7336                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
7337                // partial is root-stream-ordered — record ONE event and let the model
7338                // engine do the single add itself, straight into its own output row.
7339                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
7340                ws.ev_oproj.record(&root.stream())?;
7341                let _main = e.gpu.enter_main()?;
7342                e.stream().wait(&ws.ev_oproj)?;
7343                let mut output = e.uninit(ws.o_out)?;
7344                if oproj_tail_on() && oproj_tail_eligible() {
7345                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
7346                    // only the arithmetic moves). `output` is returned unwritten.
7347                    use cudarc::driver::DevicePtr;
7348                    let stream = e.stream();
7349                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
7350                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
7351                    set_oproj_tail((p0 as u64, p1 as u64));
7352                    return Ok(output);
7353                }
7354                e.add(
7355                    &ws.o_partials[0][0],
7356                    &ws.o_partials[1][0],
7357                    &mut output,
7358                    ws.o_out,
7359                )?;
7360                return Ok(output);
7361            }
7362            if o_fused {
7363                self.decode_v2_finish_root_fused(ws)?;
7364                ws.ev_oproj.record(&root.stream())?;
7365                let _main = e.gpu.enter_main()?;
7366                e.stream().wait(&ws.ev_oproj)?;
7367                let mut output = e.uninit(ws.o_out)?;
7368                e.stream().memcpy_dtod(
7369                    &ws.reduce_a.slice(0..ws.o_out),
7370                    &mut output.slice_mut(0..ws.o_out),
7371                )?;
7372                return Ok(output);
7373            }
7374            let mut first = true;
7375            let mut current_is_a = false;
7376            for rank in 0..ranks {
7377                for block in 0..ws.blocks_per_rank {
7378                    let use_peer = rank != 0;
7379                    if use_peer {
7380                        root.stream()
7381                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
7382                    }
7383                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
7384                    match (first, current_is_a, use_peer) {
7385                        (true, _, true) => {
7386                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7387                        }
7388                        (true, _, false) => root.add(
7389                            &ws.zeros,
7390                            &ws.o_partials[0][block],
7391                            &mut ws.reduce_a,
7392                            ws.o_out,
7393                        )?,
7394                        (false, true, true) => {
7395                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
7396                        }
7397                        (false, true, false) => root.add(
7398                            &ws.reduce_a,
7399                            &ws.o_partials[0][block],
7400                            &mut ws.reduce_b,
7401                            ws.o_out,
7402                        )?,
7403                        (false, false, true) => {
7404                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7405                        }
7406                        (false, false, false) => root.add(
7407                            &ws.reduce_b,
7408                            &ws.o_partials[0][block],
7409                            &mut ws.reduce_a,
7410                            ws.o_out,
7411                        )?,
7412                    }
7413                    current_is_a = first || !current_is_a;
7414                    first = false;
7415                }
7416            }
7417            final_in_a = current_is_a;
7418
7419            for rank in 0..ranks {
7420                let start = rank * ws.local_kv_dim;
7421                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
7422                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
7423                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
7424                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
7425            }
7426            ws.ev_oproj.record(&root.stream())?;
7427        }
7428
7429        // Model-engine output: e waits the root event, then copies the reduced row into a
7430        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
7431        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
7432        let _main = e.gpu.enter_main()?;
7433        e.stream().wait(&ws.ev_oproj)?;
7434        let mut output = e.uninit(ws.o_out)?;
7435        let source = if final_in_a {
7436            &ws.reduce_a
7437        } else {
7438            &ws.reduce_b
7439        };
7440        e.stream().memcpy_dtod(
7441            &source.slice(0..ws.o_out),
7442            &mut output.slice_mut(0..ws.o_out),
7443        )?;
7444        Ok(output)
7445    }
7446
7447    pub fn run_routed_experts(
7448        &self,
7449        experts: &ResidentExpertParallel,
7450        input: &[f32],
7451        tokens: usize,
7452        selected: &[usize],
7453        route_weights: &[f32],
7454        experts_per_token: usize,
7455        activation_limit: Option<f32>,
7456    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7457        validate_step_expert_activation_limit(activation_limit)?;
7458        validate_ep_residency(&self.ranks, experts)?;
7459        validate_activations(input, tokens, experts.input_width)?;
7460        let pairs = tokens
7461            .checked_mul(experts_per_token)
7462            .ok_or("EP route count overflow")?;
7463        if selected.len() != pairs || route_weights.len() != pairs {
7464            return Err(format!(
7465                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
7466                 {experts_per_token} ({pairs})",
7467                selected.len(),
7468                route_weights.len(),
7469            )
7470            .into());
7471        }
7472        if !route_weights.iter().all(|weight| weight.is_finite()) {
7473            return Err("EP route weights contain a non-finite value".into());
7474        }
7475        if self.native_p2p {
7476            return self.run_routed_experts_native(
7477                experts,
7478                input,
7479                tokens,
7480                selected,
7481                route_weights,
7482                experts_per_token,
7483                activation_limit,
7484            );
7485        }
7486
7487        let mut output = vec![0.0f32; tokens * experts.input_width];
7488        let per_rank = experts.expert_count / experts.ranks.len();
7489        for token in 0..tokens {
7490            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7491            for slot in 0..experts_per_token {
7492                let pair = token * experts_per_token + slot;
7493                let expert = selected[pair];
7494                if expert >= experts.expert_count {
7495                    return Err(format!(
7496                        "EP selected expert {expert} outside 0..{}",
7497                        experts.expert_count
7498                    )
7499                    .into());
7500                }
7501                let owner = expert / per_rank;
7502                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7503                let rank = &experts.ranks[owner];
7504                let engine = &self.ranks[owner];
7505                let gate =
7506                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
7507                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
7508                let activated: Vec<f32> = gate
7509                    .iter()
7510                    .zip(&up)
7511                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7512                    .collect();
7513                debug_assert_eq!(activated.len(), experts.expert_width);
7514                let down =
7515                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
7516                let weight = route_weights[pair];
7517                for (sum, value) in output
7518                    [token * experts.input_width..(token + 1) * experts.input_width]
7519                    .iter_mut()
7520                    .zip(down)
7521                {
7522                    *sum += weight * value;
7523                }
7524            }
7525        }
7526        Ok(output)
7527    }
7528
7529    fn run_routed_experts_native(
7530        &self,
7531        experts: &ResidentExpertParallel,
7532        input: &[f32],
7533        tokens: usize,
7534        selected: &[usize],
7535        route_weights: &[f32],
7536        experts_per_token: usize,
7537        activation_limit: Option<f32>,
7538    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7539        if !self.native_p2p || self.ranks.len() < 2 {
7540            return Err("native EP execution requires at least two P2P ranks".into());
7541        }
7542        if self.ep_device_arithmetic {
7543            return self.run_routed_experts_native_device(
7544                experts,
7545                input,
7546                tokens,
7547                selected,
7548                route_weights,
7549                experts_per_token,
7550                activation_limit,
7551            );
7552        }
7553        let mut output = vec![0.0f32; tokens * experts.input_width];
7554        let per_rank = experts.expert_count / experts.ranks.len();
7555        for token in 0..tokens {
7556            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7557            let mut rank_inputs = (0..self.ranks.len())
7558                .map(|_| None)
7559                .collect::<Vec<Option<CudaSlice<f32>>>>();
7560            rank_inputs[0] = Some({
7561                let root = &self.ranks[0];
7562                let _main = root.gpu.enter_main()?;
7563                root.htod(input_row)?
7564            });
7565
7566            for slot in 0..experts_per_token {
7567                let pair = token * experts_per_token + slot;
7568                let expert = selected[pair];
7569                if expert >= experts.expert_count {
7570                    return Err(format!(
7571                        "EP selected expert {expert} outside 0..{}",
7572                        experts.expert_count
7573                    )
7574                    .into());
7575                }
7576                let owner = expert / per_rank;
7577                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7578                if rank_inputs[owner].is_none() {
7579                    let peer_input = {
7580                        let root_input = rank_inputs[0]
7581                            .as_ref()
7582                            .ok_or("native EP lost its root input")?;
7583                        let engine = &self.ranks[owner];
7584                        let _main = engine.gpu.enter_main()?;
7585                        let mut peer_input = engine.uninit(experts.input_width)?;
7586                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7587                        peer_input
7588                    };
7589                    rank_inputs[owner] = Some(peer_input);
7590                }
7591
7592                let rank = &experts.ranks[owner];
7593                let engine = &self.ranks[owner];
7594                let owner_input = rank_inputs[owner]
7595                    .as_ref()
7596                    .ok_or("native EP owner input is absent after dispatch")?;
7597                let gate = run_resident_bank_expert_device(
7598                    engine,
7599                    &rank.gate,
7600                    local_expert,
7601                    owner_input,
7602                    1,
7603                )?;
7604                let up = run_resident_bank_expert_device(
7605                    engine,
7606                    &rank.up,
7607                    local_expert,
7608                    owner_input,
7609                    1,
7610                )?;
7611                let (gate, up) = {
7612                    let _main = engine.gpu.enter_main()?;
7613                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
7614                };
7615                let activated = gate
7616                    .iter()
7617                    .zip(&up)
7618                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7619                    .collect::<Vec<_>>();
7620                debug_assert_eq!(activated.len(), experts.expert_width);
7621                let activated = {
7622                    let _main = engine.gpu.enter_main()?;
7623                    engine.htod(&activated)?
7624                };
7625                let down = run_resident_bank_expert_device(
7626                    engine,
7627                    &rank.down,
7628                    local_expert,
7629                    &activated,
7630                    1,
7631                )?;
7632                let down = if owner == 0 {
7633                    let _main = engine.gpu.enter_main()?;
7634                    engine.dtoh(&down)?
7635                } else {
7636                    let root = &self.ranks[0];
7637                    let _main = root.gpu.enter_main()?;
7638                    let mut root_down = root.uninit(experts.input_width)?;
7639                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7640                    root.dtoh(&root_down)?
7641                };
7642                let weight = route_weights[pair];
7643                for (sum, value) in output
7644                    [token * experts.input_width..(token + 1) * experts.input_width]
7645                    .iter_mut()
7646                    .zip(down)
7647                {
7648                    *sum += weight * value;
7649                }
7650            }
7651        }
7652        Ok(output)
7653    }
7654
7655    fn run_routed_experts_native_device(
7656        &self,
7657        experts: &ResidentExpertParallel,
7658        input: &[f32],
7659        tokens: usize,
7660        selected: &[usize],
7661        route_weights: &[f32],
7662        experts_per_token: usize,
7663        activation_limit: Option<f32>,
7664    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7665        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
7666            return Err(
7667                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
7668            );
7669        }
7670        let mut output = Vec::with_capacity(tokens * experts.input_width);
7671        let per_rank = experts.expert_count / experts.ranks.len();
7672        let root = &self.ranks[0];
7673        for token in 0..tokens {
7674            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7675            let mut rank_inputs = (0..self.ranks.len())
7676                .map(|_| None)
7677                .collect::<Vec<Option<CudaSlice<f32>>>>();
7678            rank_inputs[0] = Some({
7679                let _main = root.gpu.enter_main()?;
7680                root.htod(input_row)?
7681            });
7682            let mut root_output = {
7683                let _main = root.gpu.enter_main()?;
7684                root.zeros(experts.input_width)?
7685            };
7686            let mut remote_down_keepalive = Vec::new();
7687
7688            for slot in 0..experts_per_token {
7689                let pair = token * experts_per_token + slot;
7690                let expert = selected[pair];
7691                if expert >= experts.expert_count {
7692                    return Err(format!(
7693                        "EP selected expert {expert} outside 0..{}",
7694                        experts.expert_count
7695                    )
7696                    .into());
7697                }
7698                let owner = expert / per_rank;
7699                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7700                if rank_inputs[owner].is_none() {
7701                    let peer_input = {
7702                        let root_input = rank_inputs[0]
7703                            .as_ref()
7704                            .ok_or("native EP lost its root input")?;
7705                        let engine = &self.ranks[owner];
7706                        let _main = engine.gpu.enter_main()?;
7707                        let mut peer_input = engine.uninit(experts.input_width)?;
7708                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7709                        peer_input
7710                    };
7711                    rank_inputs[owner] = Some(peer_input);
7712                }
7713
7714                let rank = &experts.ranks[owner];
7715                let engine = &self.ranks[owner];
7716                let owner_input = rank_inputs[owner]
7717                    .as_ref()
7718                    .ok_or("native EP owner input is absent after dispatch")?;
7719                let gate = run_resident_bank_expert_device(
7720                    engine,
7721                    &rank.gate,
7722                    local_expert,
7723                    owner_input,
7724                    1,
7725                )?;
7726                let up = run_resident_bank_expert_device(
7727                    engine,
7728                    &rank.up,
7729                    local_expert,
7730                    owner_input,
7731                    1,
7732                )?;
7733                let activated = {
7734                    let _main = engine.gpu.enter_main()?;
7735                    let mut activated = engine.uninit(experts.expert_width)?;
7736                    if let Some(limit) = activation_limit {
7737                        engine.silu_clamped_mul_host_expf(
7738                            &gate,
7739                            &up,
7740                            limit,
7741                            &mut activated,
7742                            experts.expert_width,
7743                        )?;
7744                    } else {
7745                        engine.silu_mul_host_expf(
7746                            &gate,
7747                            &up,
7748                            &mut activated,
7749                            experts.expert_width,
7750                        )?;
7751                    }
7752                    activated
7753                };
7754                let down = run_resident_bank_expert_device(
7755                    engine,
7756                    &rank.down,
7757                    local_expert,
7758                    &activated,
7759                    1,
7760                )?;
7761                let root_down = if owner == 0 {
7762                    down
7763                } else {
7764                    let _main = root.gpu.enter_main()?;
7765                    let mut root_down = root.uninit(experts.input_width)?;
7766                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7767                    // The peer copy runs on the root stream. Keep its remote source alive until
7768                    // the final root readback synchronizes that stream; otherwise async free can
7769                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
7770                    remote_down_keepalive.push(down);
7771                    root_down
7772                };
7773                let _main = root.gpu.enter_main()?;
7774                let mut destination = root_output.slice_mut(0..experts.input_width);
7775                root.axpy_host_into(
7776                    &root_down.slice(0..root_down.len()),
7777                    route_weights[pair],
7778                    &mut destination,
7779                    experts.input_width,
7780                )?;
7781            }
7782
7783            let _main = root.gpu.enter_main()?;
7784            let root_output = root.dtoh(&root_output)?;
7785            drop(remote_down_keepalive);
7786            output.extend(root_output);
7787        }
7788        Ok(output)
7789    }
7790}
7791
7792fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7793    if matrix.out_features % tp != 0 {
7794        return Err(format!(
7795            "column-parallel out_features {} is not divisible by TP={tp}",
7796            matrix.out_features
7797        ));
7798    }
7799    let local_out = matrix.out_features / tp;
7800    if local_out % FP8_BLOCK != 0 {
7801        return Err(format!(
7802            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
7803             E4M3 scale block"
7804        ));
7805    }
7806    Ok(())
7807}
7808
7809fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
7810    if !matches!(tp, 1 | 2 | 4 | 8) {
7811        return Err(format!(
7812            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7813        ));
7814    }
7815    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
7816        return Err(format!(
7817            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
7818        ));
7819    }
7820    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
7821    let local_out = out_features / tp;
7822    if local_out % canonical_rows != 0 {
7823        return Err(format!(
7824            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
7825             {canonical_rows}-row chunks"
7826        ));
7827    }
7828    Ok(canonical_rows)
7829}
7830
7831fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
7832    if !matches!(tp, 1 | 2 | 4 | 8) {
7833        return Err(format!(
7834            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7835        ));
7836    }
7837    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
7838        return Err(format!(
7839            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
7840        ));
7841    }
7842    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
7843    let local_in = in_features / tp;
7844    if local_in % canonical_cols != 0 {
7845        return Err(format!(
7846            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
7847             {canonical_cols}-column chunks"
7848        ));
7849    }
7850    Ok(canonical_cols)
7851}
7852
7853fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7854    if matrix.in_features % tp != 0 {
7855        return Err(format!(
7856            "row-parallel in_features {} is not divisible by TP={tp}",
7857            matrix.in_features
7858        ));
7859    }
7860    let local_in = matrix.in_features / tp;
7861    if local_in % FP8_BLOCK != 0 {
7862        return Err(format!(
7863            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
7864             E4M3 scale block"
7865        ));
7866    }
7867    Ok(())
7868}
7869
7870fn upload_rank(
7871    engine: &Engine,
7872    matrix: E4m3BlockMatrix<'_>,
7873) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
7874    let _main = engine.gpu.enter_main()?;
7875    matrix.validate()?;
7876    Ok(ResidentE4m3Rank {
7877        codes: engine.htod_bytes(matrix.codes)?,
7878        scales: engine.htod(matrix.scales)?,
7879        out_features: matrix.out_features,
7880        in_features: matrix.in_features,
7881    })
7882}
7883
7884fn upload_bf16_rank(
7885    engine: &Engine,
7886    matrix: Bf16Matrix<'_>,
7887    f32_mirror: bool,
7888) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
7889    let _main = engine.gpu.enter_main()?;
7890    matrix.validate()?;
7891    let bytes = engine.htod_bytes(matrix.bytes)?;
7892    let weight = if f32_mirror {
7893        let values = matrix
7894            .out_features
7895            .checked_mul(matrix.in_features)
7896            .ok_or("resident BF16 mirror element count overflow")?;
7897        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
7898    } else {
7899        ResidentBf16Weight::Bf16(bytes)
7900    };
7901    // MEMRA_STEP_TP_W8: encode the q8_0 decode mirror once, here, while the bf16 bytes are
7902    // already resident. Rows whose in_features is not a multiple of 32 have no q8_0 form and
7903    // simply keep the bf16 program (the decode arm checks for the mirror, never assumes it).
7904    let q8 = if crate::step_tp_w8_on() && matrix.in_features % 32 == 0 {
7905        if let ResidentBf16Weight::Bf16(bytes) = &weight {
7906            // Two steps, because the mmvq rp kernel does NOT read ggml-interleaved 34-byte
7907            // blocks: it reads a PLANAR mirror (all quants, then all half scales — the
7908            // q4_0/NVFP4 rp convention). The encoder writes the interleaved form and
7909            // `build_q8_rp4_raw` — the same kernel the GGUF loader uses — splits it into
7910            // planes. Skipping the split is what made the first W8 gate return zeros
7911            // (verify-prefill argmax=0, maxdiff=0.000e0).
7912            let row_bytes = Engine::q8_0_row_bytes(matrix.in_features);
7913            let mut interleaved = engine.alloc_u8_uninit(matrix.out_features * row_bytes)?;
7914            engine.encode_q8_0_from_bf16(
7915                bytes,
7916                &mut interleaved,
7917                matrix.in_features,
7918                matrix.out_features,
7919            )?;
7920            let mirror =
7921                engine.build_q8_rp4_raw(&interleaved, matrix.in_features, matrix.out_features)?;
7922            Some(mirror)
7923        } else {
7924            None
7925        }
7926    } else {
7927        None
7928    };
7929    Ok(ResidentBf16Rank {
7930        weight,
7931        out_features: matrix.out_features,
7932        in_features: matrix.in_features,
7933        q8,
7934    })
7935}
7936
7937fn upload_expert_bank_rank(
7938    engine: &Engine,
7939    bank: E4m3ExpertBank<'_>,
7940    expert_range: Range<usize>,
7941) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7942    let _main = engine.gpu.enter_main()?;
7943    bank.validate()?;
7944    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
7945        return Err(format!(
7946            "invalid EP expert range {expert_range:?} for {} experts",
7947            bank.expert_count
7948        )
7949        .into());
7950    }
7951    let code_stride = bank.out_features * bank.in_features;
7952    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
7953    Ok(ResidentE4m3ExpertBankRank {
7954        codes: engine.htod_bytes(
7955            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
7956        )?,
7957        scales: engine.htod(
7958            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
7959        )?,
7960        expert_range,
7961        out_features: bank.out_features,
7962        in_features: bank.in_features,
7963        code_stride,
7964        scale_stride,
7965        k_blocks: None,
7966    })
7967}
7968
7969fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7970    if bank.out_features % tp != 0 {
7971        return Err(format!(
7972            "TP expert output width {} is not divisible by TP={tp}",
7973            bank.out_features
7974        ));
7975    }
7976    let local_out = bank.out_features / tp;
7977    if local_out % FP8_BLOCK != 0 {
7978        return Err(format!(
7979            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
7980        ));
7981    }
7982    Ok(())
7983}
7984
7985fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7986    if bank.in_features % tp != 0 {
7987        return Err(format!(
7988            "TP expert input width {} is not divisible by TP={tp}",
7989            bank.in_features
7990        ));
7991    }
7992    let local_in = bank.in_features / tp;
7993    if local_in % FP8_BLOCK != 0 {
7994        return Err(format!(
7995            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
7996        ));
7997    }
7998    Ok(())
7999}
8000
8001fn upload_column_bank_rank(
8002    engine: &Engine,
8003    bank: E4m3ExpertBank<'_>,
8004    tp: usize,
8005    rank: usize,
8006) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8007    let _main = engine.gpu.enter_main()?;
8008    let packed = pack_column_bank_rank(bank, tp, rank)?;
8009    Ok(ResidentE4m3ExpertBankRank {
8010        codes: engine.htod_bytes(&packed.codes)?,
8011        scales: engine.htod(&packed.scales)?,
8012        expert_range: packed.expert_range,
8013        out_features: packed.out_features,
8014        in_features: packed.in_features,
8015        code_stride: packed.code_stride,
8016        scale_stride: packed.scale_stride,
8017        k_blocks: packed.k_blocks,
8018    })
8019}
8020
8021fn pack_column_bank_rank(
8022    bank: E4m3ExpertBank<'_>,
8023    tp: usize,
8024    rank: usize,
8025) -> Result<PackedE4m3ExpertBankRank, String> {
8026    bank.validate()?;
8027    validate_column_bank_shape(bank, tp)?;
8028    if rank >= tp {
8029        return Err(format!("TP rank {rank} outside 0..{tp}"));
8030    }
8031    let local_out = bank.out_features / tp;
8032    let full_code_stride = bank.out_features * bank.in_features;
8033    let local_code_stride = local_out * bank.in_features;
8034    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
8035    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
8036    let local_scale_rows = local_out / FP8_BLOCK;
8037    let local_scale_stride = local_scale_rows * scale_cols;
8038    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
8039    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
8040    let row_start = rank * local_out;
8041    let scale_row_start = rank * local_scale_rows;
8042    for expert in 0..bank.expert_count {
8043        let code_start = expert * full_code_stride + row_start * bank.in_features;
8044        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
8045        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
8046        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
8047    }
8048    Ok(PackedE4m3ExpertBankRank {
8049        codes,
8050        scales,
8051        expert_range: 0..bank.expert_count,
8052        out_features: local_out,
8053        in_features: bank.in_features,
8054        code_stride: local_code_stride,
8055        scale_stride: local_scale_stride,
8056        k_blocks: None,
8057    })
8058}
8059
8060fn upload_row_bank_rank(
8061    engine: &Engine,
8062    bank: E4m3ExpertBank<'_>,
8063    tp: usize,
8064    rank: usize,
8065) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8066    let _main = engine.gpu.enter_main()?;
8067    let packed = pack_row_bank_rank(bank, tp, rank)?;
8068    Ok(ResidentE4m3ExpertBankRank {
8069        codes: engine.htod_bytes(&packed.codes)?,
8070        scales: engine.htod(&packed.scales)?,
8071        expert_range: packed.expert_range,
8072        out_features: packed.out_features,
8073        in_features: packed.in_features,
8074        code_stride: packed.code_stride,
8075        scale_stride: packed.scale_stride,
8076        k_blocks: packed.k_blocks,
8077    })
8078}
8079
8080fn pack_row_bank_rank(
8081    bank: E4m3ExpertBank<'_>,
8082    tp: usize,
8083    rank: usize,
8084) -> Result<PackedE4m3ExpertBankRank, String> {
8085    bank.validate()?;
8086    validate_row_bank_shape(bank, tp)?;
8087    if rank >= tp {
8088        return Err(format!("TP rank {rank} outside 0..{tp}"));
8089    }
8090    let local_in = bank.in_features / tp;
8091    let full_code_stride = bank.out_features * bank.in_features;
8092    let local_code_stride = bank.out_features * local_in;
8093    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
8094    let local_scale_cols = local_in / FP8_BLOCK;
8095    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
8096    let full_scale_stride = scale_rows * full_scale_cols;
8097    let local_scale_stride = scale_rows * local_scale_cols;
8098    let global_block_start = rank * local_scale_cols;
8099    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
8100    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
8101    for expert in 0..bank.expert_count {
8102        let expert_code_start = expert * full_code_stride;
8103        let expert_scale_start = expert * full_scale_stride;
8104        for local_block in 0..local_scale_cols {
8105            let global_block = global_block_start + local_block;
8106            let column_start = global_block * FP8_BLOCK;
8107            for row in 0..bank.out_features {
8108                let start = expert_code_start + row * bank.in_features + column_start;
8109                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
8110            }
8111            for row in 0..scale_rows {
8112                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
8113            }
8114        }
8115    }
8116    Ok(PackedE4m3ExpertBankRank {
8117        codes,
8118        scales,
8119        expert_range: 0..bank.expert_count,
8120        out_features: bank.out_features,
8121        in_features: local_in,
8122        code_stride: local_code_stride,
8123        scale_stride: local_scale_stride,
8124        k_blocks: Some(local_scale_cols),
8125    })
8126}
8127
8128fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
8129    if engines.len() != ranks.len() {
8130        return Err(format!(
8131            "resident TP rank count {} != runtime rank count {}",
8132            ranks.len(),
8133            engines.len()
8134        ));
8135    }
8136    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
8137        let device = engine.ctx().ordinal();
8138        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
8139            return Err(format!(
8140                "resident TP rank {rank} is not owned by runtime device {device}"
8141            ));
8142        }
8143    }
8144    Ok(())
8145}
8146
8147fn validate_tp_bank_residency(
8148    engines: &[Engine],
8149    experts: &ResidentTpExpertBank,
8150) -> Result<(), String> {
8151    if engines.len() != experts.gate.len()
8152        || engines.len() != experts.up.len()
8153        || engines.len() != experts.down.len()
8154    {
8155        return Err(format!(
8156            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
8157            experts.gate.len(),
8158            experts.up.len(),
8159            experts.down.len(),
8160            engines.len()
8161        ));
8162    }
8163    for (rank, engine) in engines.iter().enumerate() {
8164        let device = engine.ctx().ordinal();
8165        for (projection, bank) in [
8166            ("gate", &experts.gate[rank]),
8167            ("up", &experts.up[rank]),
8168            ("down", &experts.down[rank]),
8169        ] {
8170            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
8171                return Err(format!(
8172                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
8173                     {device}"
8174                ));
8175            }
8176        }
8177    }
8178    Ok(())
8179}
8180
8181fn validate_ep_residency(
8182    engines: &[Engine],
8183    experts: &ResidentExpertParallel,
8184) -> Result<(), String> {
8185    if engines.len() != experts.ranks.len() {
8186        return Err(format!(
8187            "resident EP rank count {} != runtime rank count {}",
8188            experts.ranks.len(),
8189            engines.len()
8190        ));
8191    }
8192    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
8193        let device = engine.ctx().ordinal();
8194        for (projection, bank) in [
8195            ("gate", &resident.gate),
8196            ("up", &resident.up),
8197            ("down", &resident.down),
8198        ] {
8199            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
8200                return Err(format!(
8201                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
8202                     {device}"
8203                ));
8204            }
8205        }
8206    }
8207    Ok(())
8208}
8209
8210fn run_rank(
8211    engine: &Engine,
8212    matrix: E4m3BlockMatrix<'_>,
8213    activations: &[f32],
8214    tokens: usize,
8215) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8216    let _main = engine.gpu.enter_main()?;
8217    let codes = engine.htod_bytes(matrix.codes)?;
8218    let scales = engine.htod(matrix.scales)?;
8219    let activations = engine.htod(activations)?;
8220    let output = engine.qmatvec_mmq_fp8_blk(
8221        &codes,
8222        &scales,
8223        &activations,
8224        tokens,
8225        matrix.in_features,
8226        matrix.out_features,
8227    )?;
8228    engine.dtoh(&output)
8229}
8230
8231fn run_resident_rank(
8232    engine: &Engine,
8233    matrix: &ResidentE4m3Rank,
8234    activations: &[f32],
8235    tokens: usize,
8236) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8237    let _main = engine.gpu.enter_main()?;
8238    let activations = engine.htod(activations)?;
8239    let output = engine.qmatvec_mmq_fp8_blk(
8240        &matrix.codes,
8241        &matrix.scales,
8242        &activations,
8243        tokens,
8244        matrix.in_features,
8245        matrix.out_features,
8246    )?;
8247    engine.dtoh(&output)
8248}
8249
8250fn run_resident_bf16_rank(
8251    engine: &Engine,
8252    matrix: &ResidentBf16Rank,
8253    activations: &[f32],
8254    tokens: usize,
8255    canonical_chunk_rows: Option<usize>,
8256) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8257    let _main = engine.gpu.enter_main()?;
8258    let activations = engine.htod(activations)?;
8259    let output = run_resident_bf16_rank_device(
8260        engine,
8261        matrix,
8262        &activations,
8263        tokens,
8264        canonical_chunk_rows,
8265        false,
8266    )?;
8267    engine.dtoh(&output)
8268}
8269
8270fn run_resident_bf16_rank_device(
8271    engine: &Engine,
8272    matrix: &ResidentBf16Rank,
8273    activations: &CudaSlice<f32>,
8274    tokens: usize,
8275    canonical_chunk_rows: Option<usize>,
8276    strided_chunk_output: bool,
8277) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8278    let _main = engine.gpu.enter_main()?;
8279    if activations.ordinal() != engine.ctx().ordinal() {
8280        return Err(format!(
8281            "resident BF16 activation device {} != rank device {}",
8282            activations.ordinal(),
8283            engine.ctx().ordinal()
8284        )
8285        .into());
8286    }
8287    if activations.len() != tokens * matrix.in_features {
8288        return Err(format!(
8289            "resident BF16 activation count {} != {tokens}x{}",
8290            activations.len(),
8291            matrix.in_features
8292        )
8293        .into());
8294    }
8295    match (&matrix.weight, canonical_chunk_rows) {
8296        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
8297            .linear_bf16_resident_canonical_rows(
8298                activations,
8299                bytes,
8300                tokens,
8301                matrix.in_features,
8302                matrix.out_features,
8303                rows,
8304            ),
8305        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
8306            activations,
8307            bytes,
8308            tokens,
8309            matrix.in_features,
8310            matrix.out_features,
8311        ),
8312        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
8313            .linear_f32_resident_canonical_rows_strided(
8314                activations,
8315                values,
8316                tokens,
8317                matrix.in_features,
8318                matrix.out_features,
8319                rows,
8320            ),
8321        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
8322            activations,
8323            values,
8324            tokens,
8325            matrix.in_features,
8326            matrix.out_features,
8327            rows,
8328        ),
8329        (ResidentBf16Weight::F32(values), None) => engine.linear(
8330            activations,
8331            values,
8332            tokens,
8333            matrix.in_features,
8334            matrix.out_features,
8335        ),
8336    }
8337}
8338
8339fn validate_resident_bf16_ranks(
8340    engines: &[Engine],
8341    ranks: &[ResidentBf16Rank],
8342) -> Result<(), String> {
8343    if engines.len() != ranks.len() {
8344        return Err(format!(
8345            "resident BF16 TP rank count {} != runtime rank count {}",
8346            ranks.len(),
8347            engines.len(),
8348        ));
8349    }
8350    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
8351        let device = engine.ctx().ordinal();
8352        if matrix.weight.ordinal() != device {
8353            return Err(format!(
8354                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
8355            ));
8356        }
8357    }
8358    Ok(())
8359}
8360
8361fn validate_step_bf16_row_residency(
8362    engines: &[Engine],
8363    matrix: &ResidentStepBf16RowParallel,
8364) -> Result<(), String> {
8365    if engines.len() != matrix.ranks.len() {
8366        return Err(format!(
8367            "resident Step BF16 row rank count {} != runtime rank count {}",
8368            matrix.ranks.len(),
8369            engines.len(),
8370        ));
8371    }
8372    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
8373    if matrix.canonical_chunk_cols != canonical_cols {
8374        return Err(format!(
8375            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
8376            matrix.canonical_chunk_cols
8377        ));
8378    }
8379    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
8380    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
8381        if blocks.len() != blocks_per_rank {
8382            return Err(format!(
8383                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
8384                blocks.len()
8385            ));
8386        }
8387        let device = engine.ctx().ordinal();
8388        for (block, resident) in blocks.iter().enumerate() {
8389            if resident.weight.ordinal() != device
8390                || resident.in_features != canonical_cols
8391                || resident.out_features != matrix.out_features
8392            {
8393                return Err(format!(
8394                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
8395                     device or geometry"
8396                ));
8397            }
8398        }
8399    }
8400    Ok(())
8401}
8402
8403fn validate_replicated_device_rows(
8404    engines: &[Engine],
8405    rows: &ResidentReplicatedDeviceRows,
8406) -> Result<(), String> {
8407    let rank_lengths = rows
8408        .ranks
8409        .iter()
8410        .map(|rank_rows| rank_rows.len())
8411        .collect::<Vec<_>>();
8412    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
8413    if rows
8414        .ranks
8415        .iter()
8416        .zip(engines)
8417        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
8418    {
8419        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
8420    }
8421    Ok(())
8422}
8423
8424fn replicated_device_row_values(
8425    tokens: usize,
8426    width: usize,
8427    expected_ranks: usize,
8428    rank_lengths: &[usize],
8429) -> Result<usize, String> {
8430    let values = tokens
8431        .checked_mul(width)
8432        .ok_or("replicated device row size overflow")?;
8433    if tokens == 0
8434        || width == 0
8435        || expected_ranks == 0
8436        || rank_lengths.len() != expected_ranks
8437        || rank_lengths.iter().any(|&rank_len| rank_len != values)
8438    {
8439        return Err(format!(
8440            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
8441            tokens,
8442            width,
8443            rank_lengths.len(),
8444            expected_ranks
8445        ));
8446    }
8447    Ok(values)
8448}
8449
8450fn replicated_device_row_source_values(
8451    tokens: usize,
8452    width: usize,
8453    source_len: usize,
8454    source_device: usize,
8455    root_device: usize,
8456) -> Result<usize, String> {
8457    let values = tokens
8458        .checked_mul(width)
8459        .ok_or("replicated device row size overflow")?;
8460    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
8461        return Err(format!(
8462            "replicated device row source has inconsistent geometry/device \
8463             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
8464        ));
8465    }
8466    Ok(values)
8467}
8468
8469fn bf16_column_shard(
8470    matrix: Bf16Matrix<'_>,
8471    tp: usize,
8472    rank: usize,
8473) -> Result<Bf16Matrix<'_>, String> {
8474    matrix.validate()?;
8475    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
8476        return Err(format!(
8477            "invalid BF16 column shard out={} TP={tp} rank={rank}",
8478            matrix.out_features
8479        ));
8480    }
8481    let local_out = matrix.out_features / tp;
8482    let row_bytes = matrix.in_features * 2;
8483    let start = rank * local_out * row_bytes;
8484    Ok(Bf16Matrix {
8485        bytes: &matrix.bytes[start..start + local_out * row_bytes],
8486        out_features: local_out,
8487        in_features: matrix.in_features,
8488    })
8489}
8490
8491fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
8492    matrix.validate()?;
8493    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
8494        return Err(format!(
8495            "invalid BF16 row shard in={} TP={tp} rank={rank}",
8496            matrix.in_features
8497        ));
8498    }
8499    let local_in = matrix.in_features / tp;
8500    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
8501    for row in 0..matrix.out_features {
8502        let start = (row * matrix.in_features + rank * local_in) * 2;
8503        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
8504    }
8505    Ok(bytes)
8506}
8507
8508fn bf16_row_block(
8509    matrix: Bf16Matrix<'_>,
8510    col_start: usize,
8511    block_cols: usize,
8512) -> Result<Vec<u8>, String> {
8513    matrix.validate()?;
8514    let col_end = col_start
8515        .checked_add(block_cols)
8516        .ok_or("BF16 row block column overflow")?;
8517    if block_cols == 0 || col_end > matrix.in_features {
8518        return Err(format!(
8519            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
8520            matrix.in_features
8521        ));
8522    }
8523    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
8524    for row in 0..matrix.out_features {
8525        let start = (row * matrix.in_features + col_start) * 2;
8526        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
8527    }
8528    Ok(bytes)
8529}
8530
8531fn run_resident_bank_expert(
8532    engine: &Engine,
8533    bank: &ResidentE4m3ExpertBankRank,
8534    local_expert: usize,
8535    activations: &[f32],
8536    tokens: usize,
8537) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8538    let _main = engine.gpu.enter_main()?;
8539    if bank.k_blocks.is_some() {
8540        return Err("block-major TP row bank requires canonical block execution".into());
8541    }
8542    let local_count = bank.expert_range.end - bank.expert_range.start;
8543    if local_expert >= local_count {
8544        return Err(format!(
8545            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
8546            bank.expert_range
8547        )
8548        .into());
8549    }
8550    validate_activations(activations, tokens, bank.in_features)?;
8551    let activations = engine.htod(activations)?;
8552    let weight = bank
8553        .codes
8554        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8555    let scales = bank
8556        .scales
8557        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8558    let input = activations.slice(0..activations.len());
8559    let output = engine.qmatvec_mmq_fp8_blk_view(
8560        &weight,
8561        &scales,
8562        &input,
8563        tokens,
8564        bank.in_features,
8565        bank.out_features,
8566    )?;
8567    engine.dtoh(&output)
8568}
8569
8570fn run_resident_bank_expert_block(
8571    engine: &Engine,
8572    bank: &ResidentE4m3ExpertBankRank,
8573    local_expert: usize,
8574    block: usize,
8575    activations: &[f32],
8576) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8577    let _main = engine.gpu.enter_main()?;
8578    let local_count = bank.expert_range.end - bank.expert_range.start;
8579    if local_expert >= local_count {
8580        return Err(format!(
8581            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8582            bank.expert_range
8583        )
8584        .into());
8585    }
8586    let blocks = bank
8587        .k_blocks
8588        .ok_or("TP row bank is not packed in native K-block order")?;
8589    if block >= blocks {
8590        return Err(format!("TP row block {block} outside 0..{blocks}").into());
8591    }
8592    validate_activations(activations, 1, FP8_BLOCK)?;
8593    let block_code_stride = bank.out_features * FP8_BLOCK;
8594    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8595    if bank.in_features != blocks * FP8_BLOCK
8596        || bank.code_stride != blocks * block_code_stride
8597        || bank.scale_stride != blocks * block_scale_stride
8598    {
8599        return Err("TP row bank block-major geometry is inconsistent".into());
8600    }
8601
8602    let expert_code_start = local_expert * bank.code_stride;
8603    let expert_scale_start = local_expert * bank.scale_stride;
8604    let weight = bank.codes.slice(
8605        expert_code_start + block * block_code_stride
8606            ..expert_code_start + (block + 1) * block_code_stride,
8607    );
8608    let scales = bank.scales.slice(
8609        expert_scale_start + block * block_scale_stride
8610            ..expert_scale_start + (block + 1) * block_scale_stride,
8611    );
8612    let activations = engine.htod(activations)?;
8613    let input = activations.slice(0..activations.len());
8614    let output = engine.qmatvec_mmq_fp8_blk_view(
8615        &weight,
8616        &scales,
8617        &input,
8618        1,
8619        FP8_BLOCK,
8620        bank.out_features,
8621    )?;
8622    engine.dtoh(&output)
8623}
8624
8625fn run_resident_bank_expert_device(
8626    engine: &Engine,
8627    bank: &ResidentE4m3ExpertBankRank,
8628    local_expert: usize,
8629    activations: &CudaSlice<f32>,
8630    tokens: usize,
8631) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8632    let _main = engine.gpu.enter_main()?;
8633    if bank.k_blocks.is_some() {
8634        return Err("block-major TP row bank requires canonical block execution".into());
8635    }
8636    let local_count = bank.expert_range.end - bank.expert_range.start;
8637    if local_expert >= local_count {
8638        return Err(format!(
8639            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8640            bank.expert_range
8641        )
8642        .into());
8643    }
8644    let expected = tokens
8645        .checked_mul(bank.in_features)
8646        .ok_or("native TP activation size overflow")?;
8647    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
8648        return Err(format!(
8649            "native TP activation len/device {}/{} != expected {expected}/{}",
8650            activations.len(),
8651            activations.ordinal(),
8652            engine.ctx().ordinal()
8653        )
8654        .into());
8655    }
8656    let weight = bank
8657        .codes
8658        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8659    let scales = bank
8660        .scales
8661        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8662    let input = activations.slice(0..activations.len());
8663    engine.qmatvec_mmq_fp8_blk_view(
8664        &weight,
8665        &scales,
8666        &input,
8667        tokens,
8668        bank.in_features,
8669        bank.out_features,
8670    )
8671}
8672
8673fn run_resident_bank_expert_block_device(
8674    engine: &Engine,
8675    bank: &ResidentE4m3ExpertBankRank,
8676    local_expert: usize,
8677    block: usize,
8678    activations: &cudarc::driver::CudaView<'_, f32>,
8679) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8680    let _main = engine.gpu.enter_main()?;
8681    let local_count = bank.expert_range.end - bank.expert_range.start;
8682    if local_expert >= local_count {
8683        return Err(format!(
8684            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8685            bank.expert_range
8686        )
8687        .into());
8688    }
8689    let blocks = bank
8690        .k_blocks
8691        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
8692    if block >= blocks {
8693        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
8694    }
8695    let activation_device = activations.stream().context().ordinal();
8696    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
8697        return Err(format!(
8698            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
8699            activations.len(),
8700            activation_device,
8701            engine.ctx().ordinal()
8702        )
8703        .into());
8704    }
8705    let block_code_stride = bank.out_features * FP8_BLOCK;
8706    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8707    if bank.in_features != blocks * FP8_BLOCK
8708        || bank.code_stride != blocks * block_code_stride
8709        || bank.scale_stride != blocks * block_scale_stride
8710    {
8711        return Err("native TP row bank block-major geometry is inconsistent".into());
8712    }
8713    let expert_code_start = local_expert * bank.code_stride;
8714    let expert_scale_start = local_expert * bank.scale_stride;
8715    let weight = bank.codes.slice(
8716        expert_code_start + block * block_code_stride
8717            ..expert_code_start + (block + 1) * block_code_stride,
8718    );
8719    let scales = bank.scales.slice(
8720        expert_scale_start + block * block_scale_stride
8721            ..expert_scale_start + (block + 1) * block_scale_stride,
8722    );
8723    engine.qmatvec_mmq_fp8_blk_view(
8724        &weight,
8725        &scales,
8726        activations,
8727        1,
8728        FP8_BLOCK,
8729        bank.out_features,
8730    )
8731}
8732
8733fn configure_native_p2p(
8734    ranks: &[Engine],
8735    devices: &[usize],
8736) -> Result<(), Box<dyn std::error::Error>> {
8737    if ranks.len() != devices.len() || ranks.len() < 2 {
8738        return Err("native TP P2P setup requires matching multi-rank devices".into());
8739    }
8740    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
8741        if engine.ctx().ordinal() != device {
8742            return Err(format!(
8743                "native TP rank {rank} context device {} != requested device {device}",
8744                engine.ctx().ordinal()
8745            )
8746            .into());
8747        }
8748    }
8749
8750    for src in 0..ranks.len() {
8751        for dst in 0..ranks.len() {
8752            if src == dst {
8753                continue;
8754            }
8755            let mut can_access = 0;
8756            unsafe {
8757                cudarc::driver::sys::cuDeviceCanAccessPeer(
8758                    &mut can_access,
8759                    ranks[src].ctx().cu_device(),
8760                    ranks[dst].ctx().cu_device(),
8761                )
8762                .result()?;
8763            }
8764            if can_access == 0 {
8765                return Err(format!(
8766                    "native TP requires P2P, but dev{} cannot access dev{}",
8767                    devices[src], devices[dst]
8768                )
8769                .into());
8770            }
8771            ranks[src].ctx().bind_to_thread()?;
8772            let rc =
8773                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
8774            use cudarc::driver::sys::cudaError_enum as E;
8775            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
8776                return Err(format!(
8777                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
8778                    devices[src], devices[dst]
8779                )
8780                .into());
8781            }
8782        }
8783    }
8784
8785    for &owner in devices {
8786        for &accessor in devices {
8787            if owner == accessor {
8788                continue;
8789            }
8790            let device = cudarc::driver::result::device::get(owner as i32)?;
8791            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
8792            unsafe {
8793                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
8794            }
8795            let desc = cudarc::driver::sys::CUmemAccessDesc {
8796                location: cudarc::driver::sys::CUmemLocation {
8797                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
8798                    id: accessor as i32,
8799                },
8800                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
8801            };
8802            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
8803            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
8804                return Err(format!(
8805                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
8806                     {rc:?}"
8807                )
8808                .into());
8809            }
8810        }
8811    }
8812
8813    for src in 0..ranks.len() {
8814        for dst in 0..ranks.len() {
8815            if src == dst {
8816                continue;
8817            }
8818            for &words in NATIVE_P2P_PROBE_WORDS {
8819                let expected = (0..words)
8820                    .map(|index| {
8821                        (index as u32)
8822                            .wrapping_mul(0x9e37_79b9)
8823                            .wrapping_add(((src as u32) << 16) | dst as u32)
8824                    })
8825                    .collect::<Vec<_>>();
8826                let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
8827                let source = ranks[src].htod_u32_v(&expected)?;
8828                let mut destination = ranks[dst].htod_u32_v(&poison)?;
8829                ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
8830                let actual = ranks[dst].dtoh_u32(&destination)?;
8831                if actual != expected {
8832                    let mismatches = actual
8833                        .iter()
8834                        .zip(&expected)
8835                        .filter(|(actual, expected)| actual != expected)
8836                        .count();
8837                    return Err(format!(
8838                        "native TP peer probe dev{}->dev{} failed at {} bytes: \
8839                         {mismatches}/{} words differ",
8840                        devices[src],
8841                        devices[dst],
8842                        words * std::mem::size_of::<u32>(),
8843                        expected.len()
8844                    )
8845                    .into());
8846                }
8847            }
8848        }
8849    }
8850    ranks[0].ctx().bind_to_thread()?;
8851    eprintln!(
8852        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
8853         directions={} byte_ladder={:?} mismatches=0",
8854        ranks.len() * (ranks.len() - 1),
8855        NATIVE_P2P_PROBE_WORDS
8856            .iter()
8857            .map(|words| words * std::mem::size_of::<u32>())
8858            .collect::<Vec<_>>(),
8859    );
8860    Ok(())
8861}
8862
8863fn validate_activations(
8864    activations: &[f32],
8865    tokens: usize,
8866    in_features: usize,
8867) -> Result<(), String> {
8868    let expected = tokens
8869        .checked_mul(in_features)
8870        .ok_or_else(|| "activation size overflow".to_string())?;
8871    if activations.len() != expected {
8872        return Err(format!(
8873            "activation count {} != {tokens}x{in_features} ({expected})",
8874            activations.len()
8875        ));
8876    }
8877    if !activations.iter().all(|value| value.is_finite()) {
8878        return Err("activations contain a non-finite value".to_string());
8879    }
8880    Ok(())
8881}
8882
8883fn column_shard(
8884    matrix: E4m3BlockMatrix<'_>,
8885    tp: usize,
8886    rank: usize,
8887) -> Result<E4m3BlockMatrix<'_>, String> {
8888    let local_out = matrix.out_features / tp;
8889    let row_start = rank * local_out;
8890    let code_start = row_start * matrix.in_features;
8891    let code_end = code_start + local_out * matrix.in_features;
8892    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8893    let local_scale_rows = local_out / FP8_BLOCK;
8894    let scale_start = rank * local_scale_rows * scale_cols;
8895    let scale_end = scale_start + local_scale_rows * scale_cols;
8896    Ok(E4m3BlockMatrix {
8897        codes: &matrix.codes[code_start..code_end],
8898        scales: &matrix.scales[scale_start..scale_end],
8899        out_features: local_out,
8900        in_features: matrix.in_features,
8901    })
8902}
8903
8904fn row_shard(
8905    matrix: E4m3BlockMatrix<'_>,
8906    tp: usize,
8907    rank: usize,
8908) -> Result<(Vec<u8>, Vec<f32>), String> {
8909    let local_in = matrix.in_features / tp;
8910    let col_start = rank * local_in;
8911    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
8912    for row in 0..matrix.out_features {
8913        let start = row * matrix.in_features + col_start;
8914        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
8915    }
8916
8917    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
8918    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8919    let local_scale_cols = local_in / FP8_BLOCK;
8920    let scale_col_start = rank * local_scale_cols;
8921    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
8922    for row in 0..scale_rows {
8923        let start = row * scale_cols + scale_col_start;
8924        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
8925    }
8926    Ok((codes, scales))
8927}
8928
8929fn activation_shard(
8930    activations: &[f32],
8931    tokens: usize,
8932    in_features: usize,
8933    tp: usize,
8934    rank: usize,
8935) -> Vec<f32> {
8936    let local_in = in_features / tp;
8937    let col_start = rank * local_in;
8938    let mut shard = Vec::with_capacity(tokens * local_in);
8939    for token in 0..tokens {
8940        let start = token * in_features + col_start;
8941        shard.extend_from_slice(&activations[start..start + local_in]);
8942    }
8943    shard
8944}
8945
8946// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
8947//
8948// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
8949// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
8950// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
8951// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
8952// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
8953// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
8954//
8955// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
8956// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
8957// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
8958// TP1-vs-TP2 bit gate. Every entry point below follows this order.
8959//
8960// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
8961// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
8962// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
8963
8964/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
8965#[derive(Clone, Copy)]
8966pub struct Nvfp4BlockMatrix<'a> {
8967    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
8968    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
8969    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
8970    pub out_features: usize,
8971    pub in_features: usize,
8972}
8973
8974impl Nvfp4BlockMatrix<'_> {
8975    pub fn validate(&self) -> Result<(), String> {
8976        if self.in_features == 0 || self.out_features == 0 {
8977            return Err("NVFP4 matrix has a zero dimension".to_string());
8978        }
8979        if self.in_features % 64 != 0 {
8980            return Err(format!(
8981                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
8982                self.in_features
8983            ));
8984        }
8985        if self.codes.len() != self.out_features * self.in_features / 2 {
8986            return Err(format!(
8987                "NVFP4 code bytes {} != {}x{}/2",
8988                self.codes.len(),
8989                self.out_features,
8990                self.in_features
8991            ));
8992        }
8993        if self.scales.len() != self.out_features * self.in_features / 16 {
8994            return Err(format!(
8995                "NVFP4 scale bytes {} != {}x{}/16",
8996                self.scales.len(),
8997                self.out_features,
8998                self.in_features
8999            ));
9000        }
9001        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
9002            return Err(format!(
9003                "NVFP4 macro scale {} is not finite-positive",
9004                self.macro_scale
9005            ));
9006        }
9007        Ok(())
9008    }
9009}
9010
9011/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
9012#[derive(Clone, Copy)]
9013pub struct Nvfp4ExpertBank<'a> {
9014    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
9015    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
9016    pub macros: &'a [f32], // [expert_count] weight_scale_2
9017    pub expert_count: usize,
9018    pub out_features: usize,
9019    pub in_features: usize,
9020}
9021
9022impl Nvfp4ExpertBank<'_> {
9023    pub fn validate(&self) -> Result<(), String> {
9024        if self.expert_count == 0 {
9025            return Err("NVFP4 expert bank is empty".to_string());
9026        }
9027        if self.macros.len() != self.expert_count {
9028            return Err(format!(
9029                "NVFP4 bank macros {} != expert count {}",
9030                self.macros.len(),
9031                self.expert_count
9032            ));
9033        }
9034        self.expert(0).map(|_| ())
9035    }
9036
9037    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
9038        if expert >= self.expert_count {
9039            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
9040        }
9041        let code_stride = self.out_features * self.in_features / 2;
9042        let scale_stride = self.out_features * self.in_features / 16;
9043        if self.codes.len() != self.expert_count * code_stride
9044            || self.scales.len() != self.expert_count * scale_stride
9045        {
9046            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
9047        }
9048        let matrix = Nvfp4BlockMatrix {
9049            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
9050            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
9051            macro_scale: self.macros[expert],
9052            out_features: self.out_features,
9053            in_features: self.in_features,
9054        };
9055        matrix.validate()?;
9056        Ok(matrix)
9057    }
9058}
9059
9060/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
9061pub struct ResidentNvfp4Rank {
9062    blocks: crate::CudaSlice<u8>,
9063    macro_scale: f32,
9064    out_features: usize,
9065    in_features: usize,
9066    row_bytes: usize,
9067}
9068
9069pub struct ResidentNvfp4ColumnParallel {
9070    ranks: Vec<ResidentNvfp4Rank>,
9071    pub out_features: usize,
9072    pub in_features: usize,
9073}
9074
9075pub struct ResidentNvfp4RowParallel {
9076    ranks: Vec<ResidentNvfp4Rank>,
9077    pub out_features: usize,
9078    pub in_features: usize,
9079}
9080
9081pub struct ResidentTpNvfp4Expert {
9082    gate: ResidentNvfp4ColumnParallel,
9083    up: ResidentNvfp4ColumnParallel,
9084    down: ResidentNvfp4RowParallel,
9085    pub input_width: usize,
9086    pub expert_width: usize,
9087}
9088
9089/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
9090/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
9091/// later perf rung, mirroring the FP8 bank's history).
9092pub struct ResidentNvfp4ColumnBankRank {
9093    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
9094    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
9095    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
9096    bank: crate::CudaSlice<u8>,
9097    expert_bytes: usize,
9098    local_out: usize,
9099    in_features: usize,
9100    row_bytes: usize,
9101}
9102
9103impl ResidentNvfp4ColumnBankRank {
9104    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
9105        self.bank
9106            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
9107    }
9108}
9109
9110/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
9111/// as exactly this many input-column windows summed in shard order, at every world size: a
9112/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
9113/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
9114/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
9115pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
9116
9117pub struct ResidentNvfp4RowBankRank {
9118    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
9119    bank: crate::CudaSlice<u8>,
9120    expert_bytes: usize,
9121    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
9122    out_features: usize,
9123    local_in: usize,
9124    row_bytes: usize,
9125}
9126
9127impl ResidentNvfp4RowBankRank {
9128    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
9129        self.bank
9130            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
9131    }
9132}
9133
9134impl ResidentNvfp4TensorParallel {
9135    pub(crate) fn device_workspace_handle(
9136        &self,
9137    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
9138        &self.device_workspace
9139    }
9140}
9141
9142pub struct ResidentNvfp4TensorParallel {
9143    gate: Vec<ResidentNvfp4ColumnBankRank>,
9144    up: Vec<ResidentNvfp4ColumnBankRank>,
9145    down: Vec<ResidentNvfp4RowBankRank>,
9146    macros_gate: Vec<f32>,
9147    macros_up: Vec<f32>,
9148    macros_down: Vec<f32>,
9149    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
9150    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
9151    /// into the route-weight axpy scalar.
9152    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
9153    macros_up_dev: Vec<crate::CudaSlice<f32>>,
9154    macros_down_dev: Vec<crate::CudaSlice<f32>>,
9155    pub expert_count: usize,
9156    pub input_width: usize,
9157    pub expert_width: usize,
9158    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
9159    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
9160    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
9161    /// Grouped-prime per-rank slot-major pointer tables (gate/up/down x n_expert), built once.
9162    /// The banks are resident and never move, so rebuilding + re-uploading 3*n_expert u64s per
9163    /// rank per LAYER was pure per-call host churn on the prime path.
9164    prime_tables: std::sync::Mutex<Vec<crate::CudaSlice<u64>>>,
9165    /// Lazily-built spec-verify t=2 workspace (MEMRA_TCOL_FFN): the two-column routed
9166    /// sweep's slabs and events, kept apart from the serving workspace so the verify walk
9167    /// never perturbs serving state.
9168    t2_workspace: std::sync::Mutex<Option<Nvfp4T2Workspace>>,
9169    /// MEMRA_STEP_NVFP4_EP2: the rank banks above hold WHOLE experts (owner = id & 1,
9170    /// slot = id >> 1) at full width instead of TP shards. Consumers must branch on this;
9171    /// shard-semantics paths refuse loudly.
9172    pub(crate) ep2: bool,
9173}
9174
9175/// Persistent buffers for the two-column (spec verify) NVFP4 device-routed program: every
9176/// slab is the t=1 workspace shape doubled along the pair axis, plus per-column
9177/// accumulators. One per expert bank, reused every (round, layer) call.
9178pub struct Nvfp4T2Workspace {
9179    input2: Vec<crate::CudaSlice<f32>>,
9180    in_q2: Vec<crate::CudaSlice<i8>>,
9181    in_d2: Vec<crate::CudaSlice<f32>>,
9182    sel2: Vec<crate::CudaSlice<i32>>,
9183    route_w2: Vec<crate::CudaSlice<f32>>,
9184    gate_out2: Vec<crate::CudaSlice<f32>>,
9185    up_out2: Vec<crate::CudaSlice<f32>>,
9186    act_q2: Vec<crate::CudaSlice<i8>>,
9187    act_d2: Vec<crate::CudaSlice<f32>>,
9188    partial2: Vec<crate::CudaSlice<f32>>,
9189    /// Per-rank per-column combine accumulators ([width] each).
9190    acc_a: Vec<crate::CudaSlice<f32>>,
9191    acc_b: Vec<crate::CudaSlice<f32>>,
9192    /// down8_t2 arm: per-rank [2, width] combined slab, root peer pull and joined slab —
9193    /// the fused kernel writes both columns, so the join is ONE pull + ONE add.
9194    acc2: Vec<crate::CudaSlice<f32>>,
9195    peer2: crate::CudaSlice<f32>,
9196    omix2: crate::CudaSlice<f32>,
9197    /// Root-side pulls of rank1's accumulators and the joined columns.
9198    peer_a: crate::CudaSlice<f32>,
9199    peer_b: crate::CudaSlice<f32>,
9200    omix_a: crate::CudaSlice<f32>,
9201    omix_b: crate::CudaSlice<f32>,
9202    ev_entry: CudaEvent,
9203    ev_rank: Vec<CudaEvent>,
9204    ev_root: CudaEvent,
9205    t_cap: usize,
9206    n_sel: usize,
9207    e_device: usize,
9208}
9209
9210fn nvfp4_trow_workspace_needs_grow(
9211    current: Option<(usize, usize)>,
9212    t: usize,
9213    n_sel: usize,
9214) -> bool {
9215    current.is_none_or(|(t_cap, n_sel_cap)| t_cap < t || n_sel_cap < n_sel)
9216}
9217
9218#[cfg(test)]
9219mod nvfp4_trow_workspace_tests {
9220    use super::nvfp4_trow_workspace_needs_grow;
9221
9222    #[test]
9223    fn workspace_grows_but_never_shrinks_between_spec_and_batch() {
9224        assert!(nvfp4_trow_workspace_needs_grow(None, 2, 16));
9225        assert!(nvfp4_trow_workspace_needs_grow(Some((2, 16)), 8, 64));
9226        assert!(!nvfp4_trow_workspace_needs_grow(Some((8, 64)), 2, 16));
9227        assert!(!nvfp4_trow_workspace_needs_grow(Some((32, 256)), 8, 64));
9228    }
9229}
9230
9231/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
9232/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
9233/// (token, layer) call so the decode loop performs zero output allocations.
9234/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
9235/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
9236/// conservatively) and the persistent e-context input staging its copies read.
9237struct RoutesGraph {
9238    exec: cudarc::driver::sys::CUgraphExec,
9239    parent: cudarc::driver::sys::CUgraph,
9240    _children: Vec<cudarc::driver::CudaGraph>,
9241}
9242// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
9243// context-agnostic process handles.
9244unsafe impl Send for RoutesGraph {}
9245
9246impl Drop for RoutesGraph {
9247    fn drop(&mut self) {
9248        unsafe {
9249            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
9250            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
9251        }
9252    }
9253}
9254
9255impl Nvfp4DeviceRoutesWorkspace {
9256    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
9257        self.in_stage_e.as_ref()
9258    }
9259    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
9260        self.in_stage_e.as_mut()
9261    }
9262    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
9263        self.out_stage_e.as_mut()
9264    }
9265    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
9266    pub(crate) fn arm_stages(
9267        &mut self,
9268        e: &Engine,
9269        width: usize,
9270        n_sel: usize,
9271    ) -> Result<(), Box<dyn std::error::Error>> {
9272        let _main = e.gpu.enter_main()?;
9273        if self.in_stage_e.is_none() {
9274            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
9275            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
9276        }
9277        if self.dev_route_e.is_none() {
9278            self.dev_route_e = Some((
9279                e.htod_i32(&vec![0i32; n_sel])?,
9280                e.htod(&vec![0.0f32; n_sel])?,
9281            ));
9282        }
9283        Ok(())
9284    }
9285
9286    /// Split-borrow: the routes input (shared) + output (mut) stages together.
9287    pub(crate) fn in_and_out_stages_mut(
9288        &mut self,
9289    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
9290        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
9291            (Some(input), Some(output)) => Some((input, output)),
9292            _ => None,
9293        }
9294    }
9295    pub(crate) fn dev_route_e_mut(
9296        &mut self,
9297    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
9298        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
9299    }
9300}
9301
9302pub struct Nvfp4DeviceRoutesWorkspace {
9303    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
9304    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
9305    gate_out: Vec<crate::CudaSlice<f32>>,
9306    up_out: Vec<crate::CudaSlice<f32>>,
9307    act_q: Vec<crate::CudaSlice<i8>>,
9308    act_d: Vec<crate::CudaSlice<f32>>,
9309    sel: Vec<crate::CudaSlice<i32>>,
9310    partial: Vec<crate::CudaSlice<f32>>,
9311    accumulator: Vec<crate::CudaSlice<f32>>,
9312    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
9313    combine_w: Vec<crate::CudaSlice<f32>>,
9314    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
9315    /// in-kernel via sel + macros_down_dev).
9316    route_w: Vec<crate::CudaSlice<f32>>,
9317    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
9318    /// per-call allocation).
9319    in_q: Vec<crate::CudaSlice<i8>>,
9320    in_d: Vec<crate::CudaSlice<f32>>,
9321    /// e-context staging for the device router outputs (persistent — rank streams peer-read
9322    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
9323    /// never-free discipline).
9324    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
9325    /// Prestage door state: input pull + quantize already issued for this layer's call
9326    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
9327    prestaged: bool,
9328    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
9329    /// the routed run skips rank1's sel pull. Reset per call.
9330    rank1_routed: bool,
9331    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
9332    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
9333    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
9334    fence_flags_raw: u64,
9335    fence_ticket: u32,
9336    /// Prestage input fence, recorded on e after the input's producer.
9337    ev_input: Option<(CudaEvent, usize)>,
9338    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
9339    /// captured copies read/write), and the per-layer stitched parent.
9340    in_stage_e: Option<crate::CudaSlice<f32>>,
9341    out_stage_e: Option<crate::CudaSlice<f32>>,
9342    routes_graph: Option<RoutesGraph>,
9343    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
9344    raw_dev_route_e: Option<(u64, u64)>,
9345    raw_combine: Option<(u64, u64, u64, u64)>,
9346    raw_input: Vec<u64>,
9347    raw_sel: Vec<u64>,
9348    raw_route_w: Vec<u64>,
9349    remote: crate::CudaSlice<f32>,
9350    combined: crate::CudaSlice<f32>,
9351    n_sel: usize,
9352    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
9353    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
9354    /// BoundarySlot discipline, same as the v2 attention workspace.
9355    input: Vec<crate::CudaSlice<f32>>,
9356    ev_rank: Vec<CudaEvent>,
9357    ev_done: Option<CudaEvent>,
9358    ev_entry: Option<(CudaEvent, usize)>,
9359}
9360
9361/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
9362struct ResidentNvfp4EpRank {
9363    gate: Vec<crate::CudaSlice<u8>>,
9364    up: Vec<crate::CudaSlice<u8>>,
9365    down: Vec<crate::CudaSlice<u8>>,
9366    #[allow(dead_code)]
9367    expert_range: Range<usize>,
9368}
9369
9370pub struct ResidentNvfp4ExpertParallel {
9371    ranks: Vec<ResidentNvfp4EpRank>,
9372    macros_gate: Vec<f32>,
9373    macros_up: Vec<f32>,
9374    macros_down: Vec<f32>,
9375    pub expert_count: usize,
9376    pub input_width: usize,
9377    pub expert_width: usize,
9378    gate_row_bytes: usize,
9379    down_row_bytes: usize,
9380}
9381
9382fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
9383    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
9384        matrix.codes,
9385        matrix.scales,
9386        matrix.out_features,
9387        matrix.in_features,
9388    )
9389}
9390
9391fn nvfp4_row_bytes(in_features: usize) -> usize {
9392    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
9393}
9394
9395/// MEMRA_NVFP4_BANK_V2=1: store the contiguous expert banks in the slot-major layout the
9396/// coalesced `*_v2` kernels read (see qmatvec.cu). Pure byte permutation — value-exact.
9397/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
9398/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
9399/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
9400/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
9401/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
9402pub(crate) fn fuse_rope_append_on() -> bool {
9403    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9404    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
9405}
9406
9407pub(crate) fn no_local_shadow_on() -> bool {
9408    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9409    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
9410}
9411
9412pub(crate) fn nvfp4_bank_v2_on() -> bool {
9413    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9414    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_BANK_V2").as_deref() == Ok("1"))
9415}
9416
9417/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
9418/// into the slot-major v2 row layout: per row, slot g's 16 qs bytes at g*16, then the two
9419/// UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count unchanged.
9420fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
9421    let row_bytes = nvfp4_row_bytes(in_features);
9422    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
9423    let n_slots = in_features / 32;
9424    let mut out = Vec::with_capacity(v1.len());
9425    for row in 0..out_features {
9426        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
9427        for g in 0..n_slots {
9428            let (sblk, h) = (g / 2, g % 2);
9429            let b = &r[sblk * 36..sblk * 36 + 36];
9430            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
9431        }
9432        for g in 0..n_slots {
9433            let (sblk, h) = (g / 2, g % 2);
9434            let b = &r[sblk * 36..sblk * 36 + 36];
9435            out.push(b[2 * h]);
9436            out.push(b[2 * h + 1]);
9437        }
9438    }
9439    out
9440}
9441
9442/// Repack + (optionally) v2-permute one expert shard for the contiguous banks.
9443fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
9444    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
9445    let v1 = nvfp4_repack_matrix(matrix);
9446    if nvfp4_bank_v2_on() {
9447        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
9448    } else {
9449        v1
9450    }
9451}
9452
9453/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
9454/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
9455fn nvfp4_column_shard<'a>(
9456    matrix: Nvfp4BlockMatrix<'a>,
9457    tp: usize,
9458    rank: usize,
9459) -> Result<Nvfp4BlockMatrix<'a>, String> {
9460    if matrix.out_features % tp != 0 {
9461        return Err(format!(
9462            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
9463            matrix.out_features
9464        ));
9465    }
9466    let local_out = matrix.out_features / tp;
9467    let code_row = matrix.in_features / 2;
9468    let scale_row = matrix.in_features / 16;
9469    Ok(Nvfp4BlockMatrix {
9470        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
9471        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
9472        macro_scale: matrix.macro_scale,
9473        out_features: local_out,
9474        in_features: matrix.in_features,
9475    })
9476}
9477
9478/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
9479/// row contributes one contiguous byte window, gathered across rows.
9480fn nvfp4_row_shard(
9481    matrix: Nvfp4BlockMatrix<'_>,
9482    tp: usize,
9483    rank: usize,
9484) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
9485    if matrix.in_features % tp != 0 {
9486        return Err(format!(
9487            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
9488            matrix.in_features
9489        ));
9490    }
9491    let local_in = matrix.in_features / tp;
9492    if local_in % 64 != 0 {
9493        return Err(format!(
9494            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
9495        ));
9496    }
9497    let code_row = matrix.in_features / 2;
9498    let scale_row = matrix.in_features / 16;
9499    let local_code = local_in / 2;
9500    let local_scale = local_in / 16;
9501    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
9502    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
9503    for row in 0..matrix.out_features {
9504        let code_start = row * code_row + rank * local_code;
9505        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
9506        let scale_start = row * scale_row + rank * local_scale;
9507        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
9508    }
9509    Ok((codes, scales, local_in))
9510}
9511
9512/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
9513/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
9514/// point (see the section header).
9515fn run_rank_nvfp4(
9516    engine: &Engine,
9517    matrix: Nvfp4BlockMatrix<'_>,
9518    activations: &[f32],
9519    tokens: usize,
9520) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9521    matrix.validate()?;
9522    validate_activations(activations, tokens, matrix.in_features)?;
9523    let _main = engine.gpu.enter_main()?;
9524    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
9525    let activations = engine.htod(activations)?;
9526    let output = engine.qmatvec_nvfp4_fast(
9527        &blocks.slice(0..blocks.len()),
9528        &activations,
9529        tokens,
9530        matrix.in_features,
9531        matrix.out_features,
9532        nvfp4_row_bytes(matrix.in_features),
9533    )?;
9534    engine.dtoh(&output)
9535}
9536
9537fn upload_rank_nvfp4(
9538    engine: &Engine,
9539    matrix: Nvfp4BlockMatrix<'_>,
9540) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
9541    matrix.validate()?;
9542    let _main = engine.gpu.enter_main()?;
9543    Ok(ResidentNvfp4Rank {
9544        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
9545        macro_scale: matrix.macro_scale,
9546        out_features: matrix.out_features,
9547        in_features: matrix.in_features,
9548        row_bytes: nvfp4_row_bytes(matrix.in_features),
9549    })
9550}
9551
9552fn run_resident_rank_nvfp4(
9553    engine: &Engine,
9554    rank: &ResidentNvfp4Rank,
9555    activations: &[f32],
9556    tokens: usize,
9557) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9558    validate_activations(activations, tokens, rank.in_features)?;
9559    let _main = engine.gpu.enter_main()?;
9560    let activations = engine.htod(activations)?;
9561    let output = engine.qmatvec_nvfp4_fast(
9562        &rank.blocks.slice(0..rank.blocks.len()),
9563        &activations,
9564        tokens,
9565        rank.in_features,
9566        rank.out_features,
9567        rank.row_bytes,
9568    )?;
9569    engine.dtoh(&output)
9570}
9571
9572fn apply_macro(values: &mut [f32], macro_scale: f32) {
9573    for value in values.iter_mut() {
9574        *value *= macro_scale;
9575    }
9576}
9577
9578impl TpE4m3HostBounce {
9579    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
9580    pub fn full_nvfp4(
9581        &self,
9582        matrix: Nvfp4BlockMatrix<'_>,
9583        activations: &[f32],
9584        tokens: usize,
9585    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9586        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
9587        apply_macro(&mut output, matrix.macro_scale);
9588        Ok(output)
9589    }
9590
9591    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
9592    /// order, macro applied ONCE post-gather.
9593    pub fn column_parallel_nvfp4(
9594        &self,
9595        matrix: Nvfp4BlockMatrix<'_>,
9596        activations: &[f32],
9597        tokens: usize,
9598    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
9599        matrix.validate()?;
9600        validate_activations(activations, tokens, matrix.in_features)?;
9601        let tp = self.ranks.len();
9602        let local_out = matrix.out_features / tp;
9603        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9604        let mut rank_outputs = Vec::with_capacity(tp);
9605        for (rank_index, rank) in self.ranks.iter().enumerate() {
9606            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
9607            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
9608            let row_start = rank_index * local_out;
9609            for token in 0..tokens {
9610                gathered[token * matrix.out_features + row_start
9611                    ..token * matrix.out_features + row_start + local_out]
9612                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9613            }
9614            rank_outputs.push(output);
9615        }
9616        apply_macro(&mut gathered, matrix.macro_scale);
9617        Ok(ColumnParallelResult {
9618            gathered,
9619            rank_outputs,
9620        })
9621    }
9622
9623    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
9624    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
9625    pub fn row_parallel_nvfp4(
9626        &self,
9627        matrix: Nvfp4BlockMatrix<'_>,
9628        activations: &[f32],
9629        tokens: usize,
9630    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
9631        matrix.validate()?;
9632        validate_activations(activations, tokens, matrix.in_features)?;
9633        let tp = self.ranks.len();
9634        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9635        let mut rank_partials = Vec::with_capacity(tp);
9636        for (rank_index, rank) in self.ranks.iter().enumerate() {
9637            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
9638            let local_activations =
9639                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9640            let shard = Nvfp4BlockMatrix {
9641                codes: &codes,
9642                scales: &scales,
9643                macro_scale: matrix.macro_scale,
9644                out_features: matrix.out_features,
9645                in_features: local_in,
9646            };
9647            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
9648            for (sum, value) in reduced.iter_mut().zip(&partial) {
9649                *sum += *value;
9650            }
9651            rank_partials.push(partial);
9652        }
9653        apply_macro(&mut reduced, matrix.macro_scale);
9654        Ok(RowParallelResult {
9655            reduced,
9656            rank_partials,
9657        })
9658    }
9659
9660    pub fn upload_expert_nvfp4(
9661        &self,
9662        gate: Nvfp4BlockMatrix<'_>,
9663        up: Nvfp4BlockMatrix<'_>,
9664        down: Nvfp4BlockMatrix<'_>,
9665    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
9666        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9667            return Err("NVFP4 TP expert gate/up dimensions differ".into());
9668        }
9669        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9670            return Err(format!(
9671                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
9672                down.out_features, down.in_features, gate.out_features, gate.in_features
9673            )
9674            .into());
9675        }
9676        let tp = self.ranks.len();
9677        let mut gate_ranks = Vec::with_capacity(tp);
9678        let mut up_ranks = Vec::with_capacity(tp);
9679        let mut down_ranks = Vec::with_capacity(tp);
9680        for (rank_index, engine) in self.ranks.iter().enumerate() {
9681            gate_ranks.push(upload_rank_nvfp4(
9682                engine,
9683                nvfp4_column_shard(gate, tp, rank_index)?,
9684            )?);
9685            up_ranks.push(upload_rank_nvfp4(
9686                engine,
9687                nvfp4_column_shard(up, tp, rank_index)?,
9688            )?);
9689            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
9690            down_ranks.push(upload_rank_nvfp4(
9691                engine,
9692                Nvfp4BlockMatrix {
9693                    codes: &codes,
9694                    scales: &scales,
9695                    macro_scale: down.macro_scale,
9696                    out_features: down.out_features,
9697                    in_features: local_in,
9698                },
9699            )?);
9700        }
9701        Ok(ResidentTpNvfp4Expert {
9702            gate: ResidentNvfp4ColumnParallel {
9703                ranks: gate_ranks,
9704                out_features: gate.out_features,
9705                in_features: gate.in_features,
9706            },
9707            up: ResidentNvfp4ColumnParallel {
9708                ranks: up_ranks,
9709                out_features: up.out_features,
9710                in_features: up.in_features,
9711            },
9712            down: ResidentNvfp4RowParallel {
9713                ranks: down_ranks,
9714                out_features: down.out_features,
9715                in_features: down.in_features,
9716            },
9717            input_width: gate.in_features,
9718            expert_width: gate.out_features,
9719        })
9720    }
9721
9722    fn column_parallel_resident_nvfp4(
9723        &self,
9724        matrix: &ResidentNvfp4ColumnParallel,
9725        activations: &[f32],
9726        tokens: usize,
9727    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9728        validate_activations(activations, tokens, matrix.in_features)?;
9729        let local_out = matrix.out_features / self.ranks.len();
9730        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9731        let mut macro_scale = None;
9732        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9733            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
9734            let row_start = rank_index * local_out;
9735            for token in 0..tokens {
9736                gathered[token * matrix.out_features + row_start
9737                    ..token * matrix.out_features + row_start + local_out]
9738                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9739            }
9740            macro_scale = Some(shard.macro_scale);
9741        }
9742        apply_macro(
9743            &mut gathered,
9744            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
9745        );
9746        Ok(gathered)
9747    }
9748
9749    fn row_parallel_resident_nvfp4(
9750        &self,
9751        matrix: &ResidentNvfp4RowParallel,
9752        activations: &[f32],
9753        tokens: usize,
9754    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9755        validate_activations(activations, tokens, matrix.in_features)?;
9756        let tp = self.ranks.len();
9757        let local_in = matrix.in_features / tp;
9758        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9759        let mut macro_scale = None;
9760        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9761            if shard.in_features != local_in {
9762                return Err(format!(
9763                    "NVFP4 resident row shard in_features {} != expected {local_in}",
9764                    shard.in_features
9765                )
9766                .into());
9767            }
9768            let local_activations =
9769                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9770            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
9771            for (sum, value) in reduced.iter_mut().zip(&partial) {
9772                *sum += *value;
9773            }
9774            macro_scale = Some(shard.macro_scale);
9775        }
9776        apply_macro(
9777            &mut reduced,
9778            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
9779        );
9780        Ok(reduced)
9781    }
9782
9783    pub fn run_expert_nvfp4(
9784        &self,
9785        expert: &ResidentTpNvfp4Expert,
9786        input: &[f32],
9787        tokens: usize,
9788    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9789        validate_activations(input, tokens, expert.input_width)?;
9790        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
9791        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
9792        let activated: Vec<f32> = gate
9793            .iter()
9794            .zip(&up)
9795            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
9796            .collect();
9797        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
9798        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
9799    }
9800
9801    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
9802    pub fn upload_tensor_parallel_nvfp4(
9803        &self,
9804        gate: Nvfp4ExpertBank<'_>,
9805        up: Nvfp4ExpertBank<'_>,
9806        down: Nvfp4ExpertBank<'_>,
9807    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
9808        gate.validate()?;
9809        up.validate()?;
9810        down.validate()?;
9811        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9812            return Err("NVFP4 TP gate/up/down expert counts differ".into());
9813        }
9814        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9815            return Err("NVFP4 TP gate/up dimensions differ".into());
9816        }
9817        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9818            return Err(format!(
9819                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
9820                down.out_features, down.in_features, gate.out_features, gate.in_features
9821            )
9822            .into());
9823        }
9824        let tp = self.ranks.len();
9825        if gate.out_features % tp != 0 {
9826            return Err(format!(
9827                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
9828                gate.out_features
9829            )
9830            .into());
9831        }
9832        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
9833            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
9834        {
9835            return Err(format!(
9836                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
9837                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
9838                down.in_features
9839            )
9840            .into());
9841        }
9842        if tp > NVFP4_CANONICAL_ROW_SHARDS {
9843            return Err(format!(
9844                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
9845                 ({NVFP4_CANONICAL_ROW_SHARDS})"
9846            )
9847            .into());
9848        }
9849
9850        let ep2 = step_nvfp4_ep2_on() && tp == 2;
9851        let mut gate_ranks = Vec::with_capacity(tp);
9852        let mut up_ranks = Vec::with_capacity(tp);
9853        let mut macros_gate_dev = Vec::with_capacity(tp);
9854        let mut macros_up_dev = Vec::with_capacity(tp);
9855        let mut macros_down_dev = Vec::with_capacity(tp);
9856        for (rank_index, engine) in self.ranks.iter().enumerate() {
9857            let _main = engine.gpu.enter_main()?;
9858            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
9859            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
9860            // are unchanged (same repack).
9861            // EP2: this rank holds the FULL matrices of the experts it owns (id & 1 ==
9862            // rank_index), stacked at slot id >> 1 — same total bytes as the shard bank.
9863            let mut gate_host: Vec<u8> = Vec::new();
9864            let mut up_host: Vec<u8> = Vec::new();
9865            let mut owned = 0usize;
9866            for expert in 0..gate.expert_count {
9867                if ep2 {
9868                    if expert % 2 != rank_index {
9869                        continue;
9870                    }
9871                    owned += 1;
9872                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate.expert(expert)?));
9873                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up.expert(expert)?));
9874                } else {
9875                    let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
9876                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard));
9877                    let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
9878                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard));
9879                }
9880            }
9881            let bank_experts = if ep2 { owned } else { gate.expert_count };
9882            let gate_expert_bytes = gate_host.len() / bank_experts.max(1);
9883            let up_expert_bytes = up_host.len() / bank_experts.max(1);
9884            let local_out = if ep2 {
9885                gate.out_features
9886            } else {
9887                gate.out_features / tp
9888            };
9889            gate_ranks.push(ResidentNvfp4ColumnBankRank {
9890                bank: engine.htod_bytes(&gate_host)?,
9891                expert_bytes: gate_expert_bytes,
9892                local_out,
9893                in_features: gate.in_features,
9894                row_bytes: nvfp4_row_bytes(gate.in_features),
9895            });
9896            up_ranks.push(ResidentNvfp4ColumnBankRank {
9897                bank: engine.htod_bytes(&up_host)?,
9898                expert_bytes: up_expert_bytes,
9899                local_out,
9900                in_features: up.in_features,
9901                row_bytes: nvfp4_row_bytes(up.in_features),
9902            });
9903            macros_gate_dev.push(engine.htod(gate.macros)?);
9904            macros_up_dev.push(engine.htod(up.macros)?);
9905            macros_down_dev.push(engine.htod(down.macros)?);
9906        }
9907        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
9908        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
9909        // execution and reduction order stay identical.
9910        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
9911        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
9912            let device_rank = shard_index % tp;
9913            let engine = &self.ranks[device_rank];
9914            let _main = engine.gpu.enter_main()?;
9915            let mut down_host: Vec<u8> = Vec::new();
9916            let mut owned = 0usize;
9917            for expert in 0..down.expert_count {
9918                let down_matrix = down.expert(expert)?;
9919                if ep2 {
9920                    // EP2: shard_index doubles as the owner rank; full-width down matrices
9921                    // of the owned experts, stacked at slot id >> 1.
9922                    if expert % 2 != device_rank {
9923                        continue;
9924                    }
9925                    owned += 1;
9926                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(down_matrix));
9927                } else {
9928                    let (codes, scales, local_in) =
9929                        nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
9930                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(Nvfp4BlockMatrix {
9931                        codes: &codes,
9932                        scales: &scales,
9933                        macro_scale: down_matrix.macro_scale,
9934                        out_features: down_matrix.out_features,
9935                        in_features: local_in,
9936                    }));
9937                }
9938            }
9939            let bank_experts = if ep2 { owned } else { down.expert_count };
9940            let down_expert_bytes = down_host.len() / bank_experts.max(1);
9941            let local_in = if ep2 {
9942                down.in_features
9943            } else {
9944                down.in_features / NVFP4_CANONICAL_ROW_SHARDS
9945            };
9946            down_ranks.push(ResidentNvfp4RowBankRank {
9947                bank: engine.htod_bytes(&down_host)?,
9948                expert_bytes: down_expert_bytes,
9949                device_rank,
9950                out_features: down.out_features,
9951                local_in,
9952                row_bytes: nvfp4_row_bytes(local_in),
9953            });
9954        }
9955        Ok(ResidentNvfp4TensorParallel {
9956            gate: gate_ranks,
9957            up: up_ranks,
9958            down: down_ranks,
9959            macros_gate: gate.macros.to_vec(),
9960            macros_up: up.macros.to_vec(),
9961            macros_down: down.macros.to_vec(),
9962            macros_gate_dev,
9963            macros_up_dev,
9964            macros_down_dev,
9965            expert_count: gate.expert_count,
9966            input_width: gate.in_features,
9967            expert_width: gate.out_features,
9968            device_workspace: std::sync::Mutex::new(None),
9969            prime_tables: std::sync::Mutex::new(Vec::new()),
9970            t2_workspace: std::sync::Mutex::new(None),
9971            ep2,
9972        })
9973    }
9974
9975    /// EP2 host-canonical: the whole expert executes on its owning rank at full width
9976    /// (owner = expert & 1, bank slot = expert >> 1). Per-row program == the column-bank
9977    /// path's kernel, so gate/up are bit-equal to the TP layout.
9978    fn run_full_bank_expert_nvfp4(
9979        &self,
9980        ranks: &[ResidentNvfp4ColumnBankRank],
9981        macros: &[f32],
9982        expert: usize,
9983        input: &[f32],
9984    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9985        let owner = expert & 1;
9986        let slot = expert >> 1;
9987        let bank = ranks
9988            .get(owner)
9989            .ok_or("NVFP4 EP2 column bank missing owner rank")?;
9990        let engine = &self.ranks[owner];
9991        let _main = engine.gpu.enter_main()?;
9992        let activations = engine.htod(input)?;
9993        let output = if nvfp4_bank_v2_on() {
9994            engine.qmatvec_nvfp4_fast_v2(
9995                &bank.expert(slot),
9996                &activations,
9997                1,
9998                bank.in_features,
9999                bank.local_out,
10000                bank.row_bytes,
10001            )?
10002        } else {
10003            engine.qmatvec_nvfp4_fast(
10004                &bank.expert(slot),
10005                &activations,
10006                1,
10007                bank.in_features,
10008                bank.local_out,
10009                bank.row_bytes,
10010            )?
10011        };
10012        let mut out = engine.dtoh(&output)?;
10013        apply_macro(&mut out, macros[expert]);
10014        Ok(out)
10015    }
10016
10017    /// EP2 host-canonical down: one full-width dot on the owner (NUMERIC-CLASS vs the
10018    /// canonical 2-shard sum — the parenthesization this door declares).
10019    fn run_full_down_expert_nvfp4(
10020        &self,
10021        shards: &[ResidentNvfp4RowBankRank],
10022        macros: &[f32],
10023        expert: usize,
10024        input: &[f32],
10025    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10026        let owner = expert & 1;
10027        let slot = expert >> 1;
10028        let shard = shards
10029            .get(owner)
10030            .ok_or("NVFP4 EP2 down bank missing owner rank")?;
10031        let engine = &self.ranks[owner];
10032        let _main = engine.gpu.enter_main()?;
10033        let activations = engine.htod(input)?;
10034        let output = if nvfp4_bank_v2_on() {
10035            engine.qmatvec_nvfp4_fast_v2(
10036                &shard.expert(slot),
10037                &activations,
10038                1,
10039                shard.local_in,
10040                shard.out_features,
10041                shard.row_bytes,
10042            )?
10043        } else {
10044            engine.qmatvec_nvfp4_fast(
10045                &shard.expert(slot),
10046                &activations,
10047                1,
10048                shard.local_in,
10049                shard.out_features,
10050                shard.row_bytes,
10051            )?
10052        };
10053        let mut out = engine.dtoh(&output)?;
10054        apply_macro(&mut out, macros[expert]);
10055        Ok(out)
10056    }
10057
10058    fn run_column_bank_expert_nvfp4(
10059        &self,
10060        ranks: &[ResidentNvfp4ColumnBankRank],
10061        macros: &[f32],
10062        expert: usize,
10063        input: &[f32],
10064    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10065        let local_out = ranks
10066            .first()
10067            .ok_or("NVFP4 TP column bank has no ranks")?
10068            .local_out;
10069        let mut gathered = vec![0.0f32; local_out * ranks.len()];
10070        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
10071            let _main = engine.gpu.enter_main()?;
10072            let activations = engine.htod(input)?;
10073            let output = if nvfp4_bank_v2_on() {
10074                engine.qmatvec_nvfp4_fast_v2(
10075                    &bank.expert(expert),
10076                    &activations,
10077                    1,
10078                    bank.in_features,
10079                    bank.local_out,
10080                    bank.row_bytes,
10081                )?
10082            } else {
10083                engine.qmatvec_nvfp4_fast(
10084                    &bank.expert(expert),
10085                    &activations,
10086                    1,
10087                    bank.in_features,
10088                    bank.local_out,
10089                    bank.row_bytes,
10090                )?
10091            };
10092            let output = engine.dtoh(&output)?;
10093            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
10094        }
10095        apply_macro(&mut gathered, macros[expert]);
10096        Ok(gathered)
10097    }
10098
10099    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
10100    /// executes on its owning rank engine), so the reduction parenthesization is identical at
10101    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
10102    fn run_row_bank_expert_nvfp4(
10103        &self,
10104        shards: &[ResidentNvfp4RowBankRank],
10105        macros: &[f32],
10106        expert: usize,
10107        input: &[f32],
10108    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10109        let out_features = shards
10110            .first()
10111            .ok_or("NVFP4 TP row bank has no canonical shards")?
10112            .out_features;
10113        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
10114        let mut reduced = vec![0.0f32; out_features];
10115        for (shard_index, shard) in shards.iter().enumerate() {
10116            let engine = self
10117                .ranks
10118                .get(shard.device_rank)
10119                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
10120            let _main = engine.gpu.enter_main()?;
10121            let local_activations =
10122                activation_shard(input, 1, in_features, shards.len(), shard_index);
10123            let activations = engine.htod(&local_activations)?;
10124            let output = if nvfp4_bank_v2_on() {
10125                engine.qmatvec_nvfp4_fast_v2(
10126                    &shard.expert(expert),
10127                    &activations,
10128                    1,
10129                    shard.local_in,
10130                    shard.out_features,
10131                    shard.row_bytes,
10132                )?
10133            } else {
10134                engine.qmatvec_nvfp4_fast(
10135                    &shard.expert(expert),
10136                    &activations,
10137                    1,
10138                    shard.local_in,
10139                    shard.out_features,
10140                    shard.row_bytes,
10141                )?
10142            };
10143            let partial = engine.dtoh(&output)?;
10144            for (sum, value) in reduced.iter_mut().zip(&partial) {
10145                *sum += *value;
10146            }
10147        }
10148        apply_macro(&mut reduced, macros[expert]);
10149        Ok(reduced)
10150    }
10151
10152    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
10153    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
10154    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
10155    pub fn upload_expert_parallel_nvfp4(
10156        &self,
10157        gate: Nvfp4ExpertBank<'_>,
10158        up: Nvfp4ExpertBank<'_>,
10159        down: Nvfp4ExpertBank<'_>,
10160    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
10161        gate.validate()?;
10162        up.validate()?;
10163        down.validate()?;
10164        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
10165            return Err("NVFP4 EP gate/up/down expert counts differ".into());
10166        }
10167        if gate.in_features != up.in_features || gate.out_features != up.out_features {
10168            return Err("NVFP4 EP gate/up dimensions differ".into());
10169        }
10170        if down.in_features != gate.out_features || down.out_features != gate.in_features {
10171            return Err(format!(
10172                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
10173                down.out_features, down.in_features, gate.out_features, gate.in_features
10174            )
10175            .into());
10176        }
10177        let world = self.ranks.len();
10178        if gate.expert_count % world != 0 {
10179            return Err(format!(
10180                "NVFP4 EP expert count {} is not divisible by {world} ranks",
10181                gate.expert_count
10182            )
10183            .into());
10184        }
10185        let experts_per_rank = gate.expert_count / world;
10186        let mut ranks = Vec::with_capacity(world);
10187        for (rank_index, engine) in self.ranks.iter().enumerate() {
10188            let _main = engine.gpu.enter_main()?;
10189            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
10190            let mut gate_experts = Vec::with_capacity(experts_per_rank);
10191            let mut up_experts = Vec::with_capacity(experts_per_rank);
10192            let mut down_experts = Vec::with_capacity(experts_per_rank);
10193            for expert in expert_range.clone() {
10194                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
10195                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
10196                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
10197            }
10198            ranks.push(ResidentNvfp4EpRank {
10199                gate: gate_experts,
10200                up: up_experts,
10201                down: down_experts,
10202                expert_range,
10203            });
10204        }
10205        Ok(ResidentNvfp4ExpertParallel {
10206            ranks,
10207            macros_gate: gate.macros.to_vec(),
10208            macros_up: up.macros.to_vec(),
10209            macros_down: down.macros.to_vec(),
10210            expert_count: gate.expert_count,
10211            input_width: gate.in_features,
10212            expert_width: gate.out_features,
10213            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
10214            down_row_bytes: nvfp4_row_bytes(down.in_features),
10215        })
10216    }
10217
10218    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
10219    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
10220    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
10221    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
10222    /// contract. Exactness-first; no throughput claim.
10223    #[allow(clippy::too_many_arguments)]
10224    pub fn run_routed_experts_nvfp4(
10225        &self,
10226        experts: &ResidentNvfp4ExpertParallel,
10227        input: &[f32],
10228        tokens: usize,
10229        selected: &[usize],
10230        route_weights: &[f32],
10231        experts_per_token: usize,
10232        activation_limit: Option<f32>,
10233    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10234        validate_activations(input, tokens, experts.input_width)?;
10235        let pairs = tokens
10236            .checked_mul(experts_per_token)
10237            .ok_or("NVFP4 EP route count overflow")?;
10238        if selected.len() != pairs || route_weights.len() != pairs {
10239            return Err(format!(
10240                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
10241                 {experts_per_token} ({pairs})",
10242                selected.len(),
10243                route_weights.len(),
10244            )
10245            .into());
10246        }
10247        if !route_weights.iter().all(|weight| weight.is_finite()) {
10248            return Err("NVFP4 EP route weights contain a non-finite value".into());
10249        }
10250        let experts_per_rank = experts.expert_count / experts.ranks.len();
10251        let mut output = vec![0.0f32; tokens * experts.input_width];
10252        for token in 0..tokens {
10253            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
10254            for slot in 0..experts_per_token {
10255                let pair = token * experts_per_token + slot;
10256                let expert = selected[pair];
10257                if expert >= experts.expert_count {
10258                    return Err(format!(
10259                        "NVFP4 EP selected expert {expert} outside 0..{}",
10260                        experts.expert_count
10261                    )
10262                    .into());
10263                }
10264                let owner = expert / experts_per_rank;
10265                let local = expert - owner * experts_per_rank;
10266                let rank = &experts.ranks[owner];
10267                let engine = &self.ranks[owner];
10268                let _main = engine.gpu.enter_main()?;
10269                let device_input = engine.htod(input_row)?;
10270                let gate_out = engine.qmatvec_nvfp4_fast(
10271                    &rank.gate[local].slice(0..rank.gate[local].len()),
10272                    &device_input,
10273                    1,
10274                    experts.input_width,
10275                    experts.expert_width,
10276                    experts.gate_row_bytes,
10277                )?;
10278                let up_out = engine.qmatvec_nvfp4_fast(
10279                    &rank.up[local].slice(0..rank.up[local].len()),
10280                    &device_input,
10281                    1,
10282                    experts.input_width,
10283                    experts.expert_width,
10284                    experts.gate_row_bytes,
10285                )?;
10286                let mut gate_host = engine.dtoh(&gate_out)?;
10287                let mut up_host = engine.dtoh(&up_out)?;
10288                apply_macro(&mut gate_host, experts.macros_gate[expert]);
10289                apply_macro(&mut up_host, experts.macros_up[expert]);
10290                let activated: Vec<f32> = gate_host
10291                    .iter()
10292                    .zip(&up_host)
10293                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
10294                    .collect();
10295                let device_activated = engine.htod(&activated)?;
10296                let down_out = engine.qmatvec_nvfp4_fast(
10297                    &rank.down[local].slice(0..rank.down[local].len()),
10298                    &device_activated,
10299                    1,
10300                    experts.expert_width,
10301                    experts.input_width,
10302                    experts.down_row_bytes,
10303                )?;
10304                let mut down_host = engine.dtoh(&down_out)?;
10305                apply_macro(&mut down_host, experts.macros_down[expert]);
10306                let weight = route_weights[pair];
10307                for (sum, value) in output
10308                    [token * experts.input_width..(token + 1) * experts.input_width]
10309                    .iter_mut()
10310                    .zip(down_host)
10311                {
10312                    *sum += weight * value;
10313                }
10314            }
10315        }
10316        Ok(output)
10317    }
10318
10319    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
10320    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
10321    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
10322    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
10323    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
10324    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
10325    ///
10326    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
10327    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
10328    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
10329    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
10330    /// host-canonical oracle, and with repeat determinism against itself.
10331    /// Clamped layers refuse (they stay on the EP program).
10332    pub fn run_tensor_parallel_routes_nvfp4_device(
10333        &self,
10334        experts: &ResidentNvfp4TensorParallel,
10335        input: &[f32],
10336        selected: &[usize],
10337        route_weights: &[f32],
10338        experts_per_token: usize,
10339        activation_limit: Option<f32>,
10340    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10341        validate_activations(input, 1, experts.input_width)?;
10342        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10343            return Err(format!(
10344                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
10345                selected.len(),
10346                route_weights.len(),
10347            )
10348            .into());
10349        }
10350        if !route_weights.iter().all(|weight| weight.is_finite()) {
10351            return Err("NVFP4 device route weights contain a non-finite value".into());
10352        }
10353        let world = self.ranks.len();
10354        if world != NVFP4_CANONICAL_ROW_SHARDS {
10355            return Err(format!(
10356                "NVFP4 device routes require world == canonical shard grid \
10357                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10358            )
10359            .into());
10360        }
10361        let local_out = if experts.ep2 {
10362            experts.expert_width
10363        } else {
10364            experts.expert_width / world
10365        };
10366
10367        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
10368        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
10369        // everything else without Nsight.
10370        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10371        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10372        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10373        let started = timing.then(std::time::Instant::now);
10374
10375        let n_sel = experts_per_token;
10376        let mut workspace_guard = experts
10377            .device_workspace
10378            .lock()
10379            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10380        if workspace_guard.is_none() {
10381            let mut gate_out = Vec::with_capacity(world);
10382            let mut up_out = Vec::with_capacity(world);
10383            let mut act_q = Vec::with_capacity(world);
10384            let mut act_d = Vec::with_capacity(world);
10385            let mut sel = Vec::with_capacity(world);
10386            let mut partial = Vec::with_capacity(world);
10387            let mut accumulator = Vec::with_capacity(world);
10388            let mut combine_w = Vec::with_capacity(world);
10389            let mut route_w = Vec::with_capacity(world);
10390            let mut in_q = Vec::with_capacity(world);
10391            let mut in_d = Vec::with_capacity(world);
10392            let mut input = Vec::with_capacity(world);
10393            let mut ev_rank = Vec::with_capacity(world);
10394            let moe_direct = moe_direct_on();
10395            for (rank, engine) in self.ranks.iter().enumerate() {
10396                let _main = engine.gpu.enter_main()?;
10397                gate_out.push(engine.uninit(n_sel * local_out)?);
10398                up_out.push(engine.uninit(n_sel * local_out)?);
10399                act_q.push(engine.uninit_i8(n_sel * local_out)?);
10400                act_d.push(engine.uninit(n_sel * local_out / 32)?);
10401                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
10402                partial.push(engine.uninit(n_sel * experts.input_width)?);
10403                // Direct join: peer accumulators live on ROOT (single P2P store pass).
10404                if moe_direct && rank != 0 {
10405                    let root = &self.ranks[0];
10406                    let _root_main = root.gpu.enter_main()?;
10407                    accumulator.push(root.zeros(experts.input_width)?);
10408                } else {
10409                    accumulator.push(engine.zeros(experts.input_width)?);
10410                }
10411                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
10412                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
10413                in_q.push(engine.uninit_i8(experts.input_width)?);
10414                in_d.push(engine.uninit(experts.input_width / 32)?);
10415                input.push(engine.uninit(experts.input_width)?);
10416                ev_rank.push(engine.ctx().new_event(None)?);
10417            }
10418            let root = &self.ranks[0];
10419            let _main = root.gpu.enter_main()?;
10420            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
10421                prestaged: false,
10422                rank1_routed: false,
10423                ev_input: None,
10424                fence_flags_raw: 0,
10425                fence_ticket: 0,
10426                gate_out,
10427                up_out,
10428                act_q,
10429                act_d,
10430                sel,
10431                partial,
10432                accumulator,
10433                combine_w,
10434                route_w,
10435                in_q,
10436                in_d,
10437                dev_route_e: None,
10438                in_stage_e: None,
10439                out_stage_e: None,
10440                routes_graph: None,
10441                raw_dev_route_e: None,
10442                raw_combine: None,
10443                raw_input: Vec::new(),
10444                raw_sel: Vec::new(),
10445                raw_route_w: Vec::new(),
10446                remote: root.uninit(experts.input_width)?,
10447                combined: root.uninit(experts.input_width)?,
10448                n_sel,
10449                input,
10450                ev_rank,
10451                ev_done: Some(root.ctx().new_event(None)?),
10452                ev_entry: None,
10453            });
10454        }
10455        let workspace = workspace_guard
10456            .as_mut()
10457            .expect("NVFP4 device routes workspace initialized above");
10458        // EP2 uses this call only as the workspace-arming warmup (the prejoin path drives
10459        // decode); its host-routed sweep semantics do not apply to whole-expert banks.
10460        if experts.ep2 {
10461            return Ok(vec![0.0f32; experts.input_width]);
10462        }
10463        if workspace.n_sel != n_sel {
10464            return Err(format!(
10465                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10466                workspace.n_sel
10467            )
10468            .into());
10469        }
10470        for &expert in selected {
10471            if expert >= experts.expert_count {
10472                return Err(format!(
10473                    "NVFP4 device selected expert {expert} outside 0..{}",
10474                    experts.expert_count
10475                )
10476                .into());
10477            }
10478        }
10479        let sel_i32 = selected
10480            .iter()
10481            .map(|&expert| expert as i32)
10482            .collect::<Vec<_>>();
10483
10484        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
10485        // down) covers every selected expert via the selection array and the contiguous bank —
10486        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
10487        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
10488        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
10489        // accumulation order — the program's values are unchanged.
10490        for (rank_index, engine) in self.ranks.iter().enumerate() {
10491            let _main = engine.gpu.enter_main()?;
10492            let device_input = engine.htod(input)?;
10493            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
10494            engine.quantize_q8_1_into(
10495                &device_input,
10496                1,
10497                experts.input_width,
10498                &mut in_q[rank_index],
10499                &mut in_d[rank_index],
10500            )?;
10501            // device_input frees on this rank's stream after the quantize — same-stream order.
10502        }
10503        self.nvfp4_routes_batched_sweeps(
10504            experts,
10505            workspace,
10506            selected,
10507            route_weights,
10508            &sel_i32,
10509            local_out,
10510            n_sel,
10511            activation_limit,
10512            false,
10513        )?;
10514
10515        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
10516        // reduce in canonical shard order, read back once.
10517        let root = &self.ranks[0];
10518        for engine in &self.ranks[1..] {
10519            let _main = engine.gpu.enter_main()?;
10520            engine.stream().synchronize()?;
10521        }
10522        let _main = root.gpu.enter_main()?;
10523        root.stream()
10524            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10525        root.add(
10526            &workspace.accumulator[0],
10527            &workspace.remote,
10528            &mut workspace.combined,
10529            experts.input_width,
10530        )?;
10531        let output = root.dtoh(&workspace.combined)?;
10532        if let Some(started) = started {
10533            use std::sync::atomic::Ordering;
10534            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10535                + started.elapsed().as_nanos() as u64;
10536            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10537            if calls % 430 == 0 {
10538                eprintln!(
10539                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10540                    ns as f64 / 1.0e6,
10541                    ns as f64 / calls as f64 / 1.0e3,
10542                );
10543            }
10544        }
10545        Ok(output)
10546    }
10547
10548    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
10549    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
10550    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
10551    /// owning rank's stream; callers own input acquisition and the combine.
10552    #[allow(clippy::too_many_arguments)]
10553    fn nvfp4_routes_batched_sweeps(
10554        &self,
10555        experts: &ResidentNvfp4TensorParallel,
10556        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10557        selected: &[usize],
10558        route_weights: &[f32],
10559        sel_i32: &[i32],
10560        local_out: usize,
10561        n_sel: usize,
10562        activation_limit: Option<f32>,
10563        device_routed: bool,
10564    ) -> Result<(), Box<dyn std::error::Error>> {
10565        for rank_index in 0..self.ranks.len() {
10566            self.nvfp4_routes_batched_sweeps_rank(
10567                experts,
10568                workspace,
10569                selected,
10570                route_weights,
10571                sel_i32,
10572                local_out,
10573                n_sel,
10574                activation_limit,
10575                device_routed,
10576                rank_index,
10577            )?;
10578        }
10579        Ok(())
10580    }
10581
10582    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
10583    /// the graph door can capture each rank's segment on its own stream.
10584    #[allow(clippy::too_many_arguments)]
10585    fn nvfp4_routes_batched_sweeps_rank(
10586        &self,
10587        experts: &ResidentNvfp4TensorParallel,
10588        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10589        selected: &[usize],
10590        route_weights: &[f32],
10591        sel_i32: &[i32],
10592        local_out: usize,
10593        n_sel: usize,
10594        activation_limit: Option<f32>,
10595        device_routed: bool,
10596        rank_index: usize,
10597    ) -> Result<(), Box<dyn std::error::Error>> {
10598        {
10599            let engine = &self.ranks[rank_index];
10600            let _main = engine.gpu.enter_main()?;
10601            // EP2: whole-expert full-width sweep, owner-guarded; down+combine fused writes
10602            // this rank's slot-ordered partial straight into its accumulator (the join is
10603            // unchanged). Device-routed only — the host-routed arm and the graph door refuse
10604            // at the caller.
10605            if experts.ep2 {
10606                if !device_routed {
10607                    return Err("NVFP4 EP2 banks support the device-routed decode arm only".into());
10608                }
10609                let gate_bank = &experts.gate[rank_index];
10610                let up_bank = &experts.up[rank_index];
10611                if gate_bank.local_out != experts.expert_width
10612                    || gate_bank.expert_bytes != up_bank.expert_bytes
10613                {
10614                    return Err("NVFP4 EP2 bank geometry drifted".into());
10615                }
10616                {
10617                    let Nvfp4DeviceRoutesWorkspace {
10618                        sel,
10619                        gate_out,
10620                        up_out,
10621                        in_q,
10622                        in_d,
10623                        ..
10624                    } = &mut *workspace;
10625                    engine.qmatvec_nvfp4_sel_gu_ep_into(
10626                        &gate_bank.bank,
10627                        &up_bank.bank,
10628                        &sel[rank_index],
10629                        &in_q[rank_index],
10630                        &in_d[rank_index],
10631                        &mut gate_out[rank_index],
10632                        &mut up_out[rank_index],
10633                        n_sel,
10634                        gate_bank.in_features,
10635                        gate_bank.local_out,
10636                        gate_bank.row_bytes,
10637                        gate_bank.expert_bytes,
10638                        rank_index,
10639                    )?;
10640                }
10641                {
10642                    let Nvfp4DeviceRoutesWorkspace {
10643                        gate_out,
10644                        up_out,
10645                        sel,
10646                        act_q,
10647                        act_d,
10648                        ..
10649                    } = &mut *workspace;
10650                    engine.silu_mul_scaled_q8_1_sel_ep_into(
10651                        &gate_out[rank_index],
10652                        &up_out[rank_index],
10653                        &experts.macros_gate_dev[rank_index],
10654                        &experts.macros_up_dev[rank_index],
10655                        &sel[rank_index],
10656                        activation_limit,
10657                        &mut act_q[rank_index],
10658                        &mut act_d[rank_index],
10659                        local_out,
10660                        n_sel,
10661                        rank_index,
10662                    )?;
10663                }
10664                let shard = &experts.down[rank_index];
10665                if shard.device_rank != rank_index || shard.local_in != local_out {
10666                    return Err("NVFP4 EP2 down bank placement drifted".into());
10667                }
10668                {
10669                    let Nvfp4DeviceRoutesWorkspace {
10670                        sel,
10671                        act_q,
10672                        act_d,
10673                        route_w,
10674                        accumulator,
10675                        ..
10676                    } = &mut *workspace;
10677                    engine.qmatvec_nvfp4_sel_down8_ep_into(
10678                        &shard.bank,
10679                        &sel[rank_index],
10680                        &act_q[rank_index],
10681                        &act_d[rank_index],
10682                        &route_w[rank_index],
10683                        &experts.macros_down_dev[rank_index],
10684                        &mut accumulator[rank_index],
10685                        n_sel,
10686                        shard.local_in,
10687                        shard.out_features,
10688                        shard.row_bytes,
10689                        shard.expert_bytes,
10690                        local_out,
10691                        local_out / 32,
10692                        rank_index,
10693                    )?;
10694                }
10695                return Ok(());
10696            }
10697            if !device_routed {
10698                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
10699                // Folded combine weights (route_weight x down macro) — one 40-byte upload
10700                // replaces the accumulator reset + n_sel sequential axpy launches below.
10701                let folded = (0..n_sel)
10702                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
10703                    .collect::<Vec<_>>();
10704                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
10705                engine.stream().memcpy_htod(&folded, &mut view)?;
10706            }
10707            let gate_bank = &experts.gate[rank_index];
10708            let up_bank = &experts.up[rank_index];
10709            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
10710            // FUSION #2a (v2 banks): the two sweeps share sel/aq/ad and identical geometry
10711            // — one launch, per-row bit-identical, double the grid fill.
10712            let gu_fused = nvfp4_bank_v2_on()
10713                && gate_bank.in_features == up_bank.in_features
10714                && gate_bank.local_out == up_bank.local_out
10715                && gate_bank.row_bytes == up_bank.row_bytes
10716                && gate_bank.expert_bytes == up_bank.expert_bytes;
10717            if gu_fused {
10718                let Nvfp4DeviceRoutesWorkspace {
10719                    sel,
10720                    gate_out,
10721                    up_out,
10722                    in_q,
10723                    in_d,
10724                    ..
10725                } = &mut *workspace;
10726                engine.qmatvec_nvfp4_sel_gu_into(
10727                    &gate_bank.bank,
10728                    &up_bank.bank,
10729                    &sel[rank_index],
10730                    &in_q[rank_index],
10731                    &in_d[rank_index],
10732                    &mut gate_out[rank_index],
10733                    &mut up_out[rank_index],
10734                    n_sel,
10735                    gate_bank.in_features,
10736                    gate_bank.local_out,
10737                    gate_bank.row_bytes,
10738                    gate_bank.expert_bytes,
10739                )?;
10740            } else {
10741                engine.qmatvec_nvfp4_sel_into(
10742                    &gate_bank.bank,
10743                    &workspace.sel[rank_index],
10744                    aq,
10745                    ad,
10746                    &mut workspace.gate_out[rank_index],
10747                    n_sel,
10748                    gate_bank.in_features,
10749                    gate_bank.local_out,
10750                    gate_bank.row_bytes,
10751                    gate_bank.expert_bytes,
10752                    0,
10753                    0,
10754                )?;
10755                engine.qmatvec_nvfp4_sel_into(
10756                    &up_bank.bank,
10757                    &workspace.sel[rank_index],
10758                    aq,
10759                    ad,
10760                    &mut workspace.up_out[rank_index],
10761                    n_sel,
10762                    up_bank.in_features,
10763                    up_bank.local_out,
10764                    up_bank.row_bytes,
10765                    up_bank.expert_bytes,
10766                    0,
10767                    0,
10768                )?;
10769            }
10770            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
10771            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
10772            // input-column window (the geometry gift; see the method doc).
10773            {
10774                let Nvfp4DeviceRoutesWorkspace {
10775                    gate_out,
10776                    up_out,
10777                    sel,
10778                    act_q,
10779                    act_d,
10780                    ..
10781                } = &mut *workspace;
10782                engine.silu_mul_scaled_q8_1_sel_into(
10783                    &gate_out[rank_index],
10784                    &up_out[rank_index],
10785                    &experts.macros_gate_dev[rank_index],
10786                    &experts.macros_up_dev[rank_index],
10787                    &sel[rank_index],
10788                    activation_limit,
10789                    &mut act_q[rank_index],
10790                    &mut act_d[rank_index],
10791                    local_out,
10792                    n_sel,
10793                )?;
10794            }
10795            let shard = &experts.down[rank_index];
10796            if shard.device_rank != rank_index || shard.local_in != local_out {
10797                return Err(
10798                    "NVFP4 device routes: down canonical shard placement drifted from \
10799                     the gate/up column split"
10800                        .into(),
10801                );
10802            }
10803            // MEMRA_SEL_DOWN8=1: down sweep + route-weight combine in ONE launch, one warp
10804            // per SLOT instead of one warp per (row, slot) — the q8 `down8 w8` occupancy arm
10805            // (cx-downkernel: waves/SM 0.91 -> 4.36) ported to the NVFP4 banks. Bit-identical
10806            // (same dot program, same reduce tree, same slot-ordered chain), and the
10807            // n_sel x out_f partial buffer round trip disappears. Device-routed only: the
10808            // host-routed arm folds the macro into combine_w instead of reading md on device.
10809            let down8 = device_routed && sel_down8_on() && (shard.local_in >> 5) <= 32;
10810            {
10811                // MEMRA_SWEEP_TRACE=1: one receipt PER DISTINCT decision combo — a
10812                // silently-dead fusion reads as roofline physics without it (and the
10813                // prime's host-routed call must not swallow the decode receipt).
10814                static SEEN: std::sync::Mutex<Vec<(bool, bool)>> =
10815                    std::sync::Mutex::new(Vec::new());
10816                if std::env::var("MEMRA_SWEEP_TRACE").as_deref() == Ok("1") {
10817                    let mut seen = SEEN.lock().unwrap();
10818                    if !seen.contains(&(down8, device_routed)) {
10819                        seen.push((down8, device_routed));
10820                        eprintln!(
10821                            "[sweep-trace] down8={down8} device_routed={device_routed} \
10822                             sel_down8_on={} local_in={} n_sel={n_sel}",
10823                            sel_down8_on(),
10824                            shard.local_in
10825                        );
10826                    }
10827                }
10828            }
10829            if down8 {
10830                let Nvfp4DeviceRoutesWorkspace {
10831                    sel,
10832                    act_q,
10833                    act_d,
10834                    route_w,
10835                    accumulator,
10836                    ..
10837                } = &mut *workspace;
10838                engine.qmatvec_nvfp4_sel_down8_into(
10839                    &shard.bank,
10840                    &sel[rank_index],
10841                    &act_q[rank_index],
10842                    &act_d[rank_index],
10843                    &route_w[rank_index],
10844                    &experts.macros_down_dev[rank_index],
10845                    &mut accumulator[rank_index],
10846                    n_sel,
10847                    shard.local_in,
10848                    shard.out_features,
10849                    shard.row_bytes,
10850                    shard.expert_bytes,
10851                    local_out,
10852                    local_out / 32,
10853                )?;
10854            } else {
10855                let Nvfp4DeviceRoutesWorkspace {
10856                    sel,
10857                    act_q,
10858                    act_d,
10859                    partial,
10860                    ..
10861                } = &mut *workspace;
10862                engine.qmatvec_nvfp4_sel_into(
10863                    &shard.bank,
10864                    &sel[rank_index],
10865                    &act_q[rank_index],
10866                    &act_d[rank_index],
10867                    &mut partial[rank_index],
10868                    n_sel,
10869                    shard.local_in,
10870                    shard.out_features,
10871                    shard.row_bytes,
10872                    shard.expert_bytes,
10873                    local_out,
10874                    local_out / 32,
10875                )?;
10876            }
10877            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
10878            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
10879            // fold the down macro in-kernel from the device selection. (down8 already
10880            // produced the accumulator inside the sweep.)
10881            if !down8 {
10882                let Nvfp4DeviceRoutesWorkspace {
10883                    partial,
10884                    combine_w,
10885                    route_w,
10886                    sel,
10887                    accumulator,
10888                    ..
10889                } = &mut *workspace;
10890                if device_routed {
10891                    engine.axpy_rows_seq_md_into(
10892                        &partial[rank_index],
10893                        &route_w[rank_index],
10894                        &experts.macros_down_dev[rank_index],
10895                        &sel[rank_index],
10896                        &mut accumulator[rank_index],
10897                        experts.input_width,
10898                        n_sel,
10899                    )?;
10900                } else {
10901                    engine.axpy_rows_seq_into(
10902                        &partial[rank_index],
10903                        &combine_w[rank_index],
10904                        &mut accumulator[rank_index],
10905                        experts.input_width,
10906                        n_sel,
10907                    )?;
10908                }
10909            }
10910        }
10911        Ok(())
10912    }
10913
10914    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
10915    /// a device row on the model engine `e` and the combined output returns as a fresh
10916    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
10917    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
10918    /// the input's producer; each rank waits it before its peer read; the root reduce waits
10919    /// every rank's done event; `e` waits the root's done event before copying out. The
10920    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
10921    pub fn run_tensor_parallel_routes_nvfp4_device_io(
10922        &self,
10923        experts: &ResidentNvfp4TensorParallel,
10924        e: &Engine,
10925        input_dev: &crate::CudaSlice<f32>,
10926        selected: &[usize],
10927        route_weights: &[f32],
10928        experts_per_token: usize,
10929        activation_limit: Option<f32>,
10930    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10931        if input_dev.len() != experts.input_width {
10932            return Err(format!(
10933                "NVFP4 device-io routes input {} != width {}",
10934                input_dev.len(),
10935                experts.input_width
10936            )
10937            .into());
10938        }
10939        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10940            return Err(format!(
10941                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
10942                selected.len(),
10943                route_weights.len(),
10944            )
10945            .into());
10946        }
10947        if !route_weights.iter().all(|weight| weight.is_finite()) {
10948            return Err("NVFP4 device route weights contain a non-finite value".into());
10949        }
10950        let world = self.ranks.len();
10951        if world != NVFP4_CANONICAL_ROW_SHARDS {
10952            return Err(format!(
10953                "NVFP4 device routes require world == canonical shard grid \
10954                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10955            )
10956            .into());
10957        }
10958        let local_out = experts.expert_width / world;
10959        let n_sel = experts_per_token;
10960
10961        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10962        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10963        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10964        let started = timing.then(std::time::Instant::now);
10965
10966        let mut workspace_guard = experts
10967            .device_workspace
10968            .lock()
10969            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10970        if workspace_guard.is_none() {
10971            drop(workspace_guard);
10972            // Build through the host-IO ensure path exactly once: run it with a zero input.
10973            // Cheaper than duplicating the init; the first real call overwrites everything.
10974            let zero = vec![0.0f32; experts.input_width];
10975            let zero_sel = vec![0usize; n_sel];
10976            let zero_w = vec![0.0f32; n_sel];
10977            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10978                experts,
10979                &zero,
10980                &zero_sel,
10981                &zero_w,
10982                n_sel,
10983                activation_limit,
10984            )?;
10985            workspace_guard = experts
10986                .device_workspace
10987                .lock()
10988                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10989        }
10990        let workspace = workspace_guard
10991            .as_mut()
10992            .expect("NVFP4 device routes workspace initialized above");
10993        if workspace.n_sel != n_sel {
10994            return Err(format!(
10995                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10996                workspace.n_sel
10997            )
10998            .into());
10999        }
11000        for &expert in selected {
11001            if expert >= experts.expert_count {
11002                return Err(format!(
11003                    "NVFP4 device selected expert {expert} outside 0..{}",
11004                    experts.expert_count
11005                )
11006                .into());
11007            }
11008        }
11009        let sel_i32 = selected
11010            .iter()
11011            .map(|&expert| expert as i32)
11012            .collect::<Vec<_>>();
11013
11014        // Entry fence: e's stream position covers the input's producer AND every consumer of
11015        // the previous layer's output (queued on e's stream before this call), guarding the
11016        // workspace reuse exactly like the v2 attention driver.
11017        if let Some((_, device)) = workspace.ev_entry.as_ref() {
11018            if *device != e.ctx().ordinal() {
11019                return Err("NVFP4 device-io routes engine changed".into());
11020            }
11021        } else {
11022            let _main = e.gpu.enter_main()?;
11023            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
11024        }
11025        {
11026            let _main = e.gpu.enter_main()?;
11027            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11028            ev_entry.record(&e.stream())?;
11029        }
11030        for (rank_index, engine) in self.ranks.iter().enumerate() {
11031            let _main = engine.gpu.enter_main()?;
11032            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11033            engine.stream().wait(ev_entry)?;
11034            {
11035                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
11036                engine
11037                    .stream()
11038                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
11039            }
11040            {
11041                let Nvfp4DeviceRoutesWorkspace {
11042                    input, in_q, in_d, ..
11043                } = &mut *workspace;
11044                engine.quantize_q8_1_into(
11045                    &input[rank_index],
11046                    1,
11047                    experts.input_width,
11048                    &mut in_q[rank_index],
11049                    &mut in_d[rank_index],
11050                )?;
11051            }
11052        }
11053        self.nvfp4_routes_batched_sweeps(
11054            experts,
11055            workspace,
11056            selected,
11057            route_weights,
11058            &sel_i32,
11059            local_out,
11060            n_sel,
11061            activation_limit,
11062            false,
11063        )?;
11064
11065        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
11066        // the root stream in canonical shard order, and e copies the combined row out behind
11067        // the root's done event.
11068        // rank0 == root: its own stream order already covers its sweep; only the PEER
11069        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
11070        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
11071            let _main = engine.gpu.enter_main()?;
11072            workspace.ev_rank[rank_index].record(&engine.stream())?;
11073        }
11074        if moe_direct_on() && self.ranks.len() == 2 {
11075            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
11076            // rank0's is root-stream-ordered. One root event + rank1's own event order
11077            // the model engine's single add — same operand order as root's add
11078            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
11079            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
11080            // hazard class does not apply).
11081            {
11082                let root = &self.ranks[0];
11083                let _main = root.gpu.enter_main()?;
11084                workspace
11085                    .ev_done
11086                    .as_ref()
11087                    .expect("device routes done event")
11088                    .record(&root.stream())?;
11089            }
11090            let _main = e.gpu.enter_main()?;
11091            e.stream().wait(
11092                workspace
11093                    .ev_done
11094                    .as_ref()
11095                    .expect("device routes done event"),
11096            )?;
11097            for ev in workspace.ev_rank.iter().skip(1) {
11098                e.stream().wait(ev)?;
11099            }
11100            let mut output = e.uninit(experts.input_width)?;
11101            e.add(
11102                &workspace.accumulator[0],
11103                &workspace.accumulator[1],
11104                &mut output,
11105                experts.input_width,
11106            )?;
11107            let output = output;
11108            if let Some(started) = started {
11109                use std::sync::atomic::Ordering;
11110                let ns = TIMING_NS
11111                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11112                    + started.elapsed().as_nanos() as u64;
11113                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11114                if calls % 430 == 0 {
11115                    eprintln!(
11116                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11117                        ns as f64 / 1.0e6,
11118                        ns as f64 / calls as f64 / 1.0e3,
11119                    );
11120                }
11121            }
11122            return Ok(output);
11123        }
11124        {
11125            let root = &self.ranks[0];
11126            let _main = root.gpu.enter_main()?;
11127            for ev in workspace.ev_rank.iter().skip(1) {
11128                root.stream().wait(ev)?;
11129            }
11130            root.stream()
11131                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
11132            {
11133                let Nvfp4DeviceRoutesWorkspace {
11134                    accumulator,
11135                    remote,
11136                    combined,
11137                    ..
11138                } = &mut *workspace;
11139                root.add(&accumulator[0], remote, combined, experts.input_width)?;
11140            }
11141            workspace
11142                .ev_done
11143                .as_ref()
11144                .expect("device routes done event")
11145                .record(&root.stream())?;
11146        }
11147        let output = {
11148            let _main = e.gpu.enter_main()?;
11149            e.stream().wait(
11150                workspace
11151                    .ev_done
11152                    .as_ref()
11153                    .expect("device routes done event"),
11154            )?;
11155            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
11156            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
11157            let mut output = e.uninit(experts.input_width)?;
11158            e.stream().memcpy_dtod(
11159                &workspace.combined.slice(0..experts.input_width),
11160                &mut output.slice_mut(0..experts.input_width),
11161            )?;
11162            output
11163        };
11164        if let Some(started) = started {
11165            use std::sync::atomic::Ordering;
11166            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11167                + started.elapsed().as_nanos() as u64;
11168            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11169            if calls % 430 == 0 {
11170                eprintln!(
11171                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11172                    ns as f64 / 1.0e6,
11173                    ns as f64 / calls as f64 / 1.0e3,
11174                );
11175            }
11176        }
11177        Ok(output)
11178    }
11179
11180    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
11181    /// route weights arrive as the device router's e-context outputs — the per-layer host
11182    /// logits readback disappears. The fresh router outputs are staged into persistent
11183    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
11184    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
11185    #[allow(clippy::too_many_arguments)]
11186    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
11187    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
11188    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
11189    /// the routed run then does its own staging as before.
11190    pub fn nvfp4_routes_prestage(
11191        &self,
11192        experts: &ResidentNvfp4TensorParallel,
11193        e: &Engine,
11194        input_dev: &crate::CudaSlice<f32>,
11195    ) -> Result<bool, Box<dyn std::error::Error>> {
11196        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
11197    }
11198
11199    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
11200    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
11201    /// deterministic kernels on identical input bits produce identical sel/w, so the
11202    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
11203    /// routed run then skips rank1's sel pull.
11204    pub fn nvfp4_routes_prestage_with(
11205        &self,
11206        experts: &ResidentNvfp4TensorParallel,
11207        e: &Engine,
11208        input_dev: &crate::CudaSlice<f32>,
11209        rank1_router: impl FnOnce(
11210            &Engine,
11211            &crate::CudaSlice<f32>,
11212            &mut crate::CudaSlice<i32>,
11213            &mut crate::CudaSlice<f32>,
11214        ) -> Result<bool, Box<dyn std::error::Error>>,
11215    ) -> Result<bool, Box<dyn std::error::Error>> {
11216        if !routes_prestage_on() || step_tp_graph_enabled()? {
11217            return Ok(false);
11218        }
11219        if input_dev.len() != experts.input_width {
11220            return Err("NVFP4 prestage input width mismatch".into());
11221        }
11222        let mut workspace_guard = experts
11223            .device_workspace
11224            .lock()
11225            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11226        let Some(workspace) = workspace_guard.as_mut() else {
11227            return Ok(false);
11228        };
11229        if workspace.ev_input.is_none() {
11230            let _main = e.gpu.enter_main()?;
11231            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
11232        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
11233            return Err("NVFP4 prestage engine changed".into());
11234        }
11235        {
11236            let _main = e.gpu.enter_main()?;
11237            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
11238            ev.record(&e.stream())?;
11239        }
11240        for (rank_index, engine) in self.ranks.iter().enumerate() {
11241            let _main = engine.gpu.enter_main()?;
11242            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
11243            engine.stream().wait(ev)?;
11244            {
11245                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
11246                engine
11247                    .stream()
11248                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
11249            }
11250            {
11251                let Nvfp4DeviceRoutesWorkspace {
11252                    input, in_q, in_d, ..
11253                } = &mut *workspace;
11254                engine.quantize_q8_1_into(
11255                    &input[rank_index],
11256                    1,
11257                    experts.input_width,
11258                    &mut in_q[rank_index],
11259                    &mut in_d[rank_index],
11260                )?;
11261            }
11262        }
11263        if self.ranks.len() == 2 {
11264            let rank1 = &self.ranks[1];
11265            let _r1 = rank1.gpu.enter_main()?;
11266            let Nvfp4DeviceRoutesWorkspace {
11267                input,
11268                sel,
11269                route_w,
11270                ..
11271            } = &mut *workspace;
11272            let (in1, rest_sel) = (&input[1], &mut sel[1]);
11273            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
11274                workspace.rank1_routed = true;
11275            }
11276        }
11277        workspace.prestaged = true;
11278        Ok(true)
11279    }
11280
11281    /// TWO-COLUMN device-routed expert program (spec verify, MEMRA_TCOL_FFN): one gu_tcol
11282    /// sweep over 2*n_sel_col pairs (pair t reads activation row t/n_sel_col — weights the
11283    /// two columns share dedup through L2), the UNCHANGED silu/down kernels at n_sel=16
11284    /// (both already index per pair), and one offset-axpy combine per column (the exact
11285    /// t=1 sequential chain over that column's 8 pairs). No serving doors: no graph, no
11286    /// prestage, no shexp folding — plain evented ordering. Returns [2, input_width] on e.
11287    ///
11288    /// EXACTNESS: every kernel body is the t=1 program per (pair,row) or per element; the
11289    /// per-column combine order equals the t=1 combine; the cross-rank join adds the same
11290    /// operand values elementwise. Gated by the greedy tape like every verify arm.
11291    #[allow(clippy::too_many_arguments)]
11292    pub fn run_tensor_parallel_routes_nvfp4_device_routed_tn(
11293        &self,
11294        experts: &ResidentNvfp4TensorParallel,
11295        e: &Engine,
11296        z_t: &crate::CudaSlice<f32>,
11297        sel_d: &crate::CudaSlice<i32>,
11298        w_d: &crate::CudaSlice<f32>,
11299        t: usize,
11300        n_sel_col: usize,
11301        activation_limit: Option<f32>,
11302    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11303        self.run_tensor_parallel_routes_nvfp4_device_routed_tn_prejoin(
11304            experts,
11305            e,
11306            z_t,
11307            sel_d,
11308            w_d,
11309            t,
11310            n_sel_col,
11311            activation_limit,
11312            || Ok(()),
11313        )
11314    }
11315
11316    /// `_tn` with the plain tick's PREJOIN window (2026-08-26): `pre_join` is invoked after every
11317    /// rank's sweep is issued and before the root blocks on the peer event, so independent caller
11318    /// kernels fill the peer drain instead of leaving the stream idle. The no-hook wrapper above
11319    /// keeps the old call shape; passing a closure is the only behavioural difference, and it moves
11320    /// ISSUE ORDER only — see the hook site for why that keeps the arm bit-gateable.
11321    #[allow(clippy::too_many_arguments)]
11322    pub fn run_tensor_parallel_routes_nvfp4_device_routed_tn_prejoin(
11323        &self,
11324        experts: &ResidentNvfp4TensorParallel,
11325        e: &Engine,
11326        z_t: &crate::CudaSlice<f32>,
11327        sel_d: &crate::CudaSlice<i32>,
11328        w_d: &crate::CudaSlice<f32>,
11329        t: usize,
11330        n_sel_col: usize,
11331        activation_limit: Option<f32>,
11332        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11333    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11334        let world = self.ranks.len();
11335        if world != NVFP4_CANONICAL_ROW_SHARDS {
11336            return Err("NVFP4 t-row routes require the canonical 2-shard grid".into());
11337        }
11338        let width = experts.input_width;
11339        let n_sel = t * n_sel_col;
11340        if t == 0 || t > 32 || z_t.len() < t * width || sel_d.len() < n_sel || w_d.len() < n_sel {
11341            return Err("NVFP4 t-row routes geometry".into());
11342        }
11343        if !nvfp4_bank_v2_on() {
11344            return Err("NVFP4 t-row routes require the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
11345        }
11346        let local_out = experts.expert_width / world;
11347        let mut guard = experts
11348            .t2_workspace
11349            .lock()
11350            .map_err(|_| "NVFP4 t2 workspace lock is poisoned")?;
11351        if nvfp4_trow_workspace_needs_grow(guard.as_ref().map(|ws| (ws.t_cap, ws.n_sel)), t, n_sel)
11352        {
11353            let t_cap = guard.as_ref().map_or(t, |ws| ws.t_cap.max(t));
11354            let n_sel_cap = guard.as_ref().map_or(n_sel, |ws| ws.n_sel.max(n_sel));
11355            let mut input2 = Vec::new();
11356            let mut in_q2 = Vec::new();
11357            let mut in_d2 = Vec::new();
11358            let mut sel2 = Vec::new();
11359            let mut route_w2 = Vec::new();
11360            let mut gate_out2 = Vec::new();
11361            let mut up_out2 = Vec::new();
11362            let mut act_q2 = Vec::new();
11363            let mut act_d2 = Vec::new();
11364            let mut partial2 = Vec::new();
11365            let mut acc_a = Vec::new();
11366            let mut acc_b = Vec::new();
11367            let mut acc2 = Vec::new();
11368            let mut ev_rank = Vec::new();
11369            for engine in &self.ranks {
11370                let _m = engine.gpu.enter_main()?;
11371                input2.push(engine.uninit(t_cap * width)?);
11372                in_q2.push(engine.alloc_i8_uninit(t_cap * width)?);
11373                in_d2.push(engine.uninit(t_cap * (width / 32))?);
11374                sel2.push(engine.htod_i32(&vec![0i32; n_sel_cap])?);
11375                route_w2.push(engine.uninit(n_sel_cap)?);
11376                gate_out2.push(engine.uninit(n_sel_cap * local_out)?);
11377                up_out2.push(engine.uninit(n_sel_cap * local_out)?);
11378                act_q2.push(engine.alloc_i8_uninit(n_sel_cap * local_out)?);
11379                act_d2.push(engine.uninit(n_sel_cap * (local_out / 32))?);
11380                partial2.push(engine.uninit(n_sel_cap * width)?);
11381                acc_a.push(engine.uninit(width)?);
11382                acc_b.push(engine.uninit(width)?);
11383                acc2.push(engine.uninit(t_cap * width)?);
11384                ev_rank.push(engine.ctx().new_event(None)?);
11385            }
11386            let root = &self.ranks[0];
11387            let (peer_a, peer_b, omix_a, omix_b, peer2, omix2, ev_root) = {
11388                let _m = root.gpu.enter_main()?;
11389                (
11390                    root.uninit(width)?,
11391                    root.uninit(width)?,
11392                    root.uninit(width)?,
11393                    root.uninit(width)?,
11394                    root.uninit(t_cap * width)?,
11395                    root.uninit(t_cap * width)?,
11396                    root.ctx().new_event(None)?,
11397                )
11398            };
11399            let ev_entry = {
11400                let _m = e.gpu.enter_main()?;
11401                e.ctx().new_event(None)?
11402            };
11403            *guard = Some(Nvfp4T2Workspace {
11404                input2,
11405                in_q2,
11406                in_d2,
11407                sel2,
11408                route_w2,
11409                gate_out2,
11410                up_out2,
11411                act_q2,
11412                act_d2,
11413                partial2,
11414                acc_a,
11415                acc_b,
11416                acc2,
11417                peer2,
11418                omix2,
11419                peer_a,
11420                peer_b,
11421                omix_a,
11422                omix_b,
11423                ev_entry,
11424                ev_rank,
11425                ev_root,
11426                t_cap,
11427                n_sel: n_sel_cap,
11428                e_device: e.ctx().ordinal(),
11429            });
11430        }
11431        let ws = guard.as_mut().expect("armed above");
11432        if ws.e_device != e.ctx().ordinal() {
11433            return Err("NVFP4 t2 routes engine changed".into());
11434        }
11435        {
11436            let _main = e.gpu.enter_main()?;
11437            ws.ev_entry.record(&e.stream())?;
11438        }
11439        // One decision for the sweep AND the join (an acc2 the sweep never wrote must
11440        // never be joined). t > 2 has no split-accumulator fallback: it requires the
11441        // fused rows kernel.
11442        let down8 = sel_down8_on() && (local_out >> 5) <= 32 && n_sel_col <= 8;
11443        if !down8 && t != 2 {
11444            return Err(
11445                "NVFP4 t-row routes at t != 2 require MEMRA_SEL_DOWN8=1 (fused rows kernel)".into(),
11446            );
11447        }
11448        for rank in 0..world {
11449            let engine = &self.ranks[rank];
11450            let _main = engine.gpu.enter_main()?;
11451            engine.stream().wait(&ws.ev_entry)?;
11452            {
11453                let mut dst = ws.input2[rank].slice_mut(0..t * width);
11454                engine
11455                    .stream()
11456                    .memcpy_dtod(&z_t.slice(0..t * width), &mut dst)?;
11457            }
11458            {
11459                let mut dst = ws.sel2[rank].slice_mut(0..n_sel);
11460                engine
11461                    .stream()
11462                    .memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11463            }
11464            {
11465                let mut dst = ws.route_w2[rank].slice_mut(0..n_sel);
11466                engine
11467                    .stream()
11468                    .memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11469            }
11470            {
11471                let Nvfp4T2Workspace {
11472                    input2,
11473                    in_q2,
11474                    in_d2,
11475                    ..
11476                } = &mut *ws;
11477                engine.quantize_q8_1_into(
11478                    &input2[rank],
11479                    t,
11480                    width,
11481                    &mut in_q2[rank],
11482                    &mut in_d2[rank],
11483                )?;
11484            }
11485            let gate_bank = &experts.gate[rank];
11486            let up_bank = &experts.up[rank];
11487            if gate_bank.in_features != up_bank.in_features
11488                || gate_bank.local_out != up_bank.local_out
11489                || gate_bank.row_bytes != up_bank.row_bytes
11490                || gate_bank.expert_bytes != up_bank.expert_bytes
11491            {
11492                return Err("NVFP4 t-row routes need matched gate/up bank geometry".into());
11493            }
11494            {
11495                let Nvfp4T2Workspace {
11496                    sel2,
11497                    in_q2,
11498                    in_d2,
11499                    gate_out2,
11500                    up_out2,
11501                    ..
11502                } = &mut *ws;
11503                engine.qmatvec_nvfp4_sel_gu_tcol_into(
11504                    &gate_bank.bank,
11505                    &up_bank.bank,
11506                    &sel2[rank],
11507                    &in_q2[rank],
11508                    &in_d2[rank],
11509                    &mut gate_out2[rank],
11510                    &mut up_out2[rank],
11511                    n_sel,
11512                    n_sel_col,
11513                    gate_bank.in_features,
11514                    gate_bank.local_out,
11515                    gate_bank.row_bytes,
11516                    gate_bank.expert_bytes,
11517                    width,
11518                    width / 32,
11519                )?;
11520            }
11521            {
11522                let Nvfp4T2Workspace {
11523                    gate_out2,
11524                    up_out2,
11525                    sel2,
11526                    act_q2,
11527                    act_d2,
11528                    ..
11529                } = &mut *ws;
11530                engine.silu_mul_scaled_q8_1_sel_into(
11531                    &gate_out2[rank],
11532                    &up_out2[rank],
11533                    &experts.macros_gate_dev[rank],
11534                    &experts.macros_up_dev[rank],
11535                    &sel2[rank],
11536                    activation_limit,
11537                    &mut act_q2[rank],
11538                    &mut act_d2[rank],
11539                    local_out,
11540                    n_sel,
11541                )?;
11542            }
11543            let shard = &experts.down[rank];
11544            if shard.device_rank != rank || shard.local_in != local_out {
11545                return Err("NVFP4 t-row routes: down shard placement drifted".into());
11546            }
11547            // MEMRA_SEL_DOWN8=1: down sweep + per-row combine in ONE launch (t2 twin of
11548            // the t=1 fusion) — kills the n_sel x width partial round-trip and both axpy
11549            // passes. Each row's FP chain == its own down8/axpy pair (bit-identical).
11550            if down8 {
11551                let Nvfp4T2Workspace {
11552                    sel2,
11553                    act_q2,
11554                    act_d2,
11555                    route_w2,
11556                    acc2,
11557                    ..
11558                } = &mut *ws;
11559                engine.qmatvec_nvfp4_sel_down8_rows_into(
11560                    &shard.bank,
11561                    &sel2[rank],
11562                    &act_q2[rank],
11563                    &act_d2[rank],
11564                    &route_w2[rank],
11565                    &experts.macros_down_dev[rank],
11566                    &mut acc2[rank],
11567                    t,
11568                    n_sel_col,
11569                    shard.local_in,
11570                    shard.out_features,
11571                    shard.row_bytes,
11572                    shard.expert_bytes,
11573                    local_out,
11574                    local_out / 32,
11575                )?;
11576            } else {
11577                {
11578                    let Nvfp4T2Workspace {
11579                        sel2,
11580                        act_q2,
11581                        act_d2,
11582                        partial2,
11583                        ..
11584                    } = &mut *ws;
11585                    engine.qmatvec_nvfp4_sel_into(
11586                        &shard.bank,
11587                        &sel2[rank],
11588                        &act_q2[rank],
11589                        &act_d2[rank],
11590                        &mut partial2[rank],
11591                        n_sel,
11592                        shard.local_in,
11593                        shard.out_features,
11594                        shard.row_bytes,
11595                        shard.expert_bytes,
11596                        local_out,
11597                        local_out / 32,
11598                    )?;
11599                }
11600                let Nvfp4T2Workspace {
11601                    partial2,
11602                    route_w2,
11603                    sel2,
11604                    acc_a,
11605                    acc_b,
11606                    ..
11607                } = &mut *ws;
11608                engine.axpy_rows_seq_md_off_into(
11609                    &partial2[rank],
11610                    &route_w2[rank],
11611                    &experts.macros_down_dev[rank],
11612                    &sel2[rank],
11613                    &mut acc_a[rank],
11614                    width,
11615                    n_sel_col,
11616                    0,
11617                )?;
11618                engine.axpy_rows_seq_md_off_into(
11619                    &partial2[rank],
11620                    &route_w2[rank],
11621                    &experts.macros_down_dev[rank],
11622                    &sel2[rank],
11623                    &mut acc_b[rank],
11624                    width,
11625                    n_sel_col,
11626                    n_sel_col,
11627                )?;
11628            }
11629            if rank != 0 {
11630                ws.ev_rank[rank].record(&engine.stream())?;
11631            }
11632        }
11633        // PREJOIN hook, ported from the plain decode tick's `_routed_prejoin` arm (2026-08-26).
11634        // Every rank's sweep is ISSUED by here and dev1 is running, but the join below blocks the
11635        // root on the peer's event, so the caller's stream sits idle across the peer drain. The
11636        // plain decode path spends that window on independent work and the verify walk did not,
11637        // which is most of why the walk costs ~1.27x the plain tick at equal t (the other factor,
11638        // column scaling at 0.42x/column, already matches the vLLM reference).
11639        // Kernels queued here MUST be independent of the routed result. The caller's shared expert
11640        // is: its compute moves in here, its accumulate stays after the join. Issue order moves,
11641        // the float expression does not, so the arm is bit-gateable by construction.
11642        {
11643            let _main = e.gpu.enter_main()?;
11644            pre_join()?;
11645        }
11646        let root = &self.ranks[0];
11647        {
11648            let _main = root.gpu.enter_main()?;
11649            for ev in ws.ev_rank.iter().skip(1) {
11650                root.stream().wait(ev)?;
11651            }
11652            if down8 {
11653                // Fused-slab join: ONE peer pull + ONE elementwise add cover every
11654                // row (independent elements; per-element op == the split join).
11655                let Nvfp4T2Workspace {
11656                    acc2, peer2, omix2, ..
11657                } = &mut *ws;
11658                {
11659                    let mut dst = peer2.slice_mut(0..t * width);
11660                    root.stream()
11661                        .memcpy_dtod(&acc2[1].slice(0..t * width), &mut dst)?;
11662                }
11663                root.add(&acc2[0], peer2, omix2, t * width)?;
11664            } else {
11665                let Nvfp4T2Workspace {
11666                    acc_a,
11667                    acc_b,
11668                    peer_a,
11669                    peer_b,
11670                    omix_a,
11671                    omix_b,
11672                    ..
11673                } = &mut *ws;
11674                {
11675                    let mut dst = peer_a.slice_mut(0..width);
11676                    root.stream()
11677                        .memcpy_dtod(&acc_a[1].slice(0..width), &mut dst)?;
11678                }
11679                {
11680                    let mut dst = peer_b.slice_mut(0..width);
11681                    root.stream()
11682                        .memcpy_dtod(&acc_b[1].slice(0..width), &mut dst)?;
11683                }
11684                root.add(&acc_a[0], peer_a, omix_a, width)?;
11685                root.add(&acc_b[0], peer_b, omix_b, width)?;
11686            }
11687            ws.ev_root.record(&root.stream())?;
11688        }
11689        let _main = e.gpu.enter_main()?;
11690        e.stream().wait(&ws.ev_root)?;
11691        let mut out = e.uninit(t * width)?;
11692        if down8 {
11693            e.stream().memcpy_dtod(
11694                &ws.omix2.slice(0..t * width),
11695                &mut out.slice_mut(0..t * width),
11696            )?;
11697        } else {
11698            e.stream()
11699                .memcpy_dtod(&ws.omix_a.slice(0..width), &mut out.slice_mut(0..width))?;
11700            e.stream().memcpy_dtod(
11701                &ws.omix_b.slice(0..width),
11702                &mut out.slice_mut(width..2 * width),
11703            )?;
11704        }
11705        Ok(out)
11706    }
11707
11708    /// STEP TP2 GEMM PRIME (`MEMRA_STEP_GEMM_PRIME`, 2026-08-27, TTFT lane): one grouped
11709    /// f16 GEMM per projection over the RESIDENT NVFP4 banks for a prime chunk of `t` tokens.
11710    ///
11711    /// WHY: the t-row walk primes a 4,092-token prompt in 19.8 s at its widest (GEMV-bound) and
11712    /// the generic batch prime's decode-class MoE takes 240 s; the CUTLASS sizing rows put
11713    /// GEMM-class expert math at 170-270 TFLOP/s on this silicon, i.e. a sub-second cold prime.
11714    /// This reuses the grouped f16 lane end to end (`moe_f16g_act` -> `moe_f16_grouped`
11715    /// direct-from-NVFP4 -> silu pairs -> grouped down) once per RANK against that rank's bank
11716    /// half: gate/up are column-halves (silu runs on matching halves), down is the canonical
11717    /// row-shard pair producing partials joined in the pinned shard order, and the final
11718    /// weighted scatter runs a fixed slot-0..n_used-1 sum per token - no atomics anywhere.
11719    /// Per-expert NVFP4 macro scales land where they must: gate/up BEFORE silu (nonlinear),
11720    /// down folded into the scatter weight.
11721    ///
11722    /// NUMERIC CLASS: the f16-mirror grouped-prefill class other families already serve -
11723    /// admission is the prefill-KV acceptance gate plus the ship-shape tape, not byte identity.
11724    #[allow(clippy::too_many_arguments)]
11725    /// MEMRA_MOE_DETERM_STAGE=1: checksum a stage's device buffer so two back-to-back calls of the
11726    /// grouped routine can be compared STAGE BY STAGE. The routine's OUTPUT is nondeterministic above
11727    /// ~400 tokens on the direct lane (1.9e-7 / 99% of elements at t=4096) while its GEMM kernels are
11728    /// bit-exact in isolation, so the divergence enters somewhere between. The first stage whose
11729    /// checksum differs across the two calls is where.
11730    ///
11731    /// Sum-of-bits, not sum-of-floats: float addition would itself reorder and could mask exactly the
11732    /// class of difference being hunted.
11733    fn determ_stage_bytes(v: &[u8]) -> u64 {
11734        v.iter().fold(0u64, |a, b| {
11735            a.wrapping_mul(1_000_003).wrapping_add(*b as u64)
11736        })
11737    }
11738
11739    /// Checksum an i32 index/offset buffer. The CSR, the active-expert ids and the group
11740    /// offsets are inputs the gate kernel dereferences just as much as the activations are;
11741    /// leaving them unchecksummed is what let "identical inputs, different output" stand on a
11742    /// SUBSET of the inputs for six rounds of this investigation.
11743    fn determ_stage_i32(v: &[i32]) -> u64 {
11744        v.iter().fold(0u64, |a, b| {
11745            a.wrapping_mul(1_000_003).wrapping_add(*b as u32 as u64)
11746        })
11747    }
11748
11749    fn determ_stage_sum(v: &[f32]) -> u64 {
11750        v.iter().fold(0u64, |a, x| {
11751            a.wrapping_mul(1_000_003).wrapping_add(x.to_bits() as u64)
11752        })
11753    }
11754
11755    pub fn run_tensor_parallel_routes_nvfp4_prime_grouped(
11756        &self,
11757        experts: &ResidentNvfp4TensorParallel,
11758        e: &Engine,
11759        z_t: &crate::CudaSlice<f32>,
11760        t: usize,
11761        sel: &[i32],
11762        w: &[f32],
11763        n_used: usize,
11764        activation_limit: Option<f32>,
11765    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11766        let world = self.ranks.len();
11767        if world != NVFP4_CANONICAL_ROW_SHARDS {
11768            return Err("NVFP4 grouped prime requires the canonical 2-shard grid".into());
11769        }
11770        // The banks are v2-permuted when the door is on (serving default) — the dequant must
11771        // read the matching layout. Feeding v2 bytes to the v1 kernel was the garbage-output
11772        // bug this receipt line exists for.
11773        let bank_qt = if nvfp4_bank_v2_on() {
11774            crate::QT_NVFP4_V2
11775        } else {
11776            crate::QT_NVFP4
11777        };
11778        let width = experts.input_width;
11779        let n_expert = experts.expert_count;
11780        let n_pairs = t * n_used;
11781        if sel.len() < n_pairs || w.len() < n_pairs || z_t.len() < t * width {
11782            return Err("NVFP4 grouped prime geometry".into());
11783        }
11784        // MEMRA_PRIME_PROF=1 sub-split of the grouped prime (2026-08-28). The [moe-prof] mark
11785        // around this whole call reads 90% of the MoE bucket, but the call is not just GEMMs:
11786        // it host-builds the CSR, allocates ~6 large device buffers per rank per layer (z_r is
11787        // 67 MB, act is 84 MB at t=4096), and does 5 H2D copies per rank. Tile form, occupancy,
11788        // padding, B double-buffering and register pressure have ALL come back null, which is
11789        // the signature of time that is not in the kernel. So measure HOST wall with no syncs
11790        // for the build and the issue, and let the join wait absorb the GPU time: host-bound and
11791        // GPU-bound then read differently instead of summing into one opaque number.
11792        let gprof = std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1") && t >= 16;
11793        let g_t0 = std::time::Instant::now();
11794        // CSR: expert-major pair lists. Host-built - prime is chunk-granular, and the router
11795        // selections arrive host-side from the sigmoid router oracle.
11796        let mut buckets: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11797        for (p, &s_id) in sel.iter().take(n_pairs).enumerate() {
11798            let s_id = s_id as usize;
11799            if s_id >= n_expert {
11800                return Err(format!("grouped prime selection {s_id} >= {n_expert}").into());
11801            }
11802            buckets[s_id].push(p as i32);
11803        }
11804        let mut ex_ids: Vec<i32> = Vec::new();
11805        let mut ex_off: Vec<i32> = vec![0];
11806        let mut ex_pairs: Vec<i32> = Vec::new();
11807        for (e_id, b) in buckets.iter().enumerate() {
11808            if !b.is_empty() {
11809                ex_ids.push(e_id as i32);
11810                ex_pairs.extend_from_slice(b);
11811                ex_off.push(ex_pairs.len() as i32);
11812            }
11813        }
11814        let n_active = ex_ids.len();
11815        if n_active == 0 {
11816            return Ok(e.zeros(t * width)?);
11817        }
11818        if n_active > 512 {
11819            return Err("grouped prime n_active > 512 (direct lane cap)".into());
11820        }
11821        let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11822        // pair-id -> CSR row: lets the fused tail read the partials in place, so the prime skips
11823        // a whole [n_pairs, width] permute (532 MB read + write per rank per layer at 4k).
11824        let mut inv = vec![0i32; n_pairs];
11825        for (row, &pair) in ex_pairs.iter().enumerate() {
11826            inv[pair as usize] = row as i32;
11827        }
11828        // Per-CSR-row gate/up macro scales (before silu); down macro folds into the scatter w.
11829        let mg: Vec<f32> = ex_pairs
11830            .iter()
11831            .map(|&p| experts.macros_gate[sel[p as usize] as usize])
11832            .collect();
11833        let mu: Vec<f32> = ex_pairs
11834            .iter()
11835            .map(|&p| experts.macros_up[sel[p as usize] as usize])
11836            .collect();
11837        let wd: Vec<f32> = (0..n_pairs)
11838            .map(|p| w[p] * experts.macros_down[sel[p] as usize])
11839            .collect();
11840        // Pointer tables: built on first use and kept on the bank. Resident banks never move,
11841        // so the old per-rank-per-LAYER rebuild+upload of 3*n_expert u64s was pure prime-path
11842        // host churn (45 layers x 2 ranks x 864 entries per prime).
11843        {
11844            let mut tabs = experts
11845                .prime_tables
11846                .lock()
11847                .map_err(|_| "grouped prime table cache is poisoned")?;
11848            if tabs.len() != world {
11849                tabs.clear();
11850                for rank in 0..world {
11851                    let engine = &self.ranks[rank];
11852                    let _main = engine.gpu.enter_main()?;
11853                    let (gb, ub, db) =
11854                        (&experts.gate[rank], &experts.up[rank], &experts.down[rank]);
11855                    let mut tab = vec![0u64; 3 * n_expert];
11856                    {
11857                        use cudarc::driver::DevicePtr;
11858                        let stream = engine.stream();
11859                        let (pg, _g0) = gb.bank.device_ptr(&stream);
11860                        let (pu, _g1) = ub.bank.device_ptr(&stream);
11861                        let (pd, _g2) = db.bank.device_ptr(&stream);
11862                        for ex in 0..n_expert {
11863                            tab[ex] = pg as u64 + (ex * gb.expert_bytes) as u64;
11864                            tab[n_expert + ex] = pu as u64 + (ex * ub.expert_bytes) as u64;
11865                            tab[2 * n_expert + ex] = pd as u64 + (ex * db.expert_bytes) as u64;
11866                        }
11867                    }
11868                    tabs.push(engine.htod_u64(&tab)?);
11869                }
11870            }
11871        }
11872        let g_csr = g_t0.elapsed().as_secs_f64() * 1e3;
11873        let g_t1 = std::time::Instant::now();
11874        // WHAT ARE THESE RANKS, ACTUALLY (2026-08-28)? The grouped MoE measures join ~ span_sum
11875        // (strictly serialized) at t=4096 while the same kernel hits 40 TFLOP/s standalone, and
11876        // one intervention based on cudarc's peer-copy event was refuted. Before proposing an
11877        // eleventh mechanism, verify the premise the whole question rests on: that the two ranks
11878        // are on DISTINCT devices, contexts and streams. If they share any of those, the
11879        // serialization needs no further explanation. One line per process.
11880        {
11881            static SAID: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
11882            if gprof && !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
11883                for rank in 0..world {
11884                    let e_r = &self.ranks[rank];
11885                    let _m = e_r.gpu.enter_main();
11886                    eprintln!(
11887                        "[rank-id] rank={rank} ordinal={} ctx={:?} stream={:?} root_ordinal={} \
11888                         root_stream={:?}",
11889                        e_r.ctx().ordinal(),
11890                        std::sync::Arc::as_ptr(&e_r.ctx()),
11891                        e_r.stream().cu_stream(),
11892                        e.ctx().ordinal(),
11893                        e.stream().cu_stream(),
11894                    );
11895                }
11896            }
11897        }
11898
11899        let mut partials: Vec<crate::CudaSlice<f32>> = Vec::with_capacity(world);
11900        let mut ev_rank: Vec<CudaEvent> = Vec::with_capacity(world);
11901        let mut ev_head: Vec<CudaEvent> = Vec::with_capacity(world);
11902        let mut ev_tail_prof: Vec<CudaEvent> = Vec::with_capacity(world);
11903        for rank in 0..world {
11904            let engine = &self.ranks[rank];
11905            let _main = engine.gpu.enter_main()?;
11906            if gprof {
11907                // CU_EVENT_DEFAULT, not None: cudarc's new_event(None) creates the event with
11908                // CU_EVENT_DISABLE_TIMING, and cuEventElapsedTime then returns INVALID_HANDLE.
11909                // That is what failed every span query for two build cycles — the ordering
11910                // events below correctly keep the default, since they are never timed.
11911                let h = engine
11912                    .ctx()
11913                    .new_event(Some(cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT))?;
11914                h.record(&engine.stream())?;
11915                ev_head.push(h);
11916            }
11917            // The grouped-MoE FFI's raw launches follow the RUNTIME API's current device, not
11918            // the pushed driver context — bind it per rank or rank-1 calls die InvalidValue.
11919            engine.bind_runtime_device(engine.ctx().ordinal() as i32)?;
11920            let gb = &experts.gate[rank];
11921            let ub = &experts.up[rank];
11922            let db = &experts.down[rank];
11923            if db.device_rank != rank {
11924                return Err("grouped prime: down shard placement drifted".into());
11925            }
11926            let local_ff = gb.local_out;
11927            if ub.local_out != local_ff || db.local_in != local_ff || db.out_features != width {
11928                return Err("grouped prime: bank width mismatch".into());
11929            }
11930            // All of the rank's host-side staging lands before its first kernel, so the
11931            // launch chain below issues without host copies interleaved.
11932            let csr_tok_d = engine.htod_i32(&csr_tok)?;
11933            let exi_d = engine.htod_i32(&ex_ids)?;
11934            let exoff_d = engine.htod_i32(&ex_off)?;
11935            let mg_d = engine.htod(&mg)?;
11936            let mu_d = engine.htod(&mu)?;
11937            // Per-rank pointer table into the bank shards, slot-major like DevExps::ptr_row.
11938            let tabs_guard = experts
11939                .prime_tables
11940                .lock()
11941                .map_err(|_| "grouped prime table cache is poisoned")?;
11942            let tab_d = &tabs_guard[rank];
11943            let mut z_r = engine.uninit(t * width)?;
11944            {
11945                let mut dst = z_r.slice_mut(0..t * width);
11946                engine
11947                    .stream()
11948                    .memcpy_dtod(&z_t.slice(0..t * width), &mut dst)?;
11949            }
11950            let dstage = std::env::var("MEMRA_MOE_DETERM_STAGE").as_deref() == Ok("1") && t >= 16;
11951            let (z16, zs) = engine.moe_f16g_act(&z_r, Some(&csr_tok_d), width, n_pairs)?;
11952            if dstage {
11953                // z16 is the GEMM's actual DATA input and is a byte buffer; checksumming only
11954                // z_r and zs left "identical inputs" unestablished and produced a localization
11955                // that outran the measurement. Checksum it as bytes.
11956                let zr = engine.dtoh(&z_r)?;
11957                let zsv = engine.dtoh(&zs)?;
11958                let z16v = engine.dtoh_u8(&z16)?;
11959                eprintln!(
11960                    "[determ-stage] rank={rank} t={t} z_r={:016x} zs={:016x} z16={:016x}",
11961                    Self::determ_stage_sum(&zr),
11962                    Self::determ_stage_sum(&zsv),
11963                    Self::determ_stage_bytes(&z16v)
11964                );
11965            }
11966            if dstage {
11967                // INPUT CLOSURE. Everything the gate kernel dereferences, plus the launch
11968                // geometry that decides how it is summed, checksummed in ONE place. A kernel
11969                // proven bit-deterministic on live data, with no atomics, can only diverge if
11970                // (A) some byte it reads differs, (B) the launch differs, or (C) it reads
11971                // outside its declared inputs. This closes A and B; C is what compute-sanitizer
11972                // is for. Partial input sets are how the divergence kept retreating into the
11973                // part that was never measured.
11974                engine.stream().synchronize()?;
11975                let csr_v = engine.dtoh_i32(&csr_tok_d)?;
11976                let exi_v = engine.dtoh_i32(&exi_d)?;
11977                let exo_v = engine.dtoh_i32(&exoff_d)?;
11978                let mg_v = engine.dtoh(&mg_d)?;
11979                let mu_v = engine.dtoh(&mu_d)?;
11980                let tab_v = engine.dtoh_u64(tab_d)?;
11981                eprintln!(
11982                    "[determ-closure] rank={rank} t={t} csr_tok={:016x} exi={:016x} exoff={:016x}                      ex_off_host={:016x} mg={:016x} mu={:016x} tab={:016x} | n_active={n_active}                      n_pairs={n_pairs} width={width} local_ff={local_ff} n_expert={n_expert}                      qt={bank_qt} rb={}",
11983                    Self::determ_stage_i32(&csr_v),
11984                    Self::determ_stage_i32(&exi_v),
11985                    Self::determ_stage_i32(&exo_v),
11986                    Self::determ_stage_i32(&ex_off),
11987                    Self::determ_stage_sum(&mg_v),
11988                    Self::determ_stage_sum(&mu_v),
11989                    tab_v
11990                        .iter()
11991                        .fold(0u64, |a, b| a.wrapping_mul(1_000_003).wrapping_add(*b)),
11992                    gb.row_bytes
11993                );
11994                // The resident weight bank is the GEMM's OTHER operand and was never checked.
11995                // Opt-in because it is a ~424 MB dtoh per rank per layer.
11996                if std::env::var("MEMRA_MOE_DETERM_BANK").as_deref() == Ok("1") {
11997                    let bank_v = engine.dtoh_u8(&gb.bank)?;
11998                    eprintln!(
11999                        "[determ-closure] rank={rank} t={t} gate_bank={:016x} bytes={}",
12000                        Self::determ_stage_bytes(&bank_v),
12001                        bank_v.len()
12002                    );
12003                }
12004            }
12005            let mut g = engine.moe_f16_grouped(
12006                tab_d,
12007                0,
12008                n_expert,
12009                &exi_d,
12010                &ex_off,
12011                &exoff_d,
12012                &z16,
12013                &zs,
12014                width,
12015                local_ff,
12016                n_active,
12017                n_pairs,
12018                bank_qt,
12019                gb.row_bytes,
12020            )?;
12021            engine.scale_rows(&mut g, &mg_d, local_ff, n_pairs)?;
12022            let mut u = engine.moe_f16_grouped(
12023                tab_d,
12024                1,
12025                n_expert,
12026                &exi_d,
12027                &ex_off,
12028                &exoff_d,
12029                &z16,
12030                &zs,
12031                width,
12032                local_ff,
12033                n_active,
12034                n_pairs,
12035                bank_qt,
12036                ub.row_bytes,
12037            )?;
12038            engine.scale_rows(&mut u, &mu_d, local_ff, n_pairs)?;
12039            // step35 routed SwiGLU clamp (per-layer; live only on layers 43/44 for this
12040            // family): min(silu(g), lim) * clamp(u, +-lim). Dropping it was the second
12041            // correctness bug of the first engaged run.
12042            let act = match activation_limit.filter(|l| *l > 1e-6) {
12043                Some(lim) => {
12044                    let mut a = engine.uninit(n_pairs * local_ff)?;
12045                    engine.swiglu_clamped_mul_scaled(
12046                        &g,
12047                        &u,
12048                        1.0,
12049                        1.0,
12050                        lim,
12051                        &mut a,
12052                        n_pairs * local_ff,
12053                    )?;
12054                    a
12055                }
12056                None => engine.moe_pairs_silu_mul(&g, &u, n_pairs * local_ff)?,
12057            };
12058            if dstage {
12059                let gv = engine.dtoh(&g)?;
12060                let uv = engine.dtoh(&u)?;
12061                let av = engine.dtoh(&act)?;
12062                // A SUM tells you THAT gate differs; it does not tell you HOW. ULP-dense diffs
12063                // (nearly every element, ~1e-8) are an ordering/precision class; a handful of
12064                // huge ones are a corruption class. They need different hunts, so measure the
12065                // shape here instead of inferring it later.
12066                let key = (rank, t);
12067                let mut prev_map = DETERM_PREV
12068                    .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
12069                    .lock()
12070                    .map_err(|_| "determ prev map poisoned")?;
12071                let shape = match prev_map.get(&key) {
12072                    Some(prev) if prev.len() == gv.len() => {
12073                        let mut md = 0.0f32;
12074                        let mut n_diff = 0usize;
12075                        let mut n_big = 0usize;
12076                        for (a, b) in prev.iter().zip(gv.iter()) {
12077                            let d = (a - b).abs();
12078                            if d > 0.0 {
12079                                n_diff += 1;
12080                            }
12081                            if d > 1e-3 {
12082                                n_big += 1;
12083                            }
12084                            if d > md {
12085                                md = d;
12086                            }
12087                        }
12088                        format!(
12089                            " | vs_prev maxdiff={md:.3e} differing={n_diff}/{} big(>1e-3)={n_big}",
12090                            gv.len()
12091                        )
12092                    }
12093                    _ => String::new(),
12094                };
12095                prev_map.insert(key, gv.clone());
12096                drop(prev_map);
12097                eprintln!(
12098                    "[determ-stage] rank={rank} t={t} gate={:016x} up={:016x} silu={:016x}{shape}",
12099                    Self::determ_stage_sum(&gv),
12100                    Self::determ_stage_sum(&uv),
12101                    Self::determ_stage_sum(&av)
12102                );
12103            }
12104            let (a16, a_s) = engine.moe_f16g_act(&act, None, local_ff, n_pairs)?;
12105            let d_csr = engine.moe_f16_grouped(
12106                tab_d,
12107                2,
12108                n_expert,
12109                &exi_d,
12110                &ex_off,
12111                &exoff_d,
12112                &a16,
12113                &a_s,
12114                local_ff,
12115                width,
12116                n_active,
12117                n_pairs,
12118                bank_qt,
12119                db.row_bytes,
12120            )?;
12121
12122            // No host sync: both ranks' chains must be in flight before anything waits.
12123            // The rank's tail event orders the root's cross-device pulls below.
12124            if dstage {
12125                engine.stream().synchronize()?;
12126                let a16v = engine.dtoh_u8(&a16)?;
12127                let dv = engine.dtoh(&d_csr)?;
12128                eprintln!(
12129                    "[determ-stage] rank={rank} t={t} a16={:016x} down_partial={:016x}",
12130                    Self::determ_stage_bytes(&a16v),
12131                    Self::determ_stage_sum(&dv)
12132                );
12133            }
12134            let ev = engine.ctx().new_event(None)?;
12135            ev.record(&engine.stream())?;
12136            if gprof {
12137                // Per-rank GPU SPAN (2026-08-28). Keep the tail event; the elapsed time is read
12138                // AFTER the join sync below. Reading it here returns NOT_READY (the work has only
12139                // been queued) and cudarc's elapsed_ms synchronizes, which serialized the very
12140                // ranks this is meant to test: host issue jumped 1.9 ms -> 34-47 ms per call and
12141                // the join wall fell to match. A probe that changes the schedule measures its own
12142                // perturbation.
12143                // CudaEvent is not Clone, so record a second tail event on the same stream —
12144                // adjacent to `ev`, so it carries the same completion timestamp for timing.
12145                let tp = engine
12146                    .ctx()
12147                    .new_event(Some(cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT))?;
12148                tp.record(&engine.stream())?;
12149                ev_tail_prof.push(tp);
12150            }
12151            ev_rank.push(ev);
12152            partials.push(d_csr);
12153        }
12154        let _main = e.gpu.enter_main()?;
12155        e.bind_runtime_device(e.ctx().ordinal() as i32)?;
12156        // Host-only: every rank's chain is queued, nothing has been waited on yet.
12157        let g_issue = g_t1.elapsed().as_secs_f64() * 1e3;
12158        let g_t2 = std::time::Instant::now();
12159        for ev in &ev_rank {
12160            e.stream().wait(ev)?;
12161        }
12162        // Both partials land on the root (rank 1's crosses the link once), then ONE fused pass
12163        // does join + CSR permute + weight + scatter. Shard order stays pinned as (y0 + y1).
12164        let mut y0 = e.uninit(n_pairs * width)?;
12165        {
12166            let mut dst = y0.slice_mut(0..n_pairs * width);
12167            e.stream()
12168                .memcpy_dtod(&partials[0].slice(0..n_pairs * width), &mut dst)?;
12169        }
12170        let mut y1 = e.uninit(n_pairs * width)?;
12171        {
12172            let mut dst = y1.slice_mut(0..n_pairs * width);
12173            e.stream()
12174                .memcpy_dtod(&partials[1].slice(0..n_pairs * width), &mut dst)?;
12175        }
12176        let inv_d = e.htod_i32(&inv)?;
12177        let wd_d = e.htod(&wd)?;
12178        let mut out = e.uninit(t * width)?;
12179        e.moe_prime_join_scatter(&y0, &y1, &inv_d, &wd_d, &mut out, width, n_used, t)?;
12180        if gprof {
12181            let _ = e.stream().synchronize();
12182            let g_join = g_t2.elapsed().as_secs_f64() * 1e3;
12183            // Everything has completed, so both events of every pair are ready and elapsed_ms
12184            // cannot block. A negative entry means the query itself failed and the row must be
12185            // read as missing data, never as a zero-length span.
12186            // cuEventElapsedTime needs the events' OWN context current — computing it under the
12187            // root's pushed context returned an error for every pair, and the first version
12188            // swallowed that into -1.0 with no reason attached. Enter each rank's context, and
12189            // print the failure once so a dead probe can never again look like a zero-length span.
12190            let mut span_ms: Vec<f32> = Vec::with_capacity(world);
12191            for (rank, (h, tp)) in ev_head.iter().zip(ev_tail_prof.iter()).enumerate() {
12192                let guard = self.ranks[rank].gpu.enter_main();
12193                match guard.and_then(|_g| h.elapsed_ms(tp).map_err(|e| e.into())) {
12194                    Ok(v) => span_ms.push(v),
12195                    Err(err) => {
12196                        static SAID: std::sync::atomic::AtomicBool =
12197                            std::sync::atomic::AtomicBool::new(false);
12198                        if !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
12199                            eprintln!("[grp-prof] span query failed on rank {rank}: {err}");
12200                        }
12201                        span_ms.push(-1.0);
12202                    }
12203                }
12204            }
12205            eprintln!(
12206                "[grp-prof] t={t} n_active={n_active} csr={g_csr:.1}ms issue={g_issue:.1}ms \
12207                 join={g_join:.1}ms spans={span_ms:?} span_sum={:.1}ms span_max={:.1}ms",
12208                span_ms.iter().sum::<f32>(),
12209                span_ms.iter().cloned().fold(0.0f32, f32::max)
12210            );
12211        }
12212        Ok(out)
12213    }
12214
12215    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
12216        &self,
12217        experts: &ResidentNvfp4TensorParallel,
12218        e: &Engine,
12219        input_dev: &crate::CudaSlice<f32>,
12220        sel_d: &crate::CudaSlice<i32>,
12221        w_d: &crate::CudaSlice<f32>,
12222        experts_per_token: usize,
12223        activation_limit: Option<f32>,
12224    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
12225        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
12226            experts,
12227            e,
12228            input_dev,
12229            sel_d,
12230            w_d,
12231            experts_per_token,
12232            activation_limit,
12233            || Ok(()),
12234        )
12235    }
12236
12237    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
12238    /// runs on the host right before the join wait is enqueued on e's stream — work it
12239    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
12240    /// sweep, instead of after the join. Value-neutral by construction (the hook only
12241    /// reorders independent host issue).
12242    #[allow(clippy::too_many_arguments)]
12243    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
12244        &self,
12245        experts: &ResidentNvfp4TensorParallel,
12246        e: &Engine,
12247        input_dev: &crate::CudaSlice<f32>,
12248        sel_d: &crate::CudaSlice<i32>,
12249        w_d: &crate::CudaSlice<f32>,
12250        experts_per_token: usize,
12251        activation_limit: Option<f32>,
12252        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
12253    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
12254        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
12255            experts,
12256            e,
12257            input_dev,
12258            sel_d,
12259            w_d,
12260            experts_per_token,
12261            activation_limit,
12262            pre_join,
12263            None,
12264        )
12265    }
12266
12267    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
12268    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
12269    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
12270    /// its apply launch. Raw UVA pointers so no lock is held across the call.
12271    #[allow(clippy::too_many_arguments)]
12272    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
12273        &self,
12274        experts: &ResidentNvfp4TensorParallel,
12275        e: &Engine,
12276        input_dev: &crate::CudaSlice<f32>,
12277        sel_d: &crate::CudaSlice<i32>,
12278        w_d: &crate::CudaSlice<f32>,
12279        experts_per_token: usize,
12280        activation_limit: Option<f32>,
12281        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
12282        post_add: Option<(u64, u64)>,
12283    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
12284        if input_dev.len() != experts.input_width {
12285            return Err(format!(
12286                "NVFP4 device-routed input {} != width {}",
12287                input_dev.len(),
12288                experts.input_width
12289            )
12290            .into());
12291        }
12292        let n_sel = experts_per_token;
12293        if sel_d.len() < n_sel || w_d.len() < n_sel {
12294            return Err(format!(
12295                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
12296                sel_d.len(),
12297                w_d.len()
12298            )
12299            .into());
12300        }
12301        let world = self.ranks.len();
12302        if world != NVFP4_CANONICAL_ROW_SHARDS {
12303            return Err(format!(
12304                "NVFP4 device routes require world == canonical shard grid \
12305                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
12306            )
12307            .into());
12308        }
12309        let local_out = if experts.ep2 {
12310            experts.expert_width
12311        } else {
12312            experts.expert_width / world
12313        };
12314
12315        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12316        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
12317        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
12318        let started = timing.then(std::time::Instant::now);
12319
12320        let mut workspace_guard = experts
12321            .device_workspace
12322            .lock()
12323            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
12324        if workspace_guard.is_none() {
12325            drop(workspace_guard);
12326            let zero = vec![0.0f32; experts.input_width];
12327            let zero_sel = vec![0usize; n_sel];
12328            let zero_w = vec![0.0f32; n_sel];
12329            let _ = self.run_tensor_parallel_routes_nvfp4_device(
12330                experts,
12331                &zero,
12332                &zero_sel,
12333                &zero_w,
12334                n_sel,
12335                activation_limit,
12336            )?;
12337            workspace_guard = experts
12338                .device_workspace
12339                .lock()
12340                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
12341        }
12342        let workspace = workspace_guard
12343            .as_mut()
12344            .expect("NVFP4 device routes workspace initialized above");
12345        if workspace.n_sel != n_sel {
12346            return Err(format!(
12347                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
12348                workspace.n_sel
12349            )
12350            .into());
12351        }
12352
12353        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
12354        // stitched multi-device parent launched on e's stream — no events, no per-token node
12355        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
12356        // the children replay exactly the same kernel/copy sequence.
12357        if step_tp_graph_enabled()? {
12358            if experts.ep2 {
12359                return Err(
12360                    "MEMRA_STEP_TP_GRAPH=1 with MEMRA_STEP_NVFP4_EP2=1 has never been \
12361                     co-gated; unset one"
12362                        .into(),
12363                );
12364            }
12365            if workspace.dev_route_e.is_none() {
12366                let _main = e.gpu.enter_main()?;
12367                workspace.dev_route_e = Some((
12368                    e.htod_i32(&vec![0i32; n_sel])?,
12369                    e.htod(&vec![0.0f32; n_sel])?,
12370                ));
12371            }
12372            if workspace.in_stage_e.is_none() {
12373                let _main = e.gpu.enter_main()?;
12374                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
12375                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
12376            }
12377            if workspace.routes_graph.is_none() {
12378                let graph = self.nvfp4_routes_build_graph(
12379                    experts,
12380                    workspace,
12381                    local_out,
12382                    n_sel,
12383                    activation_limit,
12384                )?;
12385                workspace.routes_graph = Some(graph);
12386                eprintln!(
12387                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
12388                     children=3 updates=none performance_claim=false"
12389                );
12390            }
12391            let output = {
12392                let _main = e.gpu.enter_main()?;
12393                {
12394                    let (sel_e, w_e) = workspace
12395                        .dev_route_e
12396                        .as_mut()
12397                        .expect("device route staging set above");
12398                    {
12399                        let mut dst = sel_e.slice_mut(0..n_sel);
12400                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
12401                    }
12402                    {
12403                        let mut dst = w_e.slice_mut(0..n_sel);
12404                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
12405                    }
12406                }
12407                {
12408                    let in_stage = workspace
12409                        .in_stage_e
12410                        .as_mut()
12411                        .expect("graph staging set above");
12412                    let mut dst = in_stage.slice_mut(0..experts.input_width);
12413                    e.stream()
12414                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
12415                }
12416                unsafe {
12417                    let r = cudarc::driver::sys::cuGraphLaunch(
12418                        workspace
12419                            .routes_graph
12420                            .as_ref()
12421                            .expect("routes graph built above")
12422                            .exec,
12423                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
12424                    );
12425                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
12426                        return Err(format!("routes graph launch: {r:?}").into());
12427                    }
12428                }
12429                let mut output = e.uninit(experts.input_width)?;
12430                {
12431                    let out_stage = workspace
12432                        .out_stage_e
12433                        .as_ref()
12434                        .expect("graph staging set above");
12435                    e.stream().memcpy_dtod(
12436                        &out_stage.slice(0..experts.input_width),
12437                        &mut output.slice_mut(0..experts.input_width),
12438                    )?;
12439                }
12440                output
12441            };
12442            if let Some(started) = started {
12443                use std::sync::atomic::Ordering;
12444                let ns = TIMING_NS
12445                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
12446                    + started.elapsed().as_nanos() as u64;
12447                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12448                if calls % 430 == 0 {
12449                    eprintln!(
12450                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12451                        ns as f64 / 1.0e6,
12452                        ns as f64 / calls as f64 / 1.0e3,
12453                    );
12454                }
12455            }
12456            return Ok(output);
12457        }
12458
12459        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
12460        // copied into the persistent e-context pair, then the event is recorded — the caller's
12461        // sel_d/w_d can free on e's stream with no cross-stream reader.
12462        if let Some((_, device)) = workspace.ev_entry.as_ref() {
12463            if *device != e.ctx().ordinal() {
12464                return Err("NVFP4 device-routed routes engine changed".into());
12465            }
12466        } else {
12467            let _main = e.gpu.enter_main()?;
12468            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
12469        }
12470        if workspace.dev_route_e.is_none() {
12471            let _main = e.gpu.enter_main()?;
12472            workspace.dev_route_e = Some((
12473                e.htod_i32(&vec![0i32; n_sel])?,
12474                e.htod(&vec![0.0f32; n_sel])?,
12475            ));
12476        }
12477        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
12478        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
12479        // selection rows), so when every consuming rank shares e's device the ranks can read
12480        // them directly and this hop disappears. The graph door keeps the staging (its
12481        // captured copies read the fixed addresses).
12482        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
12483        let e_device = e.ctx().ordinal();
12484        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
12485        let rank1_routed_peek = workspace.rank1_routed;
12486        let stage_needed = !mirror
12487            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
12488                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
12489            });
12490        {
12491            let _main = e.gpu.enter_main()?;
12492            if stage_needed {
12493                let (sel_e, w_e) = workspace
12494                    .dev_route_e
12495                    .as_mut()
12496                    .expect("device route staging set above");
12497                {
12498                    let mut dst = sel_e.slice_mut(0..n_sel);
12499                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
12500                }
12501                {
12502                    let mut dst = w_e.slice_mut(0..n_sel);
12503                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
12504                }
12505            }
12506            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
12507            ev_entry.record(&e.stream())?;
12508        }
12509        // Prestage door: input pull + quantize were already issued on the rank streams
12510        // (before the router) — the rank stream order suffices, skip them here.
12511        let prestaged = std::mem::take(&mut workspace.prestaged);
12512        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
12513        for (rank_index, engine) in self.ranks.iter().enumerate() {
12514            let _main = engine.gpu.enter_main()?;
12515            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
12516            engine.stream().wait(ev_entry)?;
12517            if !prestaged {
12518                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
12519                engine
12520                    .stream()
12521                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
12522            }
12523            if !(rank1_routed && rank_index == 1) {
12524                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
12525                // the caller's persistent rows when this rank shares e's device (UVA, ordered
12526                // by ev_entry), else the staged e-context pair.
12527                let same_dev = engine.ctx().ordinal() == e_device;
12528                if mirror {
12529                    // Split the workspace borrow so the source (the staged pair, when this
12530                    // rank is off-device) and the destination rows coexist.
12531                    let Nvfp4DeviceRoutesWorkspace {
12532                        sel,
12533                        route_w,
12534                        dev_route_e,
12535                        ..
12536                    } = &mut *workspace;
12537                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
12538                        if same_dev {
12539                            (sel_d, w_d)
12540                        } else {
12541                            let (sel_e, w_e) = dev_route_e
12542                                .as_ref()
12543                                .expect("device route staging set above");
12544                            (sel_e, w_e)
12545                        };
12546                    engine.moe_sel_w_mirror(
12547                        src_sel,
12548                        src_w,
12549                        &mut sel[rank_index],
12550                        &mut route_w[rank_index],
12551                        n_sel,
12552                    )?;
12553                } else {
12554                    let (sel_e, w_e) = workspace
12555                        .dev_route_e
12556                        .as_ref()
12557                        .expect("device route staging set above");
12558                    {
12559                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
12560                        engine
12561                            .stream()
12562                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
12563                    }
12564                    {
12565                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
12566                        engine
12567                            .stream()
12568                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
12569                    }
12570                }
12571            }
12572            if !prestaged {
12573                let Nvfp4DeviceRoutesWorkspace {
12574                    input, in_q, in_d, ..
12575                } = &mut *workspace;
12576                engine.quantize_q8_1_into(
12577                    &input[rank_index],
12578                    1,
12579                    experts.input_width,
12580                    &mut in_q[rank_index],
12581                    &mut in_d[rank_index],
12582                )?;
12583            }
12584        }
12585        self.nvfp4_routes_batched_sweeps(
12586            experts,
12587            workspace,
12588            &[],
12589            &[],
12590            &[],
12591            local_out,
12592            n_sel,
12593            activation_limit,
12594            true,
12595        )?;
12596
12597        // rank0 == root: its own stream order already covers its sweep; only the PEER
12598        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
12599        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
12600            let _main = engine.gpu.enter_main()?;
12601            workspace.ev_rank[rank_index].record(&engine.stream())?;
12602        }
12603        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
12604        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
12605        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
12606        let mut ticket = 0u32;
12607        if memops {
12608            use cudarc::driver::sys;
12609            if workspace.fence_flags_raw == 0 {
12610                let root = &self.ranks[0];
12611                let _main = root.gpu.enter_main()?;
12612                let mut ptr: sys::CUdeviceptr = 0;
12613                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
12614                if r != sys::CUresult::CUDA_SUCCESS {
12615                    return Err(format!("fence flag alloc: {r:?}").into());
12616                }
12617                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
12618                if r != sys::CUresult::CUDA_SUCCESS {
12619                    return Err(format!("fence flag memset: {r:?}").into());
12620                }
12621                workspace.fence_flags_raw = ptr as u64;
12622            }
12623            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
12624            ticket = workspace.fence_ticket;
12625            let base = workspace.fence_flags_raw;
12626            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
12627            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
12628            // root memory is legal — the direct join already relies on it. Under
12629            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
12630            // replacing the cross-device event wait below.
12631            if fence_rank1_on() {
12632                let peer = &self.ranks[1];
12633                let _pmain = peer.gpu.enter_main()?;
12634                peer.ring_flag_raw(base, ticket)?;
12635            }
12636            {
12637                let root = &self.ranks[0];
12638                let _main = root.gpu.enter_main()?;
12639                let r = unsafe {
12640                    sys::cuStreamWriteValue32_v2(
12641                        root.stream().cu_stream() as sys::CUstream,
12642                        (base + 4) as sys::CUdeviceptr,
12643                        ticket,
12644                        0,
12645                    )
12646                };
12647                if r != sys::CUresult::CUDA_SUCCESS {
12648                    return Err(format!("fence write root: {r:?}").into());
12649                }
12650            }
12651        }
12652        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
12653        // kernels queued here execute while the peer rank drains its sweep.
12654        pre_join()?;
12655
12656        if moe_direct_on() && self.ranks.len() == 2 {
12657            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
12658            // rank0's is root-stream-ordered. One root event + rank1's own event order
12659            // the model engine's single add — same operand order as root's add
12660            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
12661            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
12662            // hazard class does not apply).
12663            let _main = e.gpu.enter_main()?;
12664            if memops {
12665                use cudarc::driver::sys;
12666                let base = workspace.fence_flags_raw;
12667                let r = unsafe {
12668                    sys::cuStreamWaitValue32_v2(
12669                        e.stream().cu_stream() as sys::CUstream,
12670                        (base + 4) as sys::CUdeviceptr,
12671                        ticket,
12672                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
12673                    )
12674                };
12675                if r != sys::CUresult::CUDA_SUCCESS {
12676                    return Err(format!("fence wait: {r:?}").into());
12677                }
12678                if fence_rank1_on() {
12679                    // Same-device wait on the flag rank1 rang over P2P.
12680                    let r = unsafe {
12681                        sys::cuStreamWaitValue32_v2(
12682                            e.stream().cu_stream() as sys::CUstream,
12683                            base as sys::CUdeviceptr,
12684                            ticket,
12685                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
12686                        )
12687                    };
12688                    if r != sys::CUresult::CUDA_SUCCESS {
12689                        return Err(format!("fence wait rank1: {r:?}").into());
12690                    }
12691                } else {
12692                    for ev in workspace.ev_rank.iter().skip(1) {
12693                        e.stream().wait(ev)?;
12694                    }
12695                }
12696            } else {
12697                {
12698                    let root = &self.ranks[0];
12699                    let _rmain = root.gpu.enter_main()?;
12700                    workspace
12701                        .ev_done
12702                        .as_ref()
12703                        .expect("device routes done event")
12704                        .record(&root.stream())?;
12705                }
12706                e.stream().wait(
12707                    workspace
12708                        .ev_done
12709                        .as_ref()
12710                        .expect("device routes done event"),
12711                )?;
12712                for ev in workspace.ev_rank.iter().skip(1) {
12713                    e.stream().wait(ev)?;
12714                }
12715            }
12716            let mut output = e.uninit(experts.input_width)?;
12717            if let Some((sh_raw, scale_raw)) = post_add {
12718                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
12719                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
12720                e.add3_raw(
12721                    &workspace.accumulator[0],
12722                    &workspace.accumulator[1],
12723                    sh_raw,
12724                    scale_raw,
12725                    &mut output,
12726                    experts.input_width,
12727                )?;
12728            } else {
12729                e.add(
12730                    &workspace.accumulator[0],
12731                    &workspace.accumulator[1],
12732                    &mut output,
12733                    experts.input_width,
12734                )?;
12735            }
12736            let output = output;
12737            if let Some(started) = started {
12738                use std::sync::atomic::Ordering;
12739                let ns = TIMING_NS
12740                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
12741                    + started.elapsed().as_nanos() as u64;
12742                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12743                if calls % 430 == 0 {
12744                    eprintln!(
12745                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12746                        ns as f64 / 1.0e6,
12747                        ns as f64 / calls as f64 / 1.0e3,
12748                    );
12749                }
12750            }
12751            return Ok(output);
12752        }
12753        {
12754            let root = &self.ranks[0];
12755            let _main = root.gpu.enter_main()?;
12756            for ev in workspace.ev_rank.iter().skip(1) {
12757                root.stream().wait(ev)?;
12758            }
12759            root.stream()
12760                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
12761            {
12762                let Nvfp4DeviceRoutesWorkspace {
12763                    accumulator,
12764                    remote,
12765                    combined,
12766                    ..
12767                } = &mut *workspace;
12768                root.add(&accumulator[0], remote, combined, experts.input_width)?;
12769            }
12770            workspace
12771                .ev_done
12772                .as_ref()
12773                .expect("device routes done event")
12774                .record(&root.stream())?;
12775        }
12776        let output = {
12777            let _main = e.gpu.enter_main()?;
12778            e.stream().wait(
12779                workspace
12780                    .ev_done
12781                    .as_ref()
12782                    .expect("device routes done event"),
12783            )?;
12784            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
12785            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
12786            let mut output = e.uninit(experts.input_width)?;
12787            e.stream().memcpy_dtod(
12788                &workspace.combined.slice(0..experts.input_width),
12789                &mut output.slice_mut(0..experts.input_width),
12790            )?;
12791            output
12792        };
12793        if let Some(started) = started {
12794            use std::sync::atomic::Ordering;
12795            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
12796                + started.elapsed().as_nanos() as u64;
12797            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12798            if calls % 430 == 0 {
12799                eprintln!(
12800                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12801                    ns as f64 / 1.0e6,
12802                    ns as f64 / calls as f64 / 1.0e3,
12803                );
12804            }
12805        }
12806        Ok(output)
12807    }
12808
12809    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
12810    /// caller wraps it with rank-event waits + the done record; the token graph captures it
12811    /// verbatim (parent edges provide the ordering).
12812    pub(crate) fn decode_v2_finish_root_fused(
12813        &self,
12814        ws: &mut StepTpDecodeV2Ws,
12815    ) -> Result<(), Box<dyn std::error::Error>> {
12816        let root = &self.ranks[0];
12817        let _main = root.gpu.enter_main()?;
12818        if ws.raw_peer_partial != 0 {
12819            // Capture-safe raw seams (arming happened in the stage flow).
12820            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
12821        } else {
12822            root.stream()
12823                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
12824        }
12825        {
12826            let StepTpDecodeV2Ws {
12827                o_partials,
12828                peer_partial,
12829                reduce_a,
12830                o_out,
12831                ..
12832            } = &mut *ws;
12833            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
12834        }
12835        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
12836        if shadows {
12837            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
12838            // raw when armed.
12839            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
12840            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
12841            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
12842            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
12843        }
12844        if shadows && ws.raw_peer_partial != 0 {
12845            raw_copy_bytes(
12846                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
12847                ws.raw_k1,
12848                ws.local_kv_dim * 4,
12849                root,
12850            )?;
12851            raw_copy_bytes(
12852                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
12853                ws.raw_v1,
12854                ws.local_kv_dim * 4,
12855                root,
12856            )?;
12857        } else if shadows {
12858            let start = ws.local_kv_dim;
12859            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
12860            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
12861            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
12862            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
12863        }
12864        if ws.raw_mixed_stage_e != 0 {
12865            // Token-graph mirrors: the e-glue children read same-context copies of the
12866            // root-produced rows.
12867            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
12868            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
12869            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
12870            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
12871        }
12872        Ok(())
12873    }
12874
12875    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
12876    /// reduce_a's own pointer.
12877    pub(crate) fn decode_v2_arm_token_mirrors(
12878        &self,
12879        ws: &mut StepTpDecodeV2Ws,
12880        mixed_stage_e: u64,
12881        shadow_stage_e: (u64, u64),
12882    ) -> Result<(), Box<dyn std::error::Error>> {
12883        use cudarc::driver::DevicePtr;
12884        let root = &self.ranks[0];
12885        let _main = root.gpu.enter_main()?;
12886        let stream = root.stream();
12887        let (a, _g) = ws.reduce_a.device_ptr(&stream);
12888        ws.raw_reduce_a = a as u64;
12889        ws.raw_mixed_stage_e = mixed_stage_e;
12890        ws.raw_shadow_stage_e = shadow_stage_e;
12891        Ok(())
12892    }
12893
12894    /// Build one layer's stitched routes graph: per-rank children captured on their own
12895    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
12896    /// capture-illegal there), a root combine child, and a multi-device parent with
12897    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
12898    /// nodes touch is persistent workspace/staging.
12899    fn nvfp4_routes_build_graph(
12900        &self,
12901        experts: &ResidentNvfp4TensorParallel,
12902        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12903        local_out: usize,
12904        n_sel: usize,
12905        activation_limit: Option<f32>,
12906    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
12907        use cudarc::driver::DevicePtr;
12908        use cudarc::driver::sys;
12909        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12910            if r == sys::CUresult::CUDA_SUCCESS {
12911                Ok(())
12912            } else {
12913                Err(format!("{what}: {r:?}").into())
12914            }
12915        }
12916        let world = self.ranks.len();
12917        if world != 2 {
12918            return Err("routes graph door is built for the TP2 pair".into());
12919        }
12920        let width = experts.input_width;
12921
12922        // Raw pointers cached before capture (each read with its owner's stream).
12923        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
12924            let stream = engine.stream();
12925            let (ptr, _g) = buf.device_ptr(&stream);
12926            ptr as u64
12927        };
12928        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
12929            let stream = engine.stream();
12930            let (ptr, _g) = buf.device_ptr(&stream);
12931            ptr as u64
12932        };
12933        let (sel_e, w_e) = workspace
12934            .dev_route_e
12935            .as_ref()
12936            .expect("device route staging set before graph build");
12937        let root_engine = &self.ranks[0];
12938        let p_in_stage = ptr_f32(
12939            workspace.in_stage_e.as_ref().expect("graph staging"),
12940            root_engine,
12941        );
12942        let p_out_stage = ptr_f32(
12943            workspace.out_stage_e.as_ref().expect("graph staging"),
12944            root_engine,
12945        );
12946        let p_sel_e = ptr_i32(sel_e, root_engine);
12947        let p_w_e = ptr_f32(w_e, root_engine);
12948        let p_input: Vec<u64> = (0..world)
12949            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
12950            .collect();
12951        let p_sel: Vec<u64> = (0..world)
12952            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
12953            .collect();
12954        let p_route_w: Vec<u64> = (0..world)
12955            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
12956            .collect();
12957        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
12958        let p_remote = ptr_f32(&workspace.remote, root_engine);
12959        let p_combined = ptr_f32(&workspace.combined, root_engine);
12960
12961        let raw_copy = |dst: u64,
12962                        src: u64,
12963                        bytes: usize,
12964                        engine: &Engine|
12965         -> Result<(), Box<dyn std::error::Error>> {
12966            unsafe {
12967                cu_try(
12968                    sys::cuMemcpyAsync(
12969                        dst as sys::CUdeviceptr,
12970                        src as sys::CUdeviceptr,
12971                        bytes,
12972                        engine.stream().cu_stream() as sys::CUstream,
12973                    ),
12974                    "routes graph cuMemcpyAsync",
12975                )
12976            }
12977        };
12978
12979        let mut children = Vec::with_capacity(3);
12980        for rank in 0..world {
12981            let engine = &self.ranks[rank];
12982            let _main = engine.gpu.enter_main()?;
12983            let (child, _retained) = engine.capture_graph_retained(|_| {
12984                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
12985                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
12986                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
12987                {
12988                    let Nvfp4DeviceRoutesWorkspace {
12989                        input, in_q, in_d, ..
12990                    } = &mut *workspace;
12991                    engine.quantize_q8_1_into(
12992                        &input[rank],
12993                        1,
12994                        width,
12995                        &mut in_q[rank],
12996                        &mut in_d[rank],
12997                    )?;
12998                }
12999                self.nvfp4_routes_batched_sweeps_rank(
13000                    experts,
13001                    workspace,
13002                    &[],
13003                    &[],
13004                    &[],
13005                    local_out,
13006                    n_sel,
13007                    activation_limit,
13008                    true,
13009                    rank,
13010                )?;
13011                Ok(())
13012            })?;
13013            children.push(child);
13014        }
13015        {
13016            let root = &self.ranks[0];
13017            let _main = root.gpu.enter_main()?;
13018            let (child, _retained) = root.capture_graph_retained(|_| {
13019                raw_copy(p_remote, p_acc1, width * 4, root)?;
13020                {
13021                    let Nvfp4DeviceRoutesWorkspace {
13022                        accumulator,
13023                        remote,
13024                        combined,
13025                        ..
13026                    } = &mut *workspace;
13027                    root.add(&accumulator[0], remote, combined, width)?;
13028                }
13029                raw_copy(p_out_stage, p_combined, width * 4, root)?;
13030                Ok(())
13031            })?;
13032            children.push(child);
13033        }
13034
13035        let mut parent: sys::CUgraph = std::ptr::null_mut();
13036        unsafe {
13037            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
13038        }
13039        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
13040        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
13041        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
13042        unsafe {
13043            cu_try(
13044                sys::cuGraphAddChildGraphNode(
13045                    &mut n0,
13046                    parent,
13047                    std::ptr::null(),
13048                    0,
13049                    children[0].cu_graph(),
13050                ),
13051                "routes child r0",
13052            )?;
13053            cu_try(
13054                sys::cuGraphAddChildGraphNode(
13055                    &mut n1,
13056                    parent,
13057                    std::ptr::null(),
13058                    0,
13059                    children[1].cu_graph(),
13060                ),
13061                "routes child r1",
13062            )?;
13063            let deps = [n0, n1];
13064            cu_try(
13065                sys::cuGraphAddChildGraphNode(
13066                    &mut n2,
13067                    parent,
13068                    deps.as_ptr(),
13069                    2,
13070                    children[2].cu_graph(),
13071                ),
13072                "routes child root",
13073            )?;
13074        }
13075        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
13076        unsafe {
13077            cu_try(
13078                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
13079                "routes instantiate",
13080            )?;
13081        }
13082        Ok(RoutesGraph {
13083            exec,
13084            parent,
13085            _children: children,
13086        })
13087    }
13088
13089    /// One rank's routes section for the token graph (event-free): staged input copy (raw
13090    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
13091    /// Eager device_routed wraps it with the entry-event wait.
13092    #[allow(clippy::too_many_arguments)]
13093    pub(crate) fn routes_rank_section(
13094        &self,
13095        experts: &ResidentNvfp4TensorParallel,
13096        workspace: &mut Nvfp4DeviceRoutesWorkspace,
13097        raw_input_src: u64,
13098        local_out: usize,
13099        n_sel: usize,
13100        activation_limit: Option<f32>,
13101        rank_index: usize,
13102    ) -> Result<(), Box<dyn std::error::Error>> {
13103        let engine = &self.ranks[rank_index];
13104        {
13105            let _main = engine.gpu.enter_main()?;
13106            // sel/route_w land via raw copies from the e staging (fixed addresses).
13107            let (sel_e_ptr, w_e_ptr) = workspace
13108                .raw_dev_route_e
13109                .ok_or("routes rank section requires armed staging pointers")?;
13110            raw_copy_bytes(
13111                workspace.raw_input[rank_index],
13112                raw_input_src,
13113                experts.input_width * 4,
13114                engine,
13115            )?;
13116            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
13117            raw_copy_bytes(
13118                workspace.raw_route_w[rank_index],
13119                w_e_ptr,
13120                n_sel * 4,
13121                engine,
13122            )?;
13123            {
13124                let Nvfp4DeviceRoutesWorkspace {
13125                    input, in_q, in_d, ..
13126                } = &mut *workspace;
13127                engine.quantize_q8_1_into(
13128                    &input[rank_index],
13129                    1,
13130                    experts.input_width,
13131                    &mut in_q[rank_index],
13132                    &mut in_d[rank_index],
13133                )?;
13134            }
13135        }
13136        self.nvfp4_routes_batched_sweeps_rank(
13137            experts,
13138            workspace,
13139            &[],
13140            &[],
13141            &[],
13142            local_out,
13143            n_sel,
13144            activation_limit,
13145            true,
13146            rank_index,
13147        )
13148    }
13149
13150    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
13151    /// add, combined row raw-copied into the fixed e-context out stage.
13152    pub(crate) fn routes_root_section(
13153        &self,
13154        experts: &ResidentNvfp4TensorParallel,
13155        workspace: &mut Nvfp4DeviceRoutesWorkspace,
13156    ) -> Result<(), Box<dyn std::error::Error>> {
13157        let root = &self.ranks[0];
13158        let _main = root.gpu.enter_main()?;
13159        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
13160            .raw_combine
13161            .ok_or("routes root section requires armed combine pointers")?;
13162        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
13163        {
13164            let Nvfp4DeviceRoutesWorkspace {
13165                accumulator,
13166                remote,
13167                combined,
13168                ..
13169            } = &mut *workspace;
13170            root.add(&accumulator[0], remote, combined, experts.input_width)?;
13171        }
13172        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
13173        Ok(())
13174    }
13175
13176    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
13177    /// combine set. Requires dev_route_e + in/out stages already allocated.
13178    pub(crate) fn routes_arm_raw(
13179        &self,
13180        experts: &ResidentNvfp4TensorParallel,
13181        workspace: &mut Nvfp4DeviceRoutesWorkspace,
13182    ) -> Result<(), Box<dyn std::error::Error>> {
13183        use cudarc::driver::DevicePtr;
13184        if workspace.raw_dev_route_e.is_some() {
13185            return Ok(());
13186        }
13187        let _ = experts;
13188        let (sel_e, w_e) = workspace
13189            .dev_route_e
13190            .as_ref()
13191            .ok_or("routes staging not armed")?;
13192        let root = &self.ranks[0];
13193        {
13194            let _main = root.gpu.enter_main()?;
13195            let stream = root.stream();
13196            let (a, _g) = sel_e.device_ptr(&stream);
13197            let (b, _g) = w_e.device_ptr(&stream);
13198            workspace.raw_dev_route_e = Some((a as u64, b as u64));
13199            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
13200            let (d, _g) = workspace.remote.device_ptr(&stream);
13201            let (f, _g) = workspace.combined.device_ptr(&stream);
13202            let out_stage = workspace
13203                .out_stage_e
13204                .as_ref()
13205                .ok_or("routes out stage not armed")?;
13206            let (g_, _g) = out_stage.device_ptr(&stream);
13207            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
13208        }
13209        for rank in 0..self.ranks.len() {
13210            let engine = &self.ranks[rank];
13211            let _main = engine.gpu.enter_main()?;
13212            let stream = engine.stream();
13213            let (a, _g) = workspace.input[rank].device_ptr(&stream);
13214            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
13215            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
13216            workspace.raw_input.push(a as u64);
13217            workspace.raw_sel.push(b as u64);
13218            workspace.raw_route_w.push(c as u64);
13219        }
13220        Ok(())
13221    }
13222
13223    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
13224    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
13225    /// throughput claim.
13226    pub fn run_tensor_parallel_routes_nvfp4(
13227        &self,
13228        experts: &ResidentNvfp4TensorParallel,
13229        input: &[f32],
13230        tokens: usize,
13231        selected: &[usize],
13232        route_weights: &[f32],
13233        experts_per_token: usize,
13234        activation_limit: Option<f32>,
13235    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
13236        validate_activations(input, tokens, experts.input_width)?;
13237        let pairs = tokens
13238            .checked_mul(experts_per_token)
13239            .ok_or("NVFP4 TP route count overflow")?;
13240        if selected.len() != pairs || route_weights.len() != pairs {
13241            return Err(format!(
13242                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
13243                 {experts_per_token} ({pairs})",
13244                selected.len(),
13245                route_weights.len(),
13246            )
13247            .into());
13248        }
13249        if !route_weights.iter().all(|weight| weight.is_finite()) {
13250            return Err("NVFP4 TP route weights contain a non-finite value".into());
13251        }
13252
13253        let mut output = vec![0.0f32; tokens * experts.input_width];
13254        for token in 0..tokens {
13255            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
13256            for slot in 0..experts_per_token {
13257                let pair = token * experts_per_token + slot;
13258                let expert = selected[pair];
13259                if expert >= experts.expert_count {
13260                    return Err(format!(
13261                        "NVFP4 TP selected expert {expert} outside 0..{}",
13262                        experts.expert_count
13263                    )
13264                    .into());
13265                }
13266                // EP2 banks hold the WHOLE expert on rank (expert & 1) at slot (expert >> 1);
13267                // per-row dots are the same full-width program either way (a column shard
13268                // splits ROWS, not the dot), so gate/up are bit-equal across layouts. Only
13269                // down's parenthesization moves (full-width dot vs canonical 2-shard sum) —
13270                // the numeric-class this door declares.
13271                let gate = if experts.ep2 {
13272                    self.run_full_bank_expert_nvfp4(
13273                        &experts.gate,
13274                        &experts.macros_gate,
13275                        expert,
13276                        input_row,
13277                    )?
13278                } else {
13279                    self.run_column_bank_expert_nvfp4(
13280                        &experts.gate,
13281                        &experts.macros_gate,
13282                        expert,
13283                        input_row,
13284                    )?
13285                };
13286                let up = if experts.ep2 {
13287                    self.run_full_bank_expert_nvfp4(
13288                        &experts.up,
13289                        &experts.macros_up,
13290                        expert,
13291                        input_row,
13292                    )?
13293                } else {
13294                    self.run_column_bank_expert_nvfp4(
13295                        &experts.up,
13296                        &experts.macros_up,
13297                        expert,
13298                        input_row,
13299                    )?
13300                };
13301                let activated: Vec<f32> = gate
13302                    .iter()
13303                    .zip(&up)
13304                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
13305                    .collect();
13306                debug_assert_eq!(activated.len(), experts.expert_width);
13307                let down = if experts.ep2 {
13308                    self.run_full_down_expert_nvfp4(
13309                        &experts.down,
13310                        &experts.macros_down,
13311                        expert,
13312                        &activated,
13313                    )?
13314                } else {
13315                    self.run_row_bank_expert_nvfp4(
13316                        &experts.down,
13317                        &experts.macros_down,
13318                        expert,
13319                        &activated,
13320                    )?
13321                };
13322                let weight = route_weights[pair];
13323                for (sum, value) in output
13324                    [token * experts.input_width..(token + 1) * experts.input_width]
13325                    .iter_mut()
13326                    .zip(down)
13327                {
13328                    *sum += weight * value;
13329                }
13330            }
13331        }
13332        Ok(output)
13333    }
13334}
13335
13336#[cfg(test)]
13337mod bank_v2_layout_tests {
13338    use super::{nvfp4_matrix_v2_permute, nvfp4_row_bytes};
13339
13340    /// The v2 bank permutation had NO test at all until 2026-08-29, while its FLAGS row carried
13341    /// a bit-identity claim and the live serving env pinned it on. This pins the DOCUMENTED
13342    /// mapping so a reader can be checked against something: per row, slot g's 16 qs bytes land
13343    /// contiguously at `g*16`, and its two UE4M3 scale bytes at `nslots*16 + g*2`. Source layout
13344    /// is memra `block_nvfp4`: 36-byte superblocks of [4 scale bytes | 32 packed e2m1], two
13345    /// 32-value slots per superblock.
13346    ///
13347    /// It is deliberately a MAPPING test, not a proof of the door: the serving corruption this
13348    /// lane measured is a v1-vs-v2 READER mismatch somewhere downstream, which a host-side
13349    /// permutation test cannot see. Closing that needs a device oracle (follow-up lane).
13350    #[test]
13351    fn the_v2_bank_row_is_the_documented_slot_major_permutation() {
13352        // two rows, in_features 128 => 2 superblocks/row, 4 slots/row, 72 bytes/row.
13353        let (out_f, in_f) = (2usize, 128usize);
13354        let row_bytes = nvfp4_row_bytes(in_f);
13355        assert_eq!(row_bytes, 72);
13356        let v1: Vec<u8> = (0..out_f * row_bytes).map(|i| (i % 251) as u8).collect();
13357        let v2 = nvfp4_matrix_v2_permute(&v1, out_f, in_f);
13358        assert_eq!(v2.len(), v1.len(), "a permutation cannot change the size");
13359        let n_slots = in_f / 32;
13360        for row in 0..out_f {
13361            let src = &v1[row * row_bytes..(row + 1) * row_bytes];
13362            let dst = &v2[row * row_bytes..(row + 1) * row_bytes];
13363            for g in 0..n_slots {
13364                let (sblk, h) = (g / 2, g % 2);
13365                let sb = &src[sblk * 36..sblk * 36 + 36];
13366                assert_eq!(
13367                    &dst[g * 16..g * 16 + 16],
13368                    &sb[4 + 16 * h..4 + 16 * h + 16],
13369                    "row {row} slot {g} codes"
13370                );
13371                assert_eq!(
13372                    &dst[n_slots * 16 + g * 2..n_slots * 16 + g * 2 + 2],
13373                    &sb[2 * h..2 * h + 2],
13374                    "row {row} slot {g} scales"
13375                );
13376            }
13377            // and it moves bytes only: same multiset per row, rows never cross.
13378            let (mut a, mut b) = (src.to_vec(), dst.to_vec());
13379            a.sort_unstable();
13380            b.sort_unstable();
13381            assert_eq!(a, b, "row {row} is not a byte permutation");
13382        }
13383    }
13384}
13385
13386#[cfg(test)]
13387mod tests {
13388
13389    /// THE DEFECT, ASSERTED SO IT CANNOT COME BACK. The retired memo key hashed only the K
13390    /// pointer, the base pointer, the layer and t, while the table it returned ALSO carried
13391    /// the V and LEN pointers. Two different allocation generations that happen to share a K
13392    /// address therefore collide, and the entry the map hands back sends a live launch at
13393    /// another allocation's V and len. This test does not assert the key is fine; it asserts
13394    /// the key is BLIND, which is why `rows_tab_restage_on` exists and defaults ON.
13395    #[test]
13396    fn the_retired_rows_tab_key_cannot_see_the_v_and_len_pointers_it_hands_back() {
13397        let (kp, bp) = (0xdead_0000u64, 0u64);
13398        let live = [[kp, 0x00b1_0000u64, 0x00c1_0000u64, bp]];
13399        let recycled = [[kp, 0x00b2_0000u64, 0x00c2_0000u64, bp]];
13400        assert_eq!(
13401            super::retired_rows_tab_key(kp, bp, 20, 2),
13402            super::retired_rows_tab_key(kp, bp, 20, 2),
13403            "same layer and t must hash the same, or the test proves nothing"
13404        );
13405        let a = super::rows_tab_host(&live, 0x9000, true, 1);
13406        let b = super::rows_tab_host(&recycled, 0x9000, true, 1);
13407        assert_ne!(a, b, "the two generations write DIFFERENT tables");
13408        // ... yet one key covers both, which is exactly the use-after-free.
13409        assert_eq!(
13410            super::retired_rows_tab_key(live[0][0], live[0][3], 20, 1),
13411            super::retired_rows_tab_key(recycled[0][0], recycled[0][3], 20, 1),
13412            "the retired key collides across allocation generations"
13413        );
13414    }
13415
13416    /// The restage must be VALUE-NEUTRAL: on a fresh lookup the memo and the restage produce
13417    /// identical bytes, which is what makes spec-on output byte-identical to spec-off.
13418    #[test]
13419    fn rows_tab_layout_is_the_same_bytes_the_memo_would_have_cached() {
13420        let parts = [
13421            [0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64],
13422            [0x00a1u64, 0x00b1u64, 0x00c1u64, 0x00d1u64],
13423        ];
13424        let same = super::rows_tab_host(&parts, 0x7000, true, 2);
13425        assert_eq!(
13426            same,
13427            vec![
13428                0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64, 0x7000,
13429                1, // row 0: back = t-1-r = 1
13430                0x00a1u64, 0x00b1u64, 0x00c1u64, 0x00d1u64, 0x7000, 0, // row 1: back = 0
13431            ],
13432            "same-session rows share one counter cell and step back t-1-r"
13433        );
13434        let cross = super::rows_tab_host(&parts, 0x7000, false, 2);
13435        assert_eq!(
13436            cross,
13437            vec![
13438                0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64, 0x7000, 0, 0x00a1u64, 0x00b1u64,
13439                0x00c1u64, 0x00d1u64, 0x7004, 0,
13440            ],
13441            "cross-session rows get their own counter cell and no step back"
13442        );
13443    }
13444    use super::*;
13445
13446    #[test]
13447    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
13448        let limit = Some(7.0);
13449        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
13450        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
13451        assert!(
13452            step_expert_activation_host(-20.0, 9.0, limit).abs()
13453                < step_expert_activation_host(-20.0, 9.0, None).abs()
13454        );
13455        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
13456        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
13457        assert!(validate_step_expert_activation_limit(limit).is_ok());
13458    }
13459
13460    #[test]
13461    fn moe_residual_host_preserves_official_add_order() {
13462        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
13463        assert_eq!(output, [0.0]);
13464        assert_eq!(
13465            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
13466            "MoE residual lengths residual=1 routed=2 shared=1"
13467        );
13468    }
13469
13470    #[test]
13471    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
13472        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
13473        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
13474        assert_eq!(owners.len(), 4);
13475        for (rank, owner) in owners.iter().enumerate() {
13476            assert_eq!(owner.rank, rank);
13477            assert_eq!(owner.selected, vec![0, 36]);
13478            assert_eq!(owner.token_rows, vec![0, 0]);
13479            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
13480        }
13481    }
13482
13483    #[test]
13484    fn expert_owner_routes_validate_geometry_and_selected_experts() {
13485        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
13486        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
13487        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
13488        assert!(error.contains("outside 0..288"));
13489    }
13490
13491    #[test]
13492    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
13493        let selected = [
13494            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
13495        ];
13496        assert_eq!(
13497            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
13498            16
13499        );
13500        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
13501        assert_eq!(
13502            owners
13503                .iter()
13504                .map(|owner| owner.selected.len())
13505                .collect::<Vec<_>>(),
13506            vec![2, 4, 6, 4]
13507        );
13508        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
13509        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
13510        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
13511    }
13512
13513    #[test]
13514    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
13515        let owner0 = [0usize, 3];
13516        let owner1 = [1usize, 2];
13517        let owners = [owner0.as_slice(), owner1.as_slice()];
13518        assert_eq!(
13519            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
13520                .unwrap(),
13521            WeightedRouteCombineShape {
13522                pairs: 4,
13523                max_pairs: 12,
13524            }
13525        );
13526        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
13527        assert!(
13528            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
13529                .is_err()
13530        );
13531        assert!(
13532            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
13533                .is_err()
13534        );
13535        assert!(
13536            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
13537                .is_err()
13538        );
13539    }
13540
13541    #[test]
13542    fn native_p2p_door_is_strict_and_default_off() {
13543        assert!(!parse_step_tp_native_p2p(None).unwrap());
13544        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
13545        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
13546        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
13547        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
13548        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
13549    }
13550
13551    #[test]
13552    fn bulk_p2p_door_is_strict_and_default_off() {
13553        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
13554        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
13555        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
13556        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
13557        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
13558        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
13559    }
13560
13561    #[test]
13562    fn ep_device_arithmetic_door_is_strict_and_default_off() {
13563        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
13564        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
13565        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
13566        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
13567        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
13568        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
13569    }
13570
13571    #[test]
13572    fn f32_mirror_door_is_strict_and_default_off() {
13573        assert!(!parse_step_tp_f32_mirror(None).unwrap());
13574        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
13575        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
13576        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
13577        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
13578        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
13579    }
13580
13581    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
13582        let codes = (0..out_features * in_features)
13583            .map(|index| (index % 251) as u8)
13584            .collect();
13585        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
13586            .map(|index| index as f32 + 1.0)
13587            .collect();
13588        (codes, scales)
13589    }
13590
13591    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
13592        (0..out_features * in_features)
13593            .flat_map(|value| (value as u16).to_le_bytes())
13594            .collect()
13595    }
13596
13597    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
13598        bytes
13599            .chunks_exact(2)
13600            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
13601            .collect()
13602    }
13603
13604    #[test]
13605    fn bf16_matrix_rejects_wrong_byte_count() {
13606        let bytes = vec![0u8; 4 * 4 * 2 - 1];
13607        let matrix = Bf16Matrix {
13608            bytes: &bytes,
13609            out_features: 4,
13610            in_features: 4,
13611        };
13612        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
13613    }
13614
13615    #[test]
13616    fn replicated_device_rows_require_exact_rank_local_shapes() {
13617        assert_eq!(
13618            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
13619            12_288
13620        );
13621        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
13622        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
13623        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
13624        assert!(
13625            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
13626        );
13627        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
13628    }
13629
13630    #[test]
13631    fn replicated_device_row_refresh_requires_exact_root_source() {
13632        assert_eq!(
13633            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
13634            12_288
13635        );
13636        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
13637        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
13638        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
13639        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
13640        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
13641    }
13642
13643    #[test]
13644    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
13645        for tp in [1, 2, 4, 8] {
13646            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
13647            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
13648            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
13649            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
13650            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
13651        }
13652        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
13653        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
13654        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
13655        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
13656    }
13657
13658    #[test]
13659    fn cache_rows_split_by_token_then_rank() {
13660        let rows = (0u8..24).collect::<Vec<_>>();
13661        assert_eq!(
13662            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
13663            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
13664        );
13665        assert_eq!(
13666            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
13667            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
13668        );
13669        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
13670        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
13671    }
13672
13673    #[test]
13674    fn bf16_column_shard_preserves_contiguous_output_rows() {
13675        let bytes = bf16_matrix_bytes(4, 4);
13676        let matrix = Bf16Matrix {
13677            bytes: &bytes,
13678            out_features: 4,
13679            in_features: 4,
13680        };
13681        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
13682        assert_eq!(shard.out_features, 2);
13683        assert_eq!(shard.in_features, 4);
13684        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
13685    }
13686
13687    #[test]
13688    fn bf16_row_shard_preserves_each_input_column_window() {
13689        let bytes = bf16_matrix_bytes(3, 4);
13690        let matrix = Bf16Matrix {
13691            bytes: &bytes,
13692            out_features: 3,
13693            in_features: 4,
13694        };
13695        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
13696        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
13697    }
13698
13699    #[test]
13700    fn bf16_row_block_preserves_global_column_order() {
13701        let bytes = bf16_matrix_bytes(3, 8);
13702        let matrix = Bf16Matrix {
13703            bytes: &bytes,
13704            out_features: 3,
13705            in_features: 8,
13706        };
13707        let block = bf16_row_block(matrix, 2, 3).unwrap();
13708        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
13709    }
13710
13711    #[test]
13712    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
13713        let (codes, scales) = matrix(1280, 4096);
13714        let matrix = E4m3BlockMatrix {
13715            codes: &codes,
13716            scales: &scales,
13717            out_features: 1280,
13718            in_features: 4096,
13719        };
13720        let shard = column_shard(matrix, 2, 1).unwrap();
13721        assert_eq!(shard.out_features, 640);
13722        assert_eq!(shard.codes, &codes[640 * 4096..]);
13723        assert_eq!(shard.scales, &scales[5 * 32..]);
13724    }
13725
13726    #[test]
13727    fn row_shard_preserves_each_weight_and_scale_column_window() {
13728        let (codes, scales) = matrix(4096, 1280);
13729        let matrix = E4m3BlockMatrix {
13730            codes: &codes,
13731            scales: &scales,
13732            out_features: 4096,
13733            in_features: 1280,
13734        };
13735        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
13736        assert_eq!(shard_codes.len(), 4096 * 640);
13737        assert_eq!(&shard_codes[..640], &codes[640..1280]);
13738        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
13739        assert_eq!(shard_scales.len(), 32 * 5);
13740        assert_eq!(&shard_scales[..5], &scales[5..10]);
13741        assert_eq!(&shard_scales[5..10], &scales[15..20]);
13742    }
13743
13744    #[test]
13745    fn activation_shards_keep_token_rows_separate() {
13746        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
13747        assert_eq!(
13748            activation_shard(&activations, 2, 8, 2, 1),
13749            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
13750        );
13751    }
13752
13753    #[test]
13754    fn expert_bank_selects_expert_major_code_and_scale_planes() {
13755        let expert_count = 2;
13756        let out_features = 128;
13757        let in_features = 128;
13758        let code_stride = out_features * in_features;
13759        let codes: Vec<u8> = (0..expert_count * code_stride)
13760            .map(|index| (index % 251) as u8)
13761            .collect();
13762        let scales = vec![1.0f32, 2.0];
13763        let bank = E4m3ExpertBank {
13764            codes: &codes,
13765            scales: &scales,
13766            expert_count,
13767            out_features,
13768            in_features,
13769        };
13770        bank.validate().unwrap();
13771        let expert = bank.expert(1).unwrap();
13772        assert_eq!(expert.codes, &codes[code_stride..]);
13773        assert_eq!(expert.scales, &[2.0]);
13774    }
13775
13776    #[test]
13777    fn expert_bank_rejects_non_positive_scale() {
13778        let codes = vec![0u8; 128 * 128];
13779        let scales = vec![0.0f32];
13780        let bank = E4m3ExpertBank {
13781            codes: &codes,
13782            scales: &scales,
13783            expert_count: 1,
13784            out_features: 128,
13785            in_features: 128,
13786        };
13787        assert!(bank.validate().unwrap_err().contains("non-positive"));
13788    }
13789
13790    #[test]
13791    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
13792        let expert_count = 2;
13793        let out_features = 256;
13794        let in_features = 128;
13795        let code_stride = out_features * in_features;
13796        let scale_stride = 2;
13797        let codes = (0..expert_count * code_stride)
13798            .map(|index| (index % 251) as u8)
13799            .collect::<Vec<_>>();
13800        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
13801        let bank = E4m3ExpertBank {
13802            codes: &codes,
13803            scales: &scales,
13804            expert_count,
13805            out_features,
13806            in_features,
13807        };
13808
13809        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
13810        assert_eq!(rank.out_features, 128);
13811        assert_eq!(rank.in_features, 128);
13812        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
13813        assert_eq!(rank.scales, vec![11.0, 21.0]);
13814        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
13815        assert_eq!(
13816            &rank.codes[128 * 128..],
13817            &codes[code_stride + 128 * 128..2 * code_stride]
13818        );
13819        assert_eq!(scale_stride, scales.len() / expert_count);
13820    }
13821
13822    #[test]
13823    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
13824        let expert_count = 2;
13825        let out_features = 128;
13826        let in_features = 256;
13827        let code_stride = out_features * in_features;
13828        let codes = (0..expert_count * code_stride)
13829            .map(|index| (index % 251) as u8)
13830            .collect::<Vec<_>>();
13831        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
13832        let bank = E4m3ExpertBank {
13833            codes: &codes,
13834            scales: &scales,
13835            expert_count,
13836            out_features,
13837            in_features,
13838        };
13839
13840        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
13841        assert_eq!(rank.out_features, 128);
13842        assert_eq!(rank.in_features, 128);
13843        assert_eq!(rank.k_blocks, Some(1));
13844        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
13845        assert_eq!(rank.scales, vec![11.0, 21.0]);
13846        assert_eq!(&rank.codes[..128], &codes[128..256]);
13847        assert_eq!(
13848            &rank.codes[128 * 128..128 * 128 + 128],
13849            &codes[code_stride + 128..code_stride + 256]
13850        );
13851    }
13852
13853    #[test]
13854    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
13855        let expert_count = 2;
13856        let out_features = 256;
13857        let in_features = 512;
13858        let code_stride = out_features * in_features;
13859        let mut codes = vec![0u8; expert_count * code_stride];
13860        for expert in 0..expert_count {
13861            for row in 0..out_features {
13862                for block in 0..4 {
13863                    let value = (expert * 80 + block * 16 + row % 16) as u8;
13864                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
13865                    codes[start..start + FP8_BLOCK].fill(value);
13866                }
13867            }
13868        }
13869        let scales = vec![
13870            1.0f32, 2.0, 3.0, 4.0, 11.0, 12.0, 13.0, 14.0, 101.0, 102.0, 103.0, 104.0, 111.0,
13871            112.0, 113.0, 114.0,
13872        ];
13873        let bank = E4m3ExpertBank {
13874            codes: &codes,
13875            scales: &scales,
13876            expert_count,
13877            out_features,
13878            in_features,
13879        };
13880
13881        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
13882        assert_eq!(rank.out_features, out_features);
13883        assert_eq!(rank.in_features, 256);
13884        assert_eq!(rank.k_blocks, Some(2));
13885        assert_eq!(rank.code_stride, out_features * 256);
13886        assert_eq!(rank.scale_stride, 4);
13887        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
13888        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
13889
13890        let block_stride = out_features * FP8_BLOCK;
13891        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
13892        assert!(
13893            rank.codes[block_stride..block_stride + FP8_BLOCK]
13894                .iter()
13895                .all(|&code| code == 48)
13896        );
13897        assert!(
13898            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
13899                .iter()
13900                .all(|&code| code == 112)
13901        );
13902        assert!(
13903            rank.codes
13904                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
13905                .iter()
13906                .all(|&code| code == 128)
13907        );
13908    }
13909
13910    #[test]
13911    fn step_ep_layer_specs_are_literal_and_fail_closed() {
13912        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
13913        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
13914        assert_eq!(
13915            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
13916            vec![StepEpLayerSpec {
13917                layer: 24,
13918                devices: vec![1, 2],
13919            }]
13920        );
13921        assert_eq!(
13922            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
13923            vec![
13924                StepEpLayerSpec {
13925                    layer: 24,
13926                    devices: vec![1, 2],
13927                },
13928                StepEpLayerSpec {
13929                    layer: 25,
13930                    devices: vec![1, 2],
13931                },
13932                StepEpLayerSpec {
13933                    layer: 31,
13934                    devices: vec![0, 2],
13935                },
13936            ]
13937        );
13938        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
13939        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
13940        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
13941        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
13942        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
13943        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
13944        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
13945    }
13946
13947    #[test]
13948    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
13949        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
13950        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
13951        assert_eq!(
13952            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
13953            vec![
13954                StepTpLayerSpec {
13955                    layer: 24,
13956                    devices: vec![1, 2],
13957                },
13958                StepTpLayerSpec {
13959                    layer: 25,
13960                    devices: vec![1, 2],
13961                },
13962            ]
13963        );
13964        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
13965        assert!(error.contains("MEMRA_STEP_TP"));
13966        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
13967        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
13968
13969        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
13970        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
13971        assert_eq!(all.first().unwrap().layer, 0);
13972        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
13973        let devices = (0..8).collect::<Vec<_>>();
13974        assert!(all.iter().all(|spec| spec.devices == devices));
13975        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
13976    }
13977}
13978
13979// ===== Whole-token graph builder (increment B) ==================================================
13980//
13981// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
13982// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
13983// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
13984// device and records a child + its dependency edges. A token then assembles as ONE multi-device
13985// parent (children per section per layer), launched once per token — the launch-collapse the
13986// per-layer minis could not reach (routes-mini negative, 2026-08-21).
13987
13988/// One captured section: the child graph plus which parent node it became, and the CUDA
13989/// context it was captured under (exec memset updates need it).
13990struct TokenGraphChild {
13991    graph: cudarc::driver::CudaGraph,
13992    node: cudarc::driver::sys::CUgraphNode,
13993    ctx: cudarc::driver::sys::CUcontext,
13994}
13995
13996/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
13997/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
13998/// handles address the parent's CLONED child graphs (the M1-probed update path).
13999struct TokenGraphFaSite {
14000    ctx: cudarc::driver::sys::CUcontext,
14001    memset_o: cudarc::driver::sys::CUgraphNode,
14002    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
14003    fa: cudarc::driver::sys::CUgraphNode,
14004    combine: cudarc::driver::sys::CUgraphNode,
14005    window: usize,
14006    n_head: usize,
14007    n_head_kv: usize,
14008    head_dim: usize,
14009}
14010
14011pub struct TokenGraphBuilder {
14012    parent: cudarc::driver::sys::CUgraph,
14013    children: Vec<TokenGraphChild>,
14014    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
14015    /// several while a parallel group is open.
14016    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
14017    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
14018    /// non-group section (they never gate a parallel group merge — the SH1 shape).
14019    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
14020    /// Open parallel group: sections issued under the same group id fork from the SAME
14021    /// predecessor set and merge into the frontier together when the group closes.
14022    group: Option<(
14023        u32,
14024        Vec<cudarc::driver::sys::CUgraphNode>,
14025        Vec<cudarc::driver::sys::CUgraphNode>,
14026    )>,
14027}
14028
14029// SAFETY: single decode thread; graph handles are process handles.
14030unsafe impl Send for TokenGraphBuilder {}
14031
14032impl TokenGraphBuilder {
14033    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
14034        use cudarc::driver::sys;
14035        let mut parent: sys::CUgraph = std::ptr::null_mut();
14036        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
14037        if r != sys::CUresult::CUDA_SUCCESS {
14038            return Err(format!("token graph create: {r:?}").into());
14039        }
14040        Ok(Self {
14041            parent,
14042            children: Vec::new(),
14043            frontier: Vec::new(),
14044            pending_detached: Vec::new(),
14045            group: None,
14046        })
14047    }
14048
14049    fn push_child(
14050        &mut self,
14051        graph: cudarc::driver::CudaGraph,
14052        parallel_group: Option<u32>,
14053        detached: bool,
14054        absorb: bool,
14055        ctx: cudarc::driver::sys::CUcontext,
14056    ) -> Result<(), Box<dyn std::error::Error>> {
14057        use cudarc::driver::sys;
14058        // Resolve the dependency set: serial sections depend on the current frontier; a
14059        // parallel-group section depends on the frontier AS OF the group opening; a
14060        // DETACHED section forks like a group member but joins only the next serial section.
14061        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
14062            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
14063            (state, Some(group)) => {
14064                // opening a new group (closing any previous one first)
14065                if let Some((_, _, members)) = state.take() {
14066                    self.frontier = members;
14067                }
14068                let base = self.frontier.clone();
14069                *state = Some((group, base.clone(), Vec::new()));
14070                base
14071            }
14072            (state, None) if detached => match state.as_ref() {
14073                Some((_, base, _)) => base.clone(),
14074                None => self.frontier.clone(),
14075            },
14076            (state, None) => {
14077                if let Some((_, _, members)) = state.take() {
14078                    self.frontier = members;
14079                }
14080                let mut deps = self.frontier.clone();
14081                if absorb {
14082                    deps.append(&mut self.pending_detached);
14083                }
14084                deps
14085            }
14086        };
14087        let mut node: sys::CUgraphNode = std::ptr::null_mut();
14088        let r = unsafe {
14089            sys::cuGraphAddChildGraphNode(
14090                &mut node,
14091                self.parent,
14092                if deps.is_empty() {
14093                    std::ptr::null()
14094                } else {
14095                    deps.as_ptr()
14096                },
14097                deps.len(),
14098                graph.cu_graph(),
14099            )
14100        };
14101        if r != sys::CUresult::CUDA_SUCCESS {
14102            return Err(format!("token graph child: {r:?}").into());
14103        }
14104        match (&mut self.group, parallel_group, detached) {
14105            (_, None, true) => self.pending_detached.push(node),
14106            (Some((_, _, members)), Some(_), _) => members.push(node),
14107            _ => self.frontier = vec![node],
14108        }
14109        self.children.push(TokenGraphChild { graph, node, ctx });
14110        Ok(())
14111    }
14112
14113    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
14114        use cudarc::driver::sys;
14115        if let Some((_, _, members)) = self.group.take() {
14116            self.frontier = members;
14117        }
14118        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
14119        // the node handles the exec update path (M1) addresses.
14120        let mut fa_sites = Vec::new();
14121        for child in &self.children {
14122            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
14123                fa_sites.push(site);
14124            }
14125        }
14126        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
14127        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
14128        if r != sys::CUresult::CUDA_SUCCESS {
14129            return Err(format!("token graph instantiate: {r:?}").into());
14130        }
14131        Ok(TokenGraph {
14132            exec,
14133            parent: self.parent,
14134            _children: self.children,
14135            fa_sites,
14136        })
14137    }
14138}
14139
14140/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
14141/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
14142fn discover_fa_site(
14143    child_node: cudarc::driver::sys::CUgraphNode,
14144    ctx: cudarc::driver::sys::CUcontext,
14145) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
14146    use cudarc::driver::sys;
14147    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
14148        if r == sys::CUresult::CUDA_SUCCESS {
14149            Ok(())
14150        } else {
14151            Err(format!("{what}: {r:?}").into())
14152        }
14153    }
14154    let mut graph: sys::CUgraph = std::ptr::null_mut();
14155    unsafe {
14156        cu_try(
14157            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
14158            "fa-site child GetGraph",
14159        )?;
14160    }
14161    let mut count: usize = 0;
14162    unsafe {
14163        cu_try(
14164            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
14165            "fa-site GetNodes(count)",
14166        )?;
14167    }
14168    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
14169    unsafe {
14170        cu_try(
14171            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
14172            "fa-site GetNodes",
14173        )?;
14174    }
14175    nodes.truncate(count);
14176    let node_type =
14177        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
14178            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
14179            unsafe {
14180                cu_try(
14181                    sys::cuGraphNodeGetType(node, &mut ty),
14182                    "fa-site NodeGetType",
14183                )?;
14184            }
14185            Ok(ty)
14186        };
14187    let memsets: Vec<sys::CUgraphNode> = {
14188        let mut v = Vec::new();
14189        for &node in &nodes {
14190            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
14191                v.push(node);
14192            }
14193        }
14194        v
14195    };
14196    if memsets.len() != 3 {
14197        return Ok(None);
14198    }
14199    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
14200    let dependents =
14201        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
14202            let mut n: usize = 0;
14203            unsafe {
14204                cu_try(
14205                    sys::cuGraphNodeGetDependentNodes_v2(
14206                        node,
14207                        std::ptr::null_mut(),
14208                        std::ptr::null_mut(),
14209                        &mut n,
14210                    ),
14211                    "fa-site GetDependentNodes(count)",
14212                )?;
14213            }
14214            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
14215            unsafe {
14216                cu_try(
14217                    sys::cuGraphNodeGetDependentNodes_v2(
14218                        node,
14219                        v.as_mut_ptr(),
14220                        std::ptr::null_mut(),
14221                        &mut n,
14222                    ),
14223                    "fa-site GetDependentNodes",
14224                )?;
14225            }
14226            v.truncate(n);
14227            Ok(v)
14228        };
14229    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
14230    // ordered among themselves but interchangeable for width updates.
14231    let mut fa: Option<sys::CUgraphNode> = None;
14232    let mut last_memset: Option<sys::CUgraphNode> = None;
14233    for &ms in &memsets {
14234        for dep in dependents(ms)? {
14235            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
14236                fa = Some(dep);
14237                last_memset = Some(ms);
14238            }
14239        }
14240    }
14241    let (Some(fa), Some(_last)) = (fa, last_memset) else {
14242        return Ok(None);
14243    };
14244    let mut combine: Option<sys::CUgraphNode> = None;
14245    for dep in dependents(fa)? {
14246        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
14247            combine = Some(dep);
14248        }
14249    }
14250    let Some(combine) = combine else {
14251        return Ok(None);
14252    };
14253    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
14254    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
14255    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
14256    unsafe {
14257        cu_try(
14258            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
14259            "fa-site KernelNodeGetParams",
14260        )?;
14261    }
14262    let arg_i32 =
14263        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
14264    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
14265    // Identify the o-partial memset (hd x wider than the m/l pair).
14266    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
14267        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
14268        unsafe {
14269            cu_try(
14270                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
14271                "fa-site MemsetNodeGetParams",
14272            )?;
14273        }
14274        Ok(mp.width)
14275    };
14276    let mut widest = memsets[0];
14277    for &ms in &memsets[1..] {
14278        if width_of(ms)? > width_of(widest)? {
14279            widest = ms;
14280        }
14281    }
14282    let memset_m: Vec<sys::CUgraphNode> =
14283        memsets.iter().copied().filter(|&m| m != widest).collect();
14284    Ok(Some(TokenGraphFaSite {
14285        ctx,
14286        memset_o: widest,
14287        memset_m: [memset_m[0], memset_m[1]],
14288        fa,
14289        combine,
14290        window: win as usize,
14291        n_head: nh as usize,
14292        n_head_kv: nhkv as usize,
14293        head_dim: hd as usize,
14294    }))
14295}
14296
14297pub struct TokenGraph {
14298    exec: cudarc::driver::sys::CUgraphExec,
14299    parent: cudarc::driver::sys::CUgraph,
14300    _children: Vec<TokenGraphChild>,
14301    fa_sites: Vec<TokenGraphFaSite>,
14302}
14303
14304unsafe impl Send for TokenGraph {}
14305
14306impl TokenGraph {
14307    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
14308    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
14309    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
14310    /// move together so the exec always matches what a fresh build at `bucket` would bake.
14311    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
14312        use cudarc::driver::sys;
14313        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
14314            if r == sys::CUresult::CUDA_SUCCESS {
14315                Ok(())
14316            } else {
14317                Err(format!("{what}: {r:?}").into())
14318            }
14319        }
14320        for site in &self.fa_sites {
14321            let layer_bucket = if site.window > 0 {
14322                bucket.min(site.window)
14323            } else {
14324                bucket
14325            };
14326            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
14327            let nsp = layer_bucket.div_ceil(sp).max(1);
14328            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
14329            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
14330            unsafe {
14331                cu_try(
14332                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
14333                    "retarget fa GetParams",
14334                )?;
14335                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
14336                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
14337                params.gridDimY = nsp as u32;
14338                cu_try(
14339                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
14340                    "retarget fa SetParams",
14341                )?;
14342            }
14343            // combine: nsp (slot 6).
14344            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
14345            unsafe {
14346                cu_try(
14347                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
14348                    "retarget combine GetParams",
14349                )?;
14350                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
14351                cu_try(
14352                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
14353                    "retarget combine SetParams",
14354                )?;
14355            }
14356            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
14357            let set_width =
14358                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
14359                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
14360                    unsafe {
14361                        cu_try(
14362                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
14363                            "retarget memset GetParams",
14364                        )?;
14365                    }
14366                    mp.width = width;
14367                    unsafe {
14368                        cu_try(
14369                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
14370                            "retarget memset SetParams",
14371                        )?;
14372                    }
14373                    Ok(())
14374                };
14375            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
14376            set_width(site.memset_m[0], site.n_head * nsp)?;
14377            set_width(site.memset_m[1], site.n_head * nsp)?;
14378        }
14379        Ok(())
14380    }
14381
14382    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
14383        use cudarc::driver::sys;
14384        let _main = e.gpu.enter_main()?;
14385        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
14386        if r != sys::CUresult::CUDA_SUCCESS {
14387            return Err(format!("token graph launch: {r:?}").into());
14388        }
14389        Ok(())
14390    }
14391}
14392
14393impl Drop for TokenGraph {
14394    fn drop(&mut self) {
14395        unsafe {
14396            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
14397            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
14398        }
14399    }
14400}
14401
14402std::thread_local! {
14403    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
14404        const { std::cell::RefCell::new(None) };
14405}
14406
14407/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
14408pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
14409    let builder = TokenGraphBuilder::new()?;
14410    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
14411    Ok(())
14412}
14413
14414/// Take the finished parent (ends build mode).
14415pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
14416    let builder = TOKEN_GRAPH_BUILDER
14417        .with(|cell| cell.borrow_mut().take())
14418        .ok_or("token graph build was not begun")?;
14419    builder.finish()
14420}
14421
14422/// True while the thread-local builder is armed.
14423pub fn token_graph_building() -> bool {
14424    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
14425}
14426
14427/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
14428/// stream capture on `engine`'s stream and records the child. Sections sharing a
14429/// `parallel_group` id fork from the same predecessor set and merge together. The closure
14430/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
14431pub fn graph_section<F>(
14432    engine: &Engine,
14433    parallel_group: Option<u32>,
14434    f: F,
14435) -> Result<(), Box<dyn std::error::Error>>
14436where
14437    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
14438{
14439    graph_section_opts(engine, parallel_group, false, false, f)
14440}
14441
14442/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
14443pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
14444where
14445    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
14446{
14447    graph_section_opts(engine, None, false, true, f)
14448}
14449
14450/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
14451/// group base) and is joined only by the next serial section — never gates a group merge.
14452pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
14453where
14454    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
14455{
14456    graph_section_opts(engine, None, true, false, f)
14457}
14458
14459pub fn graph_section_opts<F>(
14460    engine: &Engine,
14461    parallel_group: Option<u32>,
14462    detached: bool,
14463    absorb: bool,
14464    f: F,
14465) -> Result<(), Box<dyn std::error::Error>>
14466where
14467    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
14468{
14469    let building = token_graph_building();
14470    if !building {
14471        let mut f = f;
14472        return f();
14473    }
14474    let (child, ctx) = {
14475        let _main = engine.gpu.enter_main()?;
14476        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
14477        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
14478        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
14479            return Err(format!("graph section ctx query: {r:?}").into());
14480        }
14481        let mut f = f;
14482        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
14483        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
14484        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
14485        (child, ctx)
14486    };
14487    TOKEN_GRAPH_BUILDER.with(|cell| {
14488        cell.borrow_mut()
14489            .as_mut()
14490            .expect("builder checked above")
14491            .push_child(child, parallel_group, detached, absorb, ctx)
14492    })
14493}