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_MIRROR=1: the per-rank routed-selection pull runs as ONE `moe_sel_w_mirror`
182/// launch instead of two 32-byte D2D copies, and when every consuming rank shares e's device
183/// the intermediate e-context staging pair is skipped entirely (the caller's sel/route_w rows
184/// are process-persistent, so the ranks read them directly). Bit-identical: same bytes, one
185/// fewer hop. Refused under the graph door, whose captured copies need the fixed staging
186/// addresses. Default OFF until receipted.
187/// MEMRA_FENCE_RANK1=1: the peer rank rings a doorbell in ROOT memory with a kernel store
188/// (`memra_ring_flag`) and the model engine waits it with a SAME-DEVICE stream memop, instead
189/// of waiting a cross-device event. Completes the half the memops receipt left open (peer
190/// memops are rejected; peer kernel stores are the direct-join mechanism). Ordering only —
191/// values are untouched. Requires MEMRA_FENCE_MEMOPS=1 (it owns the flag allocation).
192pub(crate) fn fence_rank1_on() -> bool {
193    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
194    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_RANK1").as_deref() == Ok("1"))
195}
196
197/// MEMRA_SPEC_FA2=1 (the DSpark verify lesson): the T=2 verify walk defers each column's
198/// ATTENTION CORE — the dcw arm appends the column's K/V and stashes its post-rope q and
199/// gate rows, then ONE fa_decode_dcw2 per rank walks the KV stream once for both columns
200/// (per-row causal bounds; bit-identical per row under the equal-partition guard), the
201/// per-row combine writes both gated rows, and the o_proj join runs on the TCOL slabs.
202/// ROW-TABLE RESTAGE (`MEMRA_ROWS_TAB_RESTAGE`, DEFAULT ON since this lane).
203///
204/// ON: `decode_v2_rope_fa_rows` builds the 6-word-per-row pointer table from the caller's
205/// freshly-read live cache pointers and stages it into a persistent per-rank slab before
206/// every launch. OFF (`=0`): the retired process-lifetime `rows_tabs` memo, keyed by a hash
207/// of (k pointer, base pointer, layer, t) that could not see the V or LEN pointers the
208/// entry also carried, and that nothing invalidated when a session's KV cache was dropped.
209///
210/// Default ON because the OFF arm is a proven use-after-free, not a slower correct path:
211/// on step37-flash with MEMRA_FUSE_ROPE_APPEND=1 it made speculative decoding unservable
212/// (whole non-finite verify rows, then CUDA_ERROR_ILLEGAL_ADDRESS). ON is value-neutral on
213/// every fresh lookup by construction: identical bytes reach the same kernels. Rollback
214/// seam: `MEMRA_ROWS_TAB_RESTAGE=0`.
215/// The 6-word-per-row launch table `{k, v, len, base, ctr, back}` the fused rope/append/fa
216/// kernels dereference. Pure so it can be tested: the words come from the caller's live
217/// per-row `[k, v, len, base]` pointers, `ctr` is this rank's counter slab (one shared cell
218/// for same-session rows, one cell per row otherwise) and `back` is the same-session causal
219/// step-back `t-1-r` (0 across sessions, where each row owns its own len).
220pub(crate) fn rows_tab_host(
221    parts_rank: &[[u64; 4]],
222    ctr_base: u64,
223    same_session: bool,
224    t: usize,
225) -> Vec<u64> {
226    let mut host = Vec::with_capacity(t * 6);
227    for (r, parts) in parts_rank.iter().enumerate().take(t) {
228        host.extend_from_slice(&[
229            parts[0],
230            parts[1],
231            parts[2],
232            parts[3],
233            if same_session {
234                ctr_base
235            } else {
236                ctr_base + (r as u64) * 4
237            },
238            if same_session {
239                (t - 1 - r) as u64
240            } else {
241                0u64
242            },
243        ]);
244    }
245    host
246}
247
248/// The RETIRED memo key, kept ONLY so a test can assert what it cannot see. Both historical
249/// call sites hashed a SUBSET of the pointers the table carries; this reproduces the verify
250/// site's formula verbatim.
251#[cfg(test)]
252pub(crate) fn retired_rows_tab_key(kp: u64, bp: u64, il: usize, t: usize) -> u64 {
253    kp.rotate_left(17)
254        .wrapping_add(bp)
255        .wrapping_add((il as u64) << 32)
256        .wrapping_add(t as u64)
257        .wrapping_add(1 << 63)
258}
259
260pub(crate) fn rows_tab_restage_on() -> bool {
261    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
262    *ON.get_or_init(|| std::env::var("MEMRA_ROWS_TAB_RESTAGE").as_deref() != Ok("0"))
263}
264
265/// STALE-HIT RECEIPT (`MEMRA_ROWS_TAB_STALE_SCAN`, DEFAULT OFF, diagnostic only).
266///
267/// Keeps a HOST shadow of the last table staged under each retired memo key and prints one
268/// line whenever the key repeats with different contents, naming the words that moved. It
269/// costs a host hash lookup and a small clone per rank per layer per verify round, so it is
270/// off in serving. `[rows-tab] engaged=` on the counter proves the path executes at all,
271/// which is what separates "the memo was innocent" from "the memo never ran".
272/// Rollback seam: unset it (or `=0`).
273pub(crate) fn rows_tab_stale_scan() -> bool {
274    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
275    *ON.get_or_init(|| std::env::var("MEMRA_ROWS_TAB_STALE_SCAN").as_deref() == Ok("1"))
276}
277
278pub(crate) static ROWS_TAB_ENGAGED: std::sync::atomic::AtomicU64 =
279    std::sync::atomic::AtomicU64::new(0);
280pub(crate) static ROWS_TAB_STALE: std::sync::atomic::AtomicU64 =
281    std::sync::atomic::AtomicU64::new(0);
282
283pub(crate) fn spec_fa2_on() -> bool {
284    static ENV: std::sync::OnceLock<Option<bool>> = std::sync::OnceLock::new();
285    crate::step37_door(&ENV, "MEMRA_SPEC_FA2")
286}
287thread_local! {
288    /// The verify driver arms the column before each per-column attention call; the dcw
289    /// arm takes it (once) and stashes q/gate instead of running fa+finish.
290    static SPEC_FA2_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
291    static SPEC_FA2_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
292}
293pub(crate) fn set_spec_fa2_defer(c: Option<usize>) {
294    SPEC_FA2_DEFER.with(|x| x.set(c));
295}
296pub(crate) fn take_spec_fa2_defer() -> Option<usize> {
297    SPEC_FA2_DEFER.with(|x| x.take())
298}
299pub(crate) fn set_spec_fa2_stashed() {
300    SPEC_FA2_STASHED.with(|x| x.set(true));
301}
302pub(crate) fn take_spec_fa2_stashed() -> bool {
303    SPEC_FA2_STASHED.with(|x| x.replace(false))
304}
305
306pub(crate) fn sel_mirror_on() -> bool {
307    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
308    *ON.get_or_init(|| std::env::var("MEMRA_SEL_MIRROR").as_deref() == Ok("1"))
309}
310
311/// MEMRA_STEP_NVFP4_EP2=1: whole-expert (expert-parallel) NVFP4 banks at 2 ranks — expert e
312/// lives ENTIRE on rank (e & 1) at bank slot (e >> 1), replacing the TP column/row shards
313/// (same total VRAM; both sets cannot coexist). Decode rides owner-guarded full-width
314/// sweeps with per-rank slot-ordered partial sums; the cross-rank join is unchanged.
315/// NUMERIC-CLASS door (the slot chain regroups per rank): run-gen argmax gate + battery +
316/// fresh tape, the DEV_ROUTES acceptance class.
317pub(crate) fn step_nvfp4_ep2_on() -> bool {
318    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
319    *ON.get_or_init(|| std::env::var("MEMRA_STEP_NVFP4_EP2").as_deref() == Ok("1"))
320}
321
322pub(crate) fn oproj_direct_on() -> bool {
323    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
324    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
325}
326
327pub(crate) fn raw_copy_bytes(
328    dst: u64,
329    src: u64,
330    bytes: usize,
331    engine: &Engine,
332) -> Result<(), Box<dyn std::error::Error>> {
333    use cudarc::driver::sys;
334    let r = unsafe {
335        sys::cuMemcpyAsync(
336            dst as sys::CUdeviceptr,
337            src as sys::CUdeviceptr,
338            bytes,
339            engine.stream().cu_stream() as sys::CUstream,
340        )
341    };
342    if r == sys::CUresult::CUDA_SUCCESS {
343        Ok(())
344    } else {
345        // MEMRA_RAW_COPY_TRACE=1: a raw D2D failure carries no call site by itself, and
346        // every slab-width bug in the t-row family surfaces here. Operands + backtrace.
347        if std::env::var("MEMRA_RAW_COPY_TRACE").as_deref() == Ok("1") {
348            eprintln!(
349                "[raw-copy-fail] dst={dst:#x} src={src:#x} bytes={bytes} {r:?}\n{}",
350                std::backtrace::Backtrace::force_capture()
351            );
352        }
353        Err(format!("raw_copy_bytes: {r:?} bytes={bytes} dst={dst:#x} src={src:#x}").into())
354    }
355}
356
357pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
358    let silu = gate / (1.0 + (-gate).exp());
359    match limit {
360        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
361        None => silu * up,
362    }
363}
364
365#[derive(Debug, Clone, PartialEq, Eq)]
366struct ExpertOwnerRoutes {
367    rank: usize,
368    selected: Vec<usize>,
369    token_rows: Vec<usize>,
370    global_pairs: Vec<usize>,
371}
372
373fn partition_expert_owner_routes(
374    expert_count: usize,
375    ranks: usize,
376    tokens: usize,
377    experts_per_token: usize,
378    selected: &[usize],
379) -> Result<Vec<ExpertOwnerRoutes>, String> {
380    if expert_count == 0
381        || ranks == 0
382        || tokens == 0
383        || experts_per_token == 0
384        || expert_count % ranks != 0
385    {
386        return Err(format!(
387            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
388             tokens={tokens} experts_per_token={experts_per_token}"
389        ));
390    }
391    let pairs = tokens
392        .checked_mul(experts_per_token)
393        .ok_or("expert-owner route count overflow")?;
394    if selected.len() != pairs {
395        return Err(format!(
396            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
397            selected.len()
398        ));
399    }
400    let per_rank = expert_count / ranks;
401    let mut owners = (0..ranks)
402        .map(|rank| ExpertOwnerRoutes {
403            rank,
404            selected: Vec::new(),
405            token_rows: Vec::new(),
406            global_pairs: Vec::new(),
407        })
408        .collect::<Vec<_>>();
409    for (pair, &expert) in selected.iter().enumerate() {
410        if expert >= expert_count {
411            return Err(format!(
412                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
413            ));
414        }
415        let rank = expert / per_rank;
416        owners[rank].selected.push(expert - rank * per_rank);
417        owners[rank].token_rows.push(pair / experts_per_token);
418        owners[rank].global_pairs.push(pair);
419    }
420    Ok(owners)
421}
422
423fn validate_step_grouped_owner_routes(
424    expert_count: usize,
425    tokens: usize,
426    selected: &[usize],
427) -> Result<usize, String> {
428    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
429        return Err(format!(
430            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
431             experts={expert_count} tokens={tokens}",
432            STEP_GROUPED_FP8_EXPERTS
433        ));
434    }
435    let pairs = tokens
436        .checked_mul(STEP_GROUPED_FP8_TOP_K)
437        .ok_or("official Step owner-grouped FP8 route count overflow")?;
438    if selected.len() != pairs {
439        return Err(format!(
440            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
441            selected.len(),
442            STEP_GROUPED_FP8_TOP_K,
443        ));
444    }
445    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
446        let mut unique = routes.to_vec();
447        unique.sort_unstable();
448        unique.dedup();
449        if unique.len() != STEP_GROUPED_FP8_TOP_K {
450            return Err(format!(
451                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
452                 {routes:?}"
453            ));
454        }
455    }
456    Ok(pairs)
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Eq)]
460struct WeightedRouteCombineShape {
461    pairs: usize,
462    max_pairs: usize,
463}
464
465fn validate_weighted_route_combine(
466    width: usize,
467    experts_per_token: usize,
468    max_tokens: usize,
469    tokens: usize,
470    owner_global_pairs: &[&[usize]],
471    route_weights: &[f32],
472) -> Result<WeightedRouteCombineShape, String> {
473    if width == 0
474        || experts_per_token == 0
475        || max_tokens == 0
476        || tokens == 0
477        || tokens > max_tokens
478        || width > i32::MAX as usize
479        || experts_per_token > i32::MAX as usize
480        || tokens > i32::MAX as usize
481    {
482        return Err(format!(
483            "invalid weighted route combine geometry width={width} experts_per_token=\
484             {experts_per_token} tokens={tokens}/{max_tokens}"
485        ));
486    }
487    let pairs = tokens
488        .checked_mul(experts_per_token)
489        .ok_or("weighted route combine pair count overflow")?;
490    let max_pairs = max_tokens
491        .checked_mul(experts_per_token)
492        .ok_or("weighted route combine capacity overflow")?;
493    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
494        return Err(format!(
495            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
496            route_weights.len()
497        ));
498    }
499    let mut seen = vec![false; pairs];
500    let mut observed = 0usize;
501    for pairs_for_owner in owner_global_pairs {
502        observed = observed
503            .checked_add(pairs_for_owner.len())
504            .ok_or("weighted route combine observed pair count overflow")?;
505        for &pair in *pairs_for_owner {
506            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
507                return Err(format!(
508                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
509                ));
510            }
511        }
512    }
513    if observed != pairs || seen.iter().any(|present| !present) {
514        return Err(format!(
515            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
516        ));
517    }
518    Ok(WeightedRouteCombineShape { pairs, max_pairs })
519}
520
521fn cache_rank_rows(
522    rows: &[u8],
523    tokens: usize,
524    local_token_bytes: usize,
525    ranks: usize,
526    rank: usize,
527) -> Result<Vec<u8>, String> {
528    if ranks == 0 || rank >= ranks {
529        return Err(format!(
530            "TP cache rank {rank} is outside a {ranks}-rank layout"
531        ));
532    }
533    let global_token_bytes = local_token_bytes
534        .checked_mul(ranks)
535        .ok_or("TP cache global token-byte overflow")?;
536    let expected = tokens
537        .checked_mul(global_token_bytes)
538        .ok_or("TP cache row-byte overflow")?;
539    if rows.len() != expected {
540        return Err(format!(
541            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
542            rows.len()
543        ));
544    }
545    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
546    for token in 0..tokens {
547        let start = token * global_token_bytes + rank * local_token_bytes;
548        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
549    }
550    Ok(shard)
551}
552
553fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
554    match value {
555        None | Some("") | Some("0") => Ok(false),
556        Some("1") => Ok(true),
557        Some(value) => Err(format!(
558            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
559        )),
560    }
561}
562
563pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
564    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
565}
566
567fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
568    match value {
569        None | Some("") | Some("0") => Ok(false),
570        Some("1") => Ok(true),
571        Some(value) => Err(format!(
572            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
573        )),
574    }
575}
576
577pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
578    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
579}
580
581fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
582    match value {
583        None | Some("") | Some("0") => Ok(false),
584        Some("1") => Ok(true),
585        Some(value) => Err(format!(
586            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
587        )),
588    }
589}
590
591fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
592    match value {
593        None | Some("") | Some("0") => Ok(false),
594        Some("1") => Ok(true),
595        Some(value) => Err(format!(
596            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
597        )),
598    }
599}
600
601/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
602/// host-canonical program remains the oracle until the device path carries its own gates.
603pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
604    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
605}
606
607pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
608    parse_step_ep_device_arithmetic(
609        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
610            .ok()
611            .as_deref(),
612    )
613}
614
615fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
616    match value {
617        None | Some("") | Some("0") => Ok(false),
618        Some("1") => Ok(true),
619        Some(value) => Err(format!(
620            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
621        )),
622    }
623}
624
625pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
626    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
627}
628
629fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
630    match value {
631        None | Some("") | Some("0") => Ok(false),
632        Some("1") => Ok(true),
633        Some(value) => Err(format!(
634            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
635        )),
636    }
637}
638
639/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
640/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
641/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
642/// side of the comparison).
643pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
644    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
645}
646
647fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
648    match value {
649        None | Some("") | Some("0") => Ok(false),
650        Some("1") => Ok(true),
651        Some(value) => Err(format!(
652            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
653        )),
654    }
655}
656
657fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
658    match value {
659        None | Some("") | Some("0") => Ok(false),
660        Some("1") => Ok(true),
661        Some(value) => Err(format!(
662            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
663        )),
664    }
665}
666
667/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
668/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
669/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
670pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
671    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
672}
673
674fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
675    match value {
676        None | Some("") | Some("0") => Ok(false),
677        Some("1") => Ok(true),
678        Some(value) => Err(format!(
679            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
680        )),
681    }
682}
683
684fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
685    match value {
686        None | Some("") | Some("0") => Ok(false),
687        Some("1") => Ok(true),
688        Some(value) => Err(format!(
689            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
690        )),
691    }
692}
693
694/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
695/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
696/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
697/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
698pub fn step_tp_dcw_enabled() -> Result<bool, String> {
699    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
700}
701
702/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
703/// expert program — per-layer multi-device parents built from per-rank children, launched on
704/// the model engine's stream; zero per-token node updates). Mechanism proven by
705/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
706pub fn step_tp_graph_enabled() -> Result<bool, String> {
707    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
708}
709
710/// GRAPH-LAUNCH HEADROOM GUARD for the routed-prejoin graph door (see
711/// `spec::GRAPH_LAUNCH_MIN_FREE`): checked on the launching engine only when the door
712/// is armed (short-circuit after `step_tp_graph_enabled`), noting once per process with
713/// the sweep's grep-stable `graph replay suspended:` key.
714fn step_tp_graph_headroom_ok(e: &Engine) -> bool {
715    let ok = crate::spec::graph_launch_headroom_ok(e);
716    if !ok {
717        static NOTED: std::sync::Once = std::sync::Once::new();
718        NOTED.call_once(|| crate::spec::graph_replay_suspended_note("step-tp-routes"));
719    }
720    ok
721}
722
723/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
724/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
725/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
726pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
727    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
728}
729
730#[derive(Debug, Clone, PartialEq, Eq)]
731pub struct StepEpLayerSpec {
732    pub layer: usize,
733    pub devices: Vec<usize>,
734}
735
736pub type StepTpLayerSpec = StepEpLayerSpec;
737
738fn parse_step_layer_specs(
739    flag: &str,
740    value: Option<&str>,
741    allow_full_model: bool,
742) -> Result<Vec<StepEpLayerSpec>, String> {
743    let Some(value) = value else {
744        return Ok(Vec::new());
745    };
746    if value.is_empty() || value == "0" {
747        return Ok(Vec::new());
748    }
749
750    let mut specs = Vec::new();
751    for item in value.split(';') {
752        let (layers, devices) = item.split_once('@').ok_or_else(|| {
753            let layers = if allow_full_model {
754                "LAYER[-LAYER] or all"
755            } else {
756                "LAYER[-LAYER]"
757            };
758            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
759        })?;
760        let (first, last) = if layers == "all" {
761            if !allow_full_model {
762                return Err(format!(
763                    "{flag} does not support the full-model shorthand; assign routed layers \
764                     explicitly"
765                ));
766            }
767            (0, STEP37_TRUNK_LAYERS - 1)
768        } else {
769            match layers.split_once('-') {
770                Some((first, last)) => {
771                    let first = first
772                        .parse::<usize>()
773                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
774                    let last = last
775                        .parse::<usize>()
776                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
777                    if first > last {
778                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
779                    }
780                    if last - first + 1 > 128 {
781                        return Err(format!(
782                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
783                        ));
784                    }
785                    (first, last)
786                }
787                None => {
788                    let layer = layers
789                        .parse::<usize>()
790                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
791                    (layer, layer)
792                }
793            }
794        };
795        let devices = devices
796            .split(',')
797            .map(|device| {
798                device
799                    .parse::<usize>()
800                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
801            })
802            .collect::<Result<Vec<_>, _>>()?;
803        if !(2..=8).contains(&devices.len()) {
804            return Err(format!(
805                "{flag} requires 2..=8 devices, got {}",
806                devices.len()
807            ));
808        }
809        let mut unique = devices.clone();
810        unique.sort_unstable();
811        unique.dedup();
812        if unique.len() != devices.len() {
813            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
814        }
815        for layer in first..=last {
816            if specs
817                .iter()
818                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
819            {
820                return Err(format!("{flag} assigns layer {layer} more than once"));
821            }
822            specs.push(StepEpLayerSpec {
823                layer,
824                devices: devices.clone(),
825            });
826        }
827    }
828    Ok(specs)
829}
830
831pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
832    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
833}
834
835pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
836    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
837}
838
839pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
840    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
841}
842
843pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
844    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
845}
846
847#[derive(Clone, Copy)]
848pub struct E4m3BlockMatrix<'a> {
849    pub codes: &'a [u8],
850    pub scales: &'a [f32],
851    pub out_features: usize,
852    pub in_features: usize,
853}
854
855impl E4m3BlockMatrix<'_> {
856    fn validate(&self) -> Result<(), String> {
857        let code_count = self
858            .out_features
859            .checked_mul(self.in_features)
860            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
861        if self.codes.len() != code_count {
862            return Err(format!(
863                "E4M3 code count {} != {}x{} ({code_count})",
864                self.codes.len(),
865                self.out_features,
866                self.in_features,
867            ));
868        }
869        let scale_count =
870            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
871        if self.scales.len() != scale_count {
872            return Err(format!(
873                "E4M3 scale count {} != {scale_count} for {}x{}",
874                self.scales.len(),
875                self.out_features,
876                self.in_features,
877            ));
878        }
879        if !self
880            .scales
881            .iter()
882            .all(|scale| scale.is_finite() && *scale > 0.0)
883        {
884            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
885        }
886        Ok(())
887    }
888}
889
890#[derive(Clone, Copy)]
891pub struct E4m3ExpertBank<'a> {
892    pub codes: &'a [u8],
893    pub scales: &'a [f32],
894    pub expert_count: usize,
895    pub out_features: usize,
896    pub in_features: usize,
897}
898
899impl E4m3ExpertBank<'_> {
900    fn validate(&self) -> Result<(), String> {
901        if self.expert_count == 0 {
902            return Err("E4M3 expert bank is empty".to_string());
903        }
904        let code_stride = self
905            .out_features
906            .checked_mul(self.in_features)
907            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
908        let code_count = self
909            .expert_count
910            .checked_mul(code_stride)
911            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
912        if self.codes.len() != code_count {
913            return Err(format!(
914                "E4M3 expert code count {} != {}x{} ({code_count})",
915                self.codes.len(),
916                self.expert_count,
917                code_stride,
918            ));
919        }
920        let scale_stride =
921            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
922        let scale_count = self
923            .expert_count
924            .checked_mul(scale_stride)
925            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
926        if self.scales.len() != scale_count {
927            return Err(format!(
928                "E4M3 expert scale count {} != {}x{} ({scale_count})",
929                self.scales.len(),
930                self.expert_count,
931                scale_stride,
932            ));
933        }
934        if !self
935            .scales
936            .iter()
937            .all(|scale| scale.is_finite() && *scale > 0.0)
938        {
939            return Err(
940                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
941            );
942        }
943        Ok(())
944    }
945
946    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
947        if expert >= self.expert_count {
948            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
949        }
950        let code_stride = self.out_features * self.in_features;
951        let scale_stride =
952            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
953        Ok(E4m3BlockMatrix {
954            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
955            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
956            out_features: self.out_features,
957            in_features: self.in_features,
958        })
959    }
960}
961
962pub struct ColumnParallelResult {
963    pub gathered: Vec<f32>,
964    pub rank_outputs: Vec<Vec<f32>>,
965}
966
967pub struct RowParallelResult {
968    pub reduced: Vec<f32>,
969    pub rank_partials: Vec<Vec<f32>>,
970}
971
972#[derive(Clone, Copy)]
973pub struct Bf16Matrix<'a> {
974    pub bytes: &'a [u8],
975    pub out_features: usize,
976    pub in_features: usize,
977}
978
979impl Bf16Matrix<'_> {
980    pub fn validate(&self) -> Result<(), String> {
981        if self.out_features == 0 || self.in_features == 0 {
982            return Err("BF16 matrix dimensions must be nonzero".into());
983        }
984        let expected = self
985            .out_features
986            .checked_mul(self.in_features)
987            .and_then(|values| values.checked_mul(2))
988            .ok_or("BF16 matrix byte count overflow")?;
989        if self.bytes.len() != expected {
990            return Err(format!(
991                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
992                self.bytes.len(),
993                self.out_features,
994                self.in_features,
995            ));
996        }
997        Ok(())
998    }
999}
1000
1001struct ResidentE4m3Rank {
1002    codes: CudaSlice<u8>,
1003    scales: CudaSlice<f32>,
1004    out_features: usize,
1005    in_features: usize,
1006}
1007
1008enum ResidentBf16Weight {
1009    Bf16(CudaSlice<u8>),
1010    F32(CudaSlice<f32>),
1011}
1012
1013impl ResidentBf16Weight {
1014    fn ordinal(&self) -> usize {
1015        match self {
1016            Self::Bf16(bytes) => bytes.ordinal(),
1017            Self::F32(values) => values.ordinal(),
1018        }
1019    }
1020}
1021
1022struct ResidentBf16Rank {
1023    weight: ResidentBf16Weight,
1024    out_features: usize,
1025    in_features: usize,
1026    /// q8_0 mirror built at load under MEMRA_STEP_TP_W8 (numeric-class door; the bf16 slab
1027    /// stays resident because every prefill/verify path is qualified against it).
1028    q8: Option<CudaSlice<u8>>,
1029}
1030
1031pub struct ResidentColumnParallel {
1032    ranks: Vec<ResidentE4m3Rank>,
1033    out_features: usize,
1034    in_features: usize,
1035}
1036
1037pub struct ResidentRowParallel {
1038    ranks: Vec<ResidentE4m3Rank>,
1039    out_features: usize,
1040    in_features: usize,
1041}
1042
1043pub struct ResidentBf16ColumnParallel {
1044    ranks: Vec<ResidentBf16Rank>,
1045    out_features: usize,
1046    in_features: usize,
1047    canonical_chunk_rows: Option<usize>,
1048}
1049
1050pub struct ResidentBf16RowParallel {
1051    ranks: Vec<ResidentBf16Rank>,
1052    out_features: usize,
1053    in_features: usize,
1054}
1055
1056pub struct ResidentStepBf16RowParallel {
1057    ranks: Vec<Vec<ResidentBf16Rank>>,
1058    out_features: usize,
1059    in_features: usize,
1060    canonical_chunk_cols: usize,
1061}
1062
1063/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
1064pub struct ResidentSigmoidTopKRouter {
1065    weight: CudaSlice<f32>,
1066    correction_bias: CudaSlice<f32>,
1067    active: CudaSlice<u8>,
1068    root_device: usize,
1069    input_width: usize,
1070    expert_count: usize,
1071    experts_per_token: usize,
1072    active_count: usize,
1073    scaling_factor: f32,
1074    route_norm: bool,
1075}
1076
1077pub struct SigmoidTopKHostOutput {
1078    pub logits: Vec<f32>,
1079    pub selected: Vec<u32>,
1080    pub weights: Vec<f32>,
1081}
1082
1083/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
1084pub struct ResidentReplicatedBf16SwiGlu {
1085    gate: Vec<ResidentBf16Rank>,
1086    up: Vec<ResidentBf16Rank>,
1087    down: Vec<ResidentBf16Rank>,
1088    input_width: usize,
1089    intermediate_width: usize,
1090}
1091
1092/// One token-major F32 batch replicated across a native-P2P rank group.
1093///
1094/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
1095/// substrate between independently sharded operators; it carries no model or topology claim.
1096pub struct ResidentReplicatedDeviceRows {
1097    ranks: Vec<CudaSlice<f32>>,
1098    tokens: usize,
1099    width: usize,
1100}
1101
1102impl ResidentReplicatedDeviceRows {
1103    pub fn tokens(&self) -> usize {
1104        self.tokens
1105    }
1106
1107    pub fn width(&self) -> usize {
1108        self.width
1109    }
1110
1111    pub fn ranks(&self) -> usize {
1112        self.ranks.len()
1113    }
1114}
1115
1116/// Canonical MoE output order: routed plus shared, then add the layer residual.
1117pub fn moe_residual_host(
1118    residual: &[f32],
1119    routed: &[f32],
1120    shared: &[f32],
1121) -> Result<Vec<f32>, String> {
1122    if residual.len() != routed.len() || residual.len() != shared.len() {
1123        return Err(format!(
1124            "MoE residual lengths residual={} routed={} shared={}",
1125            residual.len(),
1126            routed.len(),
1127            shared.len()
1128        ));
1129    }
1130    let ffn = routed
1131        .iter()
1132        .zip(shared)
1133        .map(|(&routed, &shared)| routed + shared)
1134        .collect::<Vec<_>>();
1135    Ok(residual
1136        .iter()
1137        .zip(ffn)
1138        .map(|(&residual, ffn)| residual + ffn)
1139        .collect())
1140}
1141
1142pub use memra_kv::{
1143    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1144};
1145
1146/// Persistent TP2/TP4/TP8 routed-expert reference.
1147///
1148/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1149/// Activations and deterministic host-staged collectives remain per invocation. This is the
1150/// correctness substrate for serving TP/EP, not product-throughput evidence.
1151pub struct ResidentTpExpert {
1152    gate: ResidentColumnParallel,
1153    up: ResidentColumnParallel,
1154    down: ResidentRowParallel,
1155    input_width: usize,
1156    expert_width: usize,
1157}
1158
1159struct ResidentE4m3ExpertBankRank {
1160    codes: CudaSlice<u8>,
1161    scales: CudaSlice<f32>,
1162    expert_range: Range<usize>,
1163    out_features: usize,
1164    in_features: usize,
1165    code_stride: usize,
1166    scale_stride: usize,
1167    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1168    /// checkpoint's global block order exactly. Other banks remain row-major.
1169    k_blocks: Option<usize>,
1170}
1171
1172struct PackedE4m3ExpertBankRank {
1173    codes: Vec<u8>,
1174    scales: Vec<f32>,
1175    expert_range: Range<usize>,
1176    out_features: usize,
1177    in_features: usize,
1178    code_stride: usize,
1179    scale_stride: usize,
1180    k_blocks: Option<usize>,
1181}
1182
1183struct ResidentEpRank {
1184    gate: ResidentE4m3ExpertBankRank,
1185    up: ResidentE4m3ExpertBankRank,
1186    down: ResidentE4m3ExpertBankRank,
1187}
1188
1189/// Persistent expert-parallel reference.
1190///
1191/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1192/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1193/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1194/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1195pub struct ResidentExpertParallel {
1196    ranks: Vec<ResidentEpRank>,
1197    expert_count: usize,
1198    input_width: usize,
1199    expert_width: usize,
1200}
1201
1202/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1203///
1204/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1205/// outside this gate-only adapter.
1206pub struct StepGroupedFp8ProjectionOutput {
1207    pub gate: Vec<f32>,
1208    pub up: Vec<f32>,
1209    pub down: Vec<f32>,
1210}
1211
1212/// Prepared official Step grouped-FP8 projection gate.
1213///
1214/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1215/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1216pub struct PreparedStepGroupedFp8Gate {
1217    device: usize,
1218    gate: ResidentE4m3ExpertBankRank,
1219    up: ResidentE4m3ExpertBankRank,
1220    down: ResidentE4m3ExpertBankRank,
1221    input: CudaSlice<f32>,
1222    route_csr: DeviceExpertCsr,
1223    down_csr: DeviceExpertCsr,
1224    gate_workspace: Fp8GroupedWorkspace,
1225    up_workspace: Fp8GroupedWorkspace,
1226    down_workspace: Fp8GroupedWorkspace,
1227    activation: CudaSlice<f32>,
1228    activation_limit: Option<f32>,
1229    tokens: usize,
1230    pairs: usize,
1231}
1232
1233impl PreparedStepGroupedFp8Gate {
1234    pub fn tokens(&self) -> usize {
1235        self.tokens
1236    }
1237
1238    pub fn pairs(&self) -> usize {
1239        self.pairs
1240    }
1241}
1242
1243struct PreparedStepGroupedExpertOwner {
1244    rank: usize,
1245    global_pairs: Vec<usize>,
1246    route_csr: DeviceExpertCsr,
1247    down_csr: DeviceExpertCsr,
1248    gate_workspace: Fp8GroupedWorkspace,
1249    up_workspace: Fp8GroupedWorkspace,
1250    down_workspace: Fp8GroupedWorkspace,
1251    activation: CudaSlice<f32>,
1252}
1253
1254struct StepGroupedExpertOwnerSchedule {
1255    global_pairs: Vec<usize>,
1256    route_csr: ExpertCsr,
1257    down_csr: ExpertCsr,
1258}
1259
1260/// Prepared official Step expert-owner grouped-FP8 projection gate.
1261///
1262/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1263/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1264/// after every owner has completed its rank-local program.
1265pub struct PreparedStepGroupedExpertParallelGate {
1266    rank_inputs: Vec<CudaSlice<f32>>,
1267    owners: Vec<PreparedStepGroupedExpertOwner>,
1268    activation_limit: Option<f32>,
1269    tokens: usize,
1270    pairs: usize,
1271    max_tokens: usize,
1272    max_pairs: usize,
1273    input_width: usize,
1274    expert_width: usize,
1275    generation: u64,
1276    executed_generation: Option<u64>,
1277    ready: bool,
1278}
1279
1280impl PreparedStepGroupedExpertParallelGate {
1281    pub fn tokens(&self) -> usize {
1282        self.tokens
1283    }
1284
1285    pub fn pairs(&self) -> usize {
1286        self.pairs
1287    }
1288
1289    pub fn max_tokens(&self) -> usize {
1290        self.max_tokens
1291    }
1292
1293    pub fn input_width(&self) -> usize {
1294        self.input_width
1295    }
1296
1297    pub fn expert_width(&self) -> usize {
1298        self.expert_width
1299    }
1300
1301    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1302        validate_step_expert_activation_limit(limit)?;
1303        self.activation_limit = limit;
1304        self.executed_generation = None;
1305        Ok(())
1306    }
1307
1308    pub fn active_owners(&self) -> usize {
1309        self.owners
1310            .iter()
1311            .filter(|owner| !owner.global_pairs.is_empty())
1312            .count()
1313    }
1314
1315    pub fn owner_pair_counts(&self) -> Vec<usize> {
1316        self.owners
1317            .iter()
1318            .map(|owner| owner.global_pairs.len())
1319            .collect()
1320    }
1321
1322    pub fn generation(&self) -> u64 {
1323        self.generation
1324    }
1325}
1326
1327struct PreparedPeerWeightedRouteOwner {
1328    token_rows: CudaSlice<i32>,
1329    slots: CudaSlice<i32>,
1330    weights: CudaSlice<f32>,
1331    active_pairs: usize,
1332}
1333
1334/// Persistent root-side weighted combine for peer-owned canonical route rows.
1335///
1336/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1337/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1338/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1339pub struct PreparedPeerWeightedRouteCombine {
1340    root_device: usize,
1341    owners: Vec<PreparedPeerWeightedRouteOwner>,
1342    peer_staging: CudaSlice<f32>,
1343    slots: CudaSlice<f32>,
1344    weights: CudaSlice<f32>,
1345    output: CudaSlice<f32>,
1346    peer_devices: Vec<usize>,
1347    peer_outputs: Vec<CudaSlice<f32>>,
1348    width: usize,
1349    experts_per_token: usize,
1350    max_tokens: usize,
1351    max_pairs: usize,
1352    tokens: usize,
1353    pairs: usize,
1354    projection_generation: u64,
1355    output_generation: Option<u64>,
1356    broadcast_generation: Option<u64>,
1357    ready: bool,
1358}
1359
1360impl PreparedPeerWeightedRouteCombine {
1361    pub fn tokens(&self) -> usize {
1362        self.tokens
1363    }
1364
1365    pub fn pairs(&self) -> usize {
1366        self.pairs
1367    }
1368
1369    pub fn owner_pair_counts(&self) -> Vec<usize> {
1370        self.owners.iter().map(|owner| owner.active_pairs).collect()
1371    }
1372
1373    pub fn distributed_ranks(&self) -> usize {
1374        1 + self.peer_outputs.len()
1375    }
1376}
1377
1378struct ResidentTpExpertBank {
1379    gate: Vec<ResidentE4m3ExpertBankRank>,
1380    up: Vec<ResidentE4m3ExpertBankRank>,
1381    down: Vec<ResidentE4m3ExpertBankRank>,
1382    expert_count: usize,
1383    input_width: usize,
1384    expert_width: usize,
1385}
1386
1387/// Persistent tensor-parallel expert bank.
1388///
1389/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1390/// input-column shard of every down projection. Activations cross deterministic host-staged
1391/// collectives on hosts where native peer copies are unavailable or corrupt.
1392pub struct ResidentTensorParallel {
1393    bank: ResidentTpExpertBank,
1394}
1395
1396/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1397///
1398/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1399/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1400/// repeated performance evidence qualify it.
1401pub struct TpE4m3HostBounce {
1402    devices: Vec<usize>,
1403    ranks: Vec<Engine>,
1404    native_p2p: bool,
1405    ep_device_arithmetic: bool,
1406    bulk_p2p: bool,
1407    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1408    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1409    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1410}
1411
1412/// Persistent workspace of the v2 rank-local decode-attention driver.
1413///
1414/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1415/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1416/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1417/// before its consumers run in the same call; nothing carries state between tokens.
1418/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1419/// fused kernels read (F32 mirror or raw checkpoint bf16).
1420pub enum StepTpGateShards<'a> {
1421    F32(&'a [crate::CudaSlice<f32>]),
1422    Bf16(&'a [crate::CudaSlice<u8>]),
1423}
1424
1425pub struct StepTpDecodeV2Ws {
1426    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1427    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1428    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1429    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1430    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1431    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1432    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1433    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1434    pub(crate) tcol_cap: usize,
1435    /// MEMRA_STEP_TP_W8 activation scratch: per-rank q8_1 quantized attention input
1436    /// ([in_f] i8 + one f32 scale pair per 32). Persistent because the alternative is an
1437    /// allocation per rank per layer per token.
1438    w8_aq: Vec<CudaSlice<i8>>,
1439    w8_ad: Vec<CudaSlice<f32>>,
1440    w8_in: usize,
1441    /// o_proj-side twin of the same scratch (its activation is the gated attention output,
1442    /// a different vector from the QKV input, so it needs its own buffers).
1443    w8o_aq: Vec<CudaSlice<i8>>,
1444    w8o_ad: Vec<CudaSlice<f32>>,
1445    w8o_in: usize,
1446    /// VERIFY-WALK q8_1 activation scratch, t columns wide (the decode scratch above is one
1447    /// row). Two sets because the QKV input and the gated attention output are different
1448    /// vectors of different widths.
1449    w8t_aq: Vec<CudaSlice<i8>>,
1450    w8t_ad: Vec<CudaSlice<f32>>,
1451    w8t_in: usize,
1452    w8t_oaq: Vec<CudaSlice<i8>>,
1453    w8t_oad: Vec<CudaSlice<f32>>,
1454    w8t_oin: usize,
1455    w8t_cap: usize,
1456    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1457    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1458    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1459    /// MEMRA_SPEC_FA2 slabs: per-rank stashed post-rope q rows ([2, local_q_dim]), gate
1460    /// rows ([2, heads/ranks]) and the two gated outputs the per-row combine writes
1461    /// ([2, local_q_dim]). Armed lazily by the first stash.
1462    pub(crate) fa2_q: Vec<CudaSlice<f32>>,
1463    pub(crate) fa2_gate: Vec<CudaSlice<f32>>,
1464    pub(crate) fa2_gated: Vec<CudaSlice<f32>>,
1465    pub(crate) fa2_cap: usize,
1466    /// T-ROW rope/append twin scratch: per-rank roped-k rows ([8, local_kv]), per-row
1467    /// last-block counters ([8]) and the per-tick position slab ([8]). Armed with the
1468    /// fa2 slabs.
1469    rope_k_t: Vec<CudaSlice<f32>>,
1470    rope_ctr_t: Vec<CudaSlice<u32>>,
1471    rope_pos_t: Vec<CudaSlice<i32>>,
1472    /// Per-rank combined 6-word row tables, keyed by the caller's (layer, session-set,
1473    /// base-arming) signature. LEGACY: only the `MEMRA_ROWS_TAB_RESTAGE=0` rollback arm
1474    /// reads this. See `rows_tab_t` for why the key cannot be made safe.
1475    rows_tabs: Vec<std::collections::HashMap<u64, CudaSlice<u64>>>,
1476    /// Per-rank PERSISTENT 6-word row-table slab ([32, 6] u64), RESTAGED from the live
1477    /// distributed cache before every launch. Replaces the `rows_tabs` memo, whose key was
1478    /// a hash of (k pointer, base pointer, layer, t) while the table it returned also
1479    /// carried the V and LEN pointers: a session whose K buffer address was recycled hit
1480    /// another session's table and the append kernel wrote its K/V through the FREED
1481    /// pointers the entry still held. Same defect and same cure as the row-table twin in
1482    /// `step35_verify_fa_rows_join` (8c8397e0b2, Hermes `11339f5cd3c132a3`), which this
1483    /// path was left out of. One 32-word htod per rank per layer replaces the map lookup;
1484    /// no allocation, and the staging is stream-ordered exactly like `rope_pos_t`.
1485    rows_tab_t: Vec<CudaSlice<u64>>,
1486    /// HOST shadow of the last table staged under each retired memo key, used ONLY by
1487    /// `MEMRA_ROWS_TAB_STALE_SCAN=1` to prove that the retired key would have handed a live
1488    /// launch another allocation's pointers. Never read by a kernel.
1489    rows_tab_shadow: Vec<std::collections::HashMap<u64, Vec<u64>>>,
1490    tcol_gated: Vec<CudaSlice<f32>>,
1491    tcol_opart: Vec<CudaSlice<f32>>,
1492    tcol_opeer: Option<CudaSlice<f32>>,
1493    tcol_omix: Option<CudaSlice<f32>>,
1494    tcol_ocap: usize,
1495    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1496    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1497    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1498    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1499    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1500    pub(crate) q: Vec<CudaSlice<f32>>,
1501    pub(crate) k: Vec<CudaSlice<f32>>,
1502    pub(crate) pos: Vec<CudaSlice<i32>>,
1503    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1504    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1505    pub(crate) gate: Vec<CudaSlice<f32>>,
1506    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1507    pub(crate) gated: Vec<CudaSlice<f32>>,
1508    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1509    o_partials: Vec<Vec<CudaSlice<f32>>>,
1510    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1511    ev_rank: Vec<CudaEvent>,
1512    // root-context buffers
1513    peer_partial: CudaSlice<f32>,
1514    reduce_a: CudaSlice<f32>,
1515    reduce_b: CudaSlice<f32>,
1516    /// Never written; the canonical zero start of the v1 add chain.
1517    zeros: CudaSlice<f32>,
1518    pub(crate) k_shadow: CudaSlice<f32>,
1519    pub(crate) v_shadow: CudaSlice<f32>,
1520    ev_refresh: CudaEvent,
1521    ev_oproj: CudaEvent,
1522    // model-engine (e) context
1523    gate_e: CudaSlice<f32>,
1524    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1525    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1526    pub(crate) h_stage: Option<CudaSlice<f32>>,
1527    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1528    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1529    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1530    /// captured/raw address it uses must be layer-invariant).
1531    attn_in: Vec<CudaSlice<f32>>,
1532    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1533    raw_h_stage: u64,
1534    raw_pos_stage: u64,
1535    raw_attn_in: Vec<u64>,
1536    raw_pos: Vec<u64>,
1537    raw_o_partial1: u64,
1538    raw_peer_partial: u64,
1539    raw_k1: u64,
1540    raw_v1: u64,
1541    raw_k_shadow: u64,
1542    raw_v_shadow: u64,
1543    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1544    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1545    /// children read same-context memory (cross-context kernel args are capture-illegal).
1546    raw_mixed_stage_e: u64,
1547    raw_reduce_a: u64,
1548    raw_shadow_stage_e: (u64, u64),
1549    ev_entry: CudaEvent,
1550    e_device: usize,
1551    // geometry pins
1552    local_q_dim: usize,
1553    local_kv_dim: usize,
1554    heads: usize,
1555    pub(crate) o_out: usize,
1556    o_block_cols: usize,
1557    blocks_per_rank: usize,
1558}
1559
1560impl TpE4m3HostBounce {
1561    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1562        Self::new_inner(devices, false, false, false, false)
1563    }
1564
1565    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1566        Self::new_inner(devices, false, true, false, false)
1567    }
1568
1569    pub fn new_native_p2p_device_arithmetic(
1570        devices: &[usize],
1571    ) -> Result<Self, Box<dyn std::error::Error>> {
1572        Self::new_inner(devices, false, true, true, false)
1573    }
1574
1575    pub(crate) fn new_configured(
1576        devices: &[usize],
1577        native_p2p: bool,
1578        ep_device_arithmetic: bool,
1579        bulk_p2p: bool,
1580    ) -> Result<Self, Box<dyn std::error::Error>> {
1581        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1582    }
1583
1584    /// Single-rank execution of the canonical checkpoint-block TP program.
1585    ///
1586    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1587    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1588    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1589        Self::new_inner(&[device], true, false, false, false)
1590    }
1591
1592    fn new_inner(
1593        devices: &[usize],
1594        allow_single_rank: bool,
1595        native_p2p: bool,
1596        ep_device_arithmetic: bool,
1597        bulk_p2p: bool,
1598    ) -> Result<Self, Box<dyn std::error::Error>> {
1599        if ep_device_arithmetic && !native_p2p {
1600            return Err("device-resident EP arithmetic requires native P2P".into());
1601        }
1602        if bulk_p2p && !native_p2p {
1603            return Err("bulk TP transport requires native P2P".into());
1604        }
1605        let minimum = if allow_single_rank { 1 } else { 2 };
1606        if !(minimum..=8).contains(&devices.len()) {
1607            return Err(format!(
1608                "TP reference requires {minimum}..=8 devices, got {}",
1609                devices.len()
1610            )
1611            .into());
1612        }
1613        let mut unique = devices.to_vec();
1614        unique.sort_unstable();
1615        unique.dedup();
1616        if unique.len() != devices.len() {
1617            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1618        }
1619        let ranks = devices
1620            .iter()
1621            .map(|&device| Engine::new(device))
1622            .collect::<Result<Vec<_>, _>>()?;
1623        if native_p2p {
1624            configure_native_p2p(&ranks, devices)?;
1625        }
1626        if allow_single_rank {
1627            eprintln!(
1628                "[tp] canonical oracle transport=local device={} performance_claim=false",
1629                devices[0]
1630            );
1631        } else if native_p2p {
1632            if ep_device_arithmetic {
1633                eprintln!(
1634                    "[tp] correctness transport=native-p2p devices={devices:?} \
1635                     native_p2p=true activation=device-host-exact \
1636                     accumulation=device-host-exact output=root-readback \
1637                     bulk_p2p={bulk_p2p} performance_claim=false"
1638                );
1639            } else {
1640                eprintln!(
1641                    "[tp] correctness transport=native-p2p devices={devices:?} \
1642                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1643                     performance_claim=false"
1644                );
1645            }
1646        } else {
1647            eprintln!(
1648                "[tp] correctness transport=host-bounce devices={devices:?} \
1649                 native_p2p=false performance_claim=false"
1650            );
1651        }
1652        Ok(Self {
1653            devices: devices.to_vec(),
1654            ranks,
1655            native_p2p,
1656            ep_device_arithmetic,
1657            bulk_p2p,
1658            decode_v2: std::sync::Mutex::new(Vec::new()),
1659        })
1660    }
1661
1662    pub fn devices(&self) -> &[usize] {
1663        &self.devices
1664    }
1665
1666    pub fn native_p2p(&self) -> bool {
1667        self.native_p2p
1668    }
1669
1670    pub fn bulk_p2p(&self) -> bool {
1671        self.bulk_p2p
1672    }
1673
1674    pub fn expert_activation_label(&self) -> &'static str {
1675        if self.ep_device_arithmetic {
1676            "device-host-exact"
1677        } else {
1678            "host-canonical"
1679        }
1680    }
1681
1682    pub fn expert_accumulation_label(&self) -> &'static str {
1683        self.expert_activation_label()
1684    }
1685
1686    pub fn expert_output_label(&self) -> &'static str {
1687        if self.ep_device_arithmetic {
1688            "root-readback"
1689        } else {
1690            "host-accumulated"
1691        }
1692    }
1693
1694    pub fn transport_label(&self) -> &'static str {
1695        if self.devices.len() == 1 {
1696            "local"
1697        } else if self.native_p2p {
1698            "native-p2p"
1699        } else {
1700            "host-bounce"
1701        }
1702    }
1703
1704    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1705        self.ranks
1706            .iter()
1707            .map(|rank| rank.ctx().name().map_err(Into::into))
1708            .collect()
1709    }
1710
1711    /// Correctness-gate access to the engine that owns one TP rank.
1712    ///
1713    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1714    /// focused gates can prove that the rank-local projection outputs remain device-resident
1715    /// through the next ownership boundary before that boundary is wired into serving.
1716    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1717        self.ranks.get(rank)
1718    }
1719
1720    pub fn allocate_tp_kv_cache(
1721        &self,
1722        kv_dim_k: usize,
1723        kv_dim_v: usize,
1724        capacity: usize,
1725    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1726        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1727    }
1728
1729    pub fn allocate_tp_swa_kv_cache(
1730        &self,
1731        kv_dim_k: usize,
1732        kv_dim_v: usize,
1733        capacity: usize,
1734        window: usize,
1735    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1736        if window == 0 {
1737            return Err("TP SWA KV window must be nonzero".into());
1738        }
1739        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1740    }
1741
1742    fn allocate_tp_kv_cache_inner(
1743        &self,
1744        kv_dim_k: usize,
1745        kv_dim_v: usize,
1746        capacity: usize,
1747        window: Option<usize>,
1748    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1749        if capacity == 0 || capacity > i32::MAX as usize {
1750            return Err(
1751                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1752            );
1753        }
1754        let tp = self.ranks.len();
1755        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1756        let physical_rows = window
1757            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1758            .unwrap_or(capacity);
1759        let k_plane_bytes = physical_rows
1760            .checked_mul(shape.k_token_bytes)
1761            .and_then(|bytes| bytes.checked_add(8))
1762            .ok_or("TP KV K plane-byte overflow")?;
1763        let v_plane_bytes = physical_rows
1764            .checked_mul(shape.v_token_bytes)
1765            .and_then(|bytes| bytes.checked_add(8))
1766            .ok_or("TP KV V plane-byte overflow")?;
1767        let mut ranks = Vec::with_capacity(tp);
1768        for engine in &self.ranks {
1769            let _main = engine.gpu.enter_main()?;
1770            ranks.push(ResidentTpKvCacheRank::new(
1771                engine.alloc_u8(k_plane_bytes)?,
1772                engine.alloc_u8(v_plane_bytes)?,
1773                engine.htod_i32(&[0])?,
1774            ));
1775        }
1776        Ok(match window {
1777            Some(window) => ResidentTpKvCache::new_swa(
1778                ranks,
1779                shape.kv_dim_k,
1780                shape.kv_dim_v,
1781                shape.k_token_bytes,
1782                shape.v_token_bytes,
1783                capacity,
1784                window,
1785            ),
1786            None => ResidentTpKvCache::new(
1787                ranks,
1788                shape.kv_dim_k,
1789                shape.kv_dim_v,
1790                shape.k_token_bytes,
1791                shape.v_token_bytes,
1792                capacity,
1793            ),
1794        })
1795    }
1796
1797    pub fn grow_tp_kv_cache(
1798        &self,
1799        source: &ResidentTpKvCache,
1800        target_capacity: usize,
1801        rows: usize,
1802    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1803        self.validate_tp_kv_cache(source)?;
1804        let plan = source.prepare_grow(target_capacity, rows)?;
1805        let ranks = self.ranks.len();
1806        let global_k = source
1807            .kv_dim_k()
1808            .checked_mul(ranks)
1809            .ok_or("TP KV grow global K dimension overflow")?;
1810        let global_v = source
1811            .kv_dim_v()
1812            .checked_mul(ranks)
1813            .ok_or("TP KV grow global V dimension overflow")?;
1814        let mut target = match source.ring_window() {
1815            Some(window) => {
1816                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1817            }
1818            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1819        };
1820        self.validate_tp_kv_cache(&target)?;
1821
1822        for (rank, engine) in self.ranks.iter().enumerate() {
1823            let _main = engine.gpu.enter_main()?;
1824            let src = source
1825                .rank(rank)
1826                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1827            let dst = target
1828                .rank_mut(rank)
1829                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1830            if plan.k_bytes() > 0 {
1831                engine.copy_u8_range_into(
1832                    dst.k_mut(),
1833                    0,
1834                    src.k(),
1835                    plan.source_row() * source.k_tok_bytes(),
1836                    plan.k_bytes(),
1837                )?;
1838            }
1839            if plan.v_bytes() > 0 {
1840                engine.copy_u8_range_into(
1841                    dst.v_mut(),
1842                    0,
1843                    src.v(),
1844                    plan.source_row() * source.v_tok_bytes(),
1845                    plan.v_bytes(),
1846                )?;
1847            }
1848        }
1849        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1850
1851        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1852        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1853        for engine in &self.ranks {
1854            let _main = engine.gpu.enter_main()?;
1855            engine.stream().synchronize()?;
1856        }
1857        let physical_copy_rows = plan.copy_rows();
1858        target.publish_grow(plan)?;
1859        eprintln!(
1860            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1861             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1862             rank_streams_synchronized=true generation_preserved=true",
1863            rows,
1864            source.capacity(),
1865            target_capacity,
1866            ranks,
1867            physical_copy_rows,
1868            source.ring_window(),
1869        );
1870        Ok(target)
1871    }
1872
1873    pub fn hydrate_tp_kv_cache(
1874        &self,
1875        cache: &mut ResidentTpKvCache,
1876        rows: usize,
1877        k_rows: &[u8],
1878        v_rows: &[u8],
1879    ) -> Result<(), Box<dyn std::error::Error>> {
1880        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1881    }
1882
1883    pub fn hydrate_tp_kv_cache_from(
1884        &self,
1885        cache: &mut ResidentTpKvCache,
1886        logical_len: usize,
1887        resident_start: usize,
1888        k_rows: &[u8],
1889        v_rows: &[u8],
1890    ) -> Result<(), Box<dyn std::error::Error>> {
1891        self.validate_tp_kv_cache(cache)?;
1892        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1893            return Err(format!(
1894                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1895                cache.committed_len(),
1896                cache.staged_len()
1897            )
1898            .into());
1899        }
1900        if resident_start > logical_len || logical_len > cache.capacity() {
1901            return Err(format!(
1902                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1903                cache.capacity(),
1904            )
1905            .into());
1906        }
1907        let rows = logical_len - resident_start;
1908        if rows > cache.physical_capacity() {
1909            return Err(format!(
1910                "TP KV hydration rows {rows} exceed physical capacity {}",
1911                cache.physical_capacity()
1912            )
1913            .into());
1914        }
1915        for rank in 0..self.ranks.len() {
1916            let k_rank =
1917                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1918            let v_rank =
1919                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1920            let engine = &self.ranks[rank];
1921            let _main = engine.gpu.enter_main()?;
1922            let rank_cache = cache
1923                .rank_mut(rank)
1924                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1925            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1926            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1927        }
1928        cache.publish_hydration(logical_len, resident_start)?;
1929        Ok(())
1930    }
1931
1932    pub fn append_tp_kv_transaction(
1933        &self,
1934        cache: &mut ResidentTpKvCache,
1935        transaction: TpKvTransaction,
1936        k_shards: &[CudaSlice<f32>],
1937        v_shards: &[CudaSlice<f32>],
1938        rows: usize,
1939    ) -> Result<(), Box<dyn std::error::Error>> {
1940        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1941    }
1942
1943    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1944    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1945    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1946    /// which land the same value the in-stream inc produced).
1947    #[allow(clippy::too_many_arguments)]
1948    pub fn append_tp_kv_transaction_inner(
1949        &self,
1950        cache: &mut ResidentTpKvCache,
1951        transaction: TpKvTransaction,
1952        k_shards: &[CudaSlice<f32>],
1953        v_shards: &[CudaSlice<f32>],
1954        rows: usize,
1955        external_rank_appends: bool,
1956    ) -> Result<(), Box<dyn std::error::Error>> {
1957        self.validate_tp_kv_cache(cache)?;
1958        let plan = cache.prepare_append(transaction, rows)?;
1959        let target = plan.target();
1960        let expected_k = rows
1961            .checked_mul(cache.kv_dim_k())
1962            .ok_or("TP KV K append size overflow")?;
1963        let expected_v = rows
1964            .checked_mul(cache.kv_dim_v())
1965            .ok_or("TP KV V append size overflow")?;
1966        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1967        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1968        if !external_rank_appends
1969            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1970        {
1971            return Err(format!(
1972                "TP KV append shard counts k={} v={} != ranks {}",
1973                k_shards.len(),
1974                v_shards.len(),
1975                self.ranks.len()
1976            )
1977            .into());
1978        }
1979        let kv_dim_k = cache.kv_dim_k();
1980        let kv_dim_v = cache.kv_dim_v();
1981        let k_tok_bytes = cache.k_tok_bytes();
1982        let v_tok_bytes = cache.v_tok_bytes();
1983        if let Some(KvRingAppend::Rebase {
1984            src_row,
1985            keep_rows,
1986            new_base,
1987            ..
1988        }) = plan.ring_append()
1989        {
1990            for rank in 0..self.ranks.len() {
1991                let engine = &self.ranks[rank];
1992                let _main = engine.gpu.enter_main()?;
1993                let rank_cache = cache
1994                    .rank_mut(rank)
1995                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1996                if keep_rows > 0 {
1997                    let k_len = keep_rows
1998                        .checked_mul(k_tok_bytes)
1999                        .ok_or("TP KV K rebase-byte overflow")?;
2000                    let v_len = keep_rows
2001                        .checked_mul(v_tok_bytes)
2002                        .ok_or("TP KV V rebase-byte overflow")?;
2003                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
2004                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
2005                    engine.copy_u8_range_into(
2006                        &mut k_tmp,
2007                        0,
2008                        rank_cache.k(),
2009                        src_row * k_tok_bytes,
2010                        k_len,
2011                    )?;
2012                    engine.copy_u8_range_into(
2013                        &mut v_tmp,
2014                        0,
2015                        rank_cache.v(),
2016                        src_row * v_tok_bytes,
2017                        v_len,
2018                    )?;
2019                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
2020                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
2021                }
2022                // dcw base mirror (graph increment A): physical row 0 now holds logical
2023                // row `new_base`; armed device mirrors track it (rebases are rare host
2024                // events, so a host set here is the whole maintenance cost).
2025                if rank_cache.base_d().is_some() {
2026                    let value = new_base as i32;
2027                    let rank_cache = cache
2028                        .rank_mut(rank)
2029                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2030                    if let Some(base_d) = rank_cache.base_d_mut() {
2031                        engine.set_i32_one(base_d, value)?;
2032                    }
2033                }
2034            }
2035        }
2036        cache.publish_append_rebase(plan)?;
2037        let write_row = plan.write_row();
2038        for rank in 0..self.ranks.len() {
2039            if external_rank_appends {
2040                break;
2041            }
2042            let engine = &self.ranks[rank];
2043            let _main = engine.gpu.enter_main()?;
2044            if k_shards[rank].len() != expected_k
2045                || v_shards[rank].len() != expected_v
2046                || k_shards[rank].ordinal() != engine.ctx().ordinal()
2047                || v_shards[rank].ordinal() != engine.ctx().ordinal()
2048            {
2049                return Err(format!(
2050                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
2051                     != expected {expected_k}/{expected_v} on device {}",
2052                    k_shards[rank].len(),
2053                    k_shards[rank].ordinal(),
2054                    v_shards[rank].len(),
2055                    v_shards[rank].ordinal(),
2056                    engine.ctx().ordinal(),
2057                )
2058                .into());
2059            }
2060            let rank_cache = cache
2061                .rank_mut(rank)
2062                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
2063            let (rank_k, rank_v) = rank_cache.planes_mut();
2064            engine.append_kv_quantized_rows(
2065                &k_shards[rank],
2066                &v_shards[rank],
2067                rank_k,
2068                rank_v,
2069                write_row,
2070                rows,
2071                kv_dim_k,
2072                kv_dim_v,
2073                k_tok_bytes,
2074                v_tok_bytes,
2075                Engine::kv_fp8_on(),
2076            )?;
2077        }
2078        if !external_rank_appends {
2079            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
2080            // here would race the merged per-rank append (it reads len_d for its write row).
2081            self.set_tp_kv_len_mirrors(cache, target)?;
2082        }
2083        cache.publish_append_plan(plan)?;
2084        Ok(())
2085    }
2086
2087    pub fn commit_tp_kv_transaction(
2088        &self,
2089        cache: &mut ResidentTpKvCache,
2090        transaction: TpKvTransaction,
2091        accepted_rows: usize,
2092    ) -> Result<(), Box<dyn std::error::Error>> {
2093        self.validate_tp_kv_cache(cache)?;
2094        let target = cache.commit_target(transaction, accepted_rows)?;
2095        self.set_tp_kv_len_mirrors(cache, target)?;
2096        cache.publish_finalize(transaction, target)?;
2097        Ok(())
2098    }
2099
2100    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
2101    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
2102    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
2103    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
2104    /// counter backward mid-token.
2105    pub fn commit_tp_kv_transaction_external(
2106        &self,
2107        cache: &mut ResidentTpKvCache,
2108        transaction: TpKvTransaction,
2109        accepted_rows: usize,
2110    ) -> Result<(), Box<dyn std::error::Error>> {
2111        self.validate_tp_kv_cache(cache)?;
2112        let target = cache.commit_target(transaction, accepted_rows)?;
2113        cache.publish_finalize(transaction, target)?;
2114        Ok(())
2115    }
2116
2117    pub fn rollback_tp_kv_transaction(
2118        &self,
2119        cache: &mut ResidentTpKvCache,
2120        transaction: TpKvTransaction,
2121    ) -> Result<(), Box<dyn std::error::Error>> {
2122        self.validate_tp_kv_cache(cache)?;
2123        cache.validate_transaction(transaction)?;
2124        let target = transaction.base_len();
2125        self.set_tp_kv_len_mirrors(cache, target)?;
2126        cache.publish_finalize(transaction, target)?;
2127        Ok(())
2128    }
2129
2130    pub fn tp_kv_device_lengths(
2131        &self,
2132        cache: &ResidentTpKvCache,
2133    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
2134        self.validate_tp_kv_cache(cache)?;
2135        let mut lengths = Vec::with_capacity(self.ranks.len());
2136        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
2137            let _main = engine.gpu.enter_main()?;
2138            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
2139        }
2140        Ok(lengths)
2141    }
2142
2143    fn set_tp_kv_len_mirrors(
2144        &self,
2145        cache: &mut ResidentTpKvCache,
2146        len: usize,
2147    ) -> Result<(), Box<dyn std::error::Error>> {
2148        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
2149        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
2150            let _main = engine.gpu.enter_main()?;
2151            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
2152        }
2153        Ok(())
2154    }
2155
2156    fn validate_tp_kv_cache(
2157        &self,
2158        cache: &ResidentTpKvCache,
2159    ) -> Result<(), Box<dyn std::error::Error>> {
2160        if cache.ranks_len() != self.ranks.len() {
2161            return Err(format!(
2162                "TP KV cache ranks {} != runtime ranks {}",
2163                cache.ranks_len(),
2164                self.ranks.len()
2165            )
2166            .into());
2167        }
2168        let expected_k = cache
2169            .physical_capacity()
2170            .checked_mul(cache.k_tok_bytes())
2171            .and_then(|bytes| bytes.checked_add(8))
2172            .ok_or("TP KV K plane validation overflow")?;
2173        let expected_v = cache
2174            .physical_capacity()
2175            .checked_mul(cache.v_tok_bytes())
2176            .and_then(|bytes| bytes.checked_add(8))
2177            .ok_or("TP KV V plane validation overflow")?;
2178        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
2179            let device = engine.ctx().ordinal();
2180            if rank_cache.k().len() != expected_k
2181                || rank_cache.v().len() != expected_v
2182                || rank_cache.len_d().len() != 1
2183                || rank_cache.k().ordinal() != device
2184                || rank_cache.v().ordinal() != device
2185                || rank_cache.len_d().ordinal() != device
2186            {
2187                return Err(format!(
2188                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
2189                )
2190                .into());
2191            }
2192        }
2193        Ok(())
2194    }
2195
2196    pub fn full(
2197        &self,
2198        matrix: E4m3BlockMatrix<'_>,
2199        activations: &[f32],
2200        tokens: usize,
2201    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2202        matrix.validate()?;
2203        validate_activations(activations, tokens, matrix.in_features)?;
2204        run_rank(&self.ranks[0], matrix, activations, tokens)
2205    }
2206
2207    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2208    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2209    /// output is host-gathered in rank order.
2210    pub fn column_parallel(
2211        &self,
2212        matrix: E4m3BlockMatrix<'_>,
2213        activations: &[f32],
2214        tokens: usize,
2215    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2216        matrix.validate()?;
2217        validate_activations(activations, tokens, matrix.in_features)?;
2218        let tp = self.ranks.len();
2219        if matrix.out_features % tp != 0 {
2220            return Err(format!(
2221                "column-parallel out_features {} is not divisible by TP={tp}",
2222                matrix.out_features
2223            )
2224            .into());
2225        }
2226        let local_out = matrix.out_features / tp;
2227        if local_out % FP8_BLOCK != 0 {
2228            return Err(format!(
2229                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2230                 E4M3 scale block"
2231            )
2232            .into());
2233        }
2234
2235        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2236        let mut rank_outputs = Vec::with_capacity(tp);
2237        for (rank_index, rank) in self.ranks.iter().enumerate() {
2238            let shard = column_shard(matrix, tp, rank_index)?;
2239            let output = run_rank(rank, shard, activations, tokens)?;
2240            let row_start = rank_index * local_out;
2241            for token in 0..tokens {
2242                gathered[token * matrix.out_features + row_start
2243                    ..token * matrix.out_features + row_start + local_out]
2244                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2245            }
2246            rank_outputs.push(output);
2247        }
2248        Ok(ColumnParallelResult {
2249            gathered,
2250            rank_outputs,
2251        })
2252    }
2253
2254    pub fn upload_column_parallel(
2255        &self,
2256        matrix: E4m3BlockMatrix<'_>,
2257    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2258        matrix.validate()?;
2259        let tp = self.ranks.len();
2260        validate_column_shape(matrix, tp)?;
2261        let mut ranks = Vec::with_capacity(tp);
2262        for (rank_index, engine) in self.ranks.iter().enumerate() {
2263            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2264        }
2265        Ok(ResidentColumnParallel {
2266            ranks,
2267            out_features: matrix.out_features,
2268            in_features: matrix.in_features,
2269        })
2270    }
2271
2272    pub fn column_parallel_resident(
2273        &self,
2274        matrix: &ResidentColumnParallel,
2275        activations: &[f32],
2276        tokens: usize,
2277    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2278        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2279        validate_activations(activations, tokens, matrix.in_features)?;
2280        let local_out = matrix.out_features / self.ranks.len();
2281        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2282        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2283        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2284            let output = run_resident_rank(engine, shard, activations, tokens)?;
2285            let row_start = rank_index * local_out;
2286            for token in 0..tokens {
2287                gathered[token * matrix.out_features + row_start
2288                    ..token * matrix.out_features + row_start + local_out]
2289                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2290            }
2291            rank_outputs.push(output);
2292        }
2293        Ok(ColumnParallelResult {
2294            gathered,
2295            rank_outputs,
2296        })
2297    }
2298
2299    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2300    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2301    /// rank order.
2302    pub fn row_parallel(
2303        &self,
2304        matrix: E4m3BlockMatrix<'_>,
2305        activations: &[f32],
2306        tokens: usize,
2307    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2308        matrix.validate()?;
2309        validate_activations(activations, tokens, matrix.in_features)?;
2310        let tp = self.ranks.len();
2311        if matrix.in_features % tp != 0 {
2312            return Err(format!(
2313                "row-parallel in_features {} is not divisible by TP={tp}",
2314                matrix.in_features
2315            )
2316            .into());
2317        }
2318        let local_in = matrix.in_features / tp;
2319        if local_in % FP8_BLOCK != 0 {
2320            return Err(format!(
2321                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2322                 E4M3 scale block"
2323            )
2324            .into());
2325        }
2326
2327        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2328        let mut rank_partials = Vec::with_capacity(tp);
2329        for (rank_index, rank) in self.ranks.iter().enumerate() {
2330            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2331            let local_activations =
2332                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2333            let shard = E4m3BlockMatrix {
2334                codes: &codes,
2335                scales: &scales,
2336                out_features: matrix.out_features,
2337                in_features: local_in,
2338            };
2339            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2340            for (sum, value) in reduced.iter_mut().zip(&partial) {
2341                *sum += *value;
2342            }
2343            rank_partials.push(partial);
2344        }
2345        Ok(RowParallelResult {
2346            reduced,
2347            rank_partials,
2348        })
2349    }
2350
2351    pub fn upload_row_parallel(
2352        &self,
2353        matrix: E4m3BlockMatrix<'_>,
2354    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2355        matrix.validate()?;
2356        let tp = self.ranks.len();
2357        validate_row_shape(matrix, tp)?;
2358        let local_in = matrix.in_features / tp;
2359        let mut ranks = Vec::with_capacity(tp);
2360        for (rank_index, engine) in self.ranks.iter().enumerate() {
2361            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2362            ranks.push(upload_rank(
2363                engine,
2364                E4m3BlockMatrix {
2365                    codes: &codes,
2366                    scales: &scales,
2367                    out_features: matrix.out_features,
2368                    in_features: local_in,
2369                },
2370            )?);
2371        }
2372        Ok(ResidentRowParallel {
2373            ranks,
2374            out_features: matrix.out_features,
2375            in_features: matrix.in_features,
2376        })
2377    }
2378
2379    pub fn row_parallel_resident(
2380        &self,
2381        matrix: &ResidentRowParallel,
2382        activations: &[f32],
2383        tokens: usize,
2384    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2385        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2386        validate_activations(activations, tokens, matrix.in_features)?;
2387        let tp = self.ranks.len();
2388        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2389        let mut rank_partials = Vec::with_capacity(tp);
2390        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2391            let local_activations =
2392                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2393            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2394            for (sum, value) in reduced.iter_mut().zip(&partial) {
2395                *sum += *value;
2396            }
2397            rank_partials.push(partial);
2398        }
2399        Ok(RowParallelResult {
2400            reduced,
2401            rank_partials,
2402        })
2403    }
2404
2405    pub fn upload_bf16_column_parallel(
2406        &self,
2407        matrix: Bf16Matrix<'_>,
2408    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2409        self.upload_bf16_column_parallel_inner(matrix, None, false)
2410    }
2411
2412    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2413    pub fn upload_step_bf16_column_parallel(
2414        &self,
2415        matrix: Bf16Matrix<'_>,
2416    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2417        self.upload_step_bf16_column_parallel_inner(matrix, false)
2418    }
2419
2420    /// Load-time exact F32 expansion of a Step BF16 shard.
2421    ///
2422    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2423    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2424    pub fn upload_step_bf16_column_parallel_f32_mirror(
2425        &self,
2426        matrix: Bf16Matrix<'_>,
2427    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2428        self.upload_step_bf16_column_parallel_inner(matrix, true)
2429    }
2430
2431    fn upload_step_bf16_column_parallel_inner(
2432        &self,
2433        matrix: Bf16Matrix<'_>,
2434        f32_mirror: bool,
2435    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2436        let canonical_chunk_rows =
2437            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2438        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2439    }
2440
2441    fn upload_bf16_column_parallel_inner(
2442        &self,
2443        matrix: Bf16Matrix<'_>,
2444        canonical_chunk_rows: Option<usize>,
2445        f32_mirror: bool,
2446    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2447        matrix.validate()?;
2448        let tp = self.ranks.len();
2449        if matrix.out_features % tp != 0 {
2450            return Err(format!(
2451                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2452                matrix.out_features
2453            )
2454            .into());
2455        }
2456        let mut ranks = Vec::with_capacity(tp);
2457        for (rank, engine) in self.ranks.iter().enumerate() {
2458            ranks.push(upload_bf16_rank(
2459                engine,
2460                bf16_column_shard(matrix, tp, rank)?,
2461                f32_mirror,
2462            )?);
2463        }
2464        Ok(ResidentBf16ColumnParallel {
2465            ranks,
2466            out_features: matrix.out_features,
2467            in_features: matrix.in_features,
2468            canonical_chunk_rows,
2469        })
2470    }
2471
2472    pub fn bf16_column_parallel_resident(
2473        &self,
2474        matrix: &ResidentBf16ColumnParallel,
2475        activations: &[f32],
2476        tokens: usize,
2477    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2478        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2479        validate_activations(activations, tokens, matrix.in_features)?;
2480        let local_out = matrix.out_features / self.ranks.len();
2481        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2482        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2483        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2484            let output = run_resident_bf16_rank(
2485                engine,
2486                shard,
2487                activations,
2488                tokens,
2489                matrix.canonical_chunk_rows,
2490            )?;
2491            for token in 0..tokens {
2492                let src = &output[token * local_out..(token + 1) * local_out];
2493                let dst_start = token * matrix.out_features + rank * local_out;
2494                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2495            }
2496            rank_outputs.push(output);
2497        }
2498        Ok(ColumnParallelResult {
2499            gathered,
2500            rank_outputs,
2501        })
2502    }
2503
2504    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2505    ///
2506    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2507    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2508    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2509    /// attention and KV ownership are separate milestones.
2510    pub fn bf16_column_parallel_resident_native(
2511        &self,
2512        matrix: &ResidentBf16ColumnParallel,
2513        activations: &[f32],
2514        tokens: usize,
2515    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2516        let rank_outputs =
2517            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2518        let local_out = matrix.out_features / self.ranks.len();
2519        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2520    }
2521
2522    /// Does the serving engine live in the SAME CUDA context as this runtime's root rank?
2523    /// The device-resident input/output seams below hand raw device buffers across the
2524    /// Engine boundary, which is only addressable when both sides share the root device's
2525    /// primary context — the seam `step35_tp_qkv` keys its residency dispatch on.
2526    pub fn root_shares_ctx(&self, e: &Engine) -> bool {
2527        self.ranks
2528            .first()
2529            .is_some_and(|root| root.ctx().cu_ctx() == e.ctx().cu_ctx())
2530    }
2531
2532    /// Device-input twin of [`Self::bf16_column_parallel_resident_native`] (lane/
2533    /// hermes-perf-fixes, 2026-08-23 — the step QKV TP host-bounce finding). The activation
2534    /// arrives as a ROOT-DEVICE buffer (first `tokens * in_features` values) instead of a
2535    /// host slice, and the gathered output stays root-resident: no DtoH of the hidden state,
2536    /// no host q/k/v staging, no re-upload. BYTE-IDENTICAL to the host-canonical native arm
2537    /// by construction — the root input bytes are dtod-copied where the host arm htod'd the
2538    /// same bytes, and every kernel, peer copy, and gather order is shared.
2539    ///
2540    /// FENCES: caller must have synchronized the producer stream that wrote
2541    /// `root_activation` (the serving engine's — a DIFFERENT stream in the same context);
2542    /// this method synchronizes the root stream before returning so the caller's stream can
2543    /// consume the gathered output immediately.
2544    pub fn bf16_column_parallel_resident_native_device(
2545        &self,
2546        matrix: &ResidentBf16ColumnParallel,
2547        root_activation: &CudaSlice<f32>,
2548        tokens: usize,
2549    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2550        let rank_outputs = self.bf16_column_parallel_resident_device_shards_from_root(
2551            matrix,
2552            root_activation,
2553            tokens,
2554        )?;
2555        let local_out = matrix.out_features / self.ranks.len();
2556        let gathered = self.gather_native_column_shards_device(&rank_outputs, tokens, local_out)?;
2557        let root = &self.ranks[0];
2558        let _main = root.gpu.enter_main()?;
2559        root.stream().synchronize()?;
2560        Ok(gathered)
2561    }
2562
2563    /// Root-device-input twin of [`Self::bf16_column_parallel_resident_device_shards`]:
2564    /// the canonical activation is already resident on the root device (len >=
2565    /// `tokens * in_features`; extra tail values beyond the active prefix are ignored,
2566    /// the reused-prime-slab contract of `active_matrix_values`).
2567    pub fn bf16_column_parallel_resident_device_shards_from_root(
2568        &self,
2569        matrix: &ResidentBf16ColumnParallel,
2570        root_activation: &CudaSlice<f32>,
2571        tokens: usize,
2572    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2573        if self.ranks.len() > 1 && !self.native_p2p {
2574            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2575        }
2576        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2577        let values = tokens
2578            .checked_mul(matrix.in_features)
2579            .ok_or("device BF16 column activation size overflow")?;
2580        let root = &self.ranks[0];
2581        if tokens == 0
2582            || root_activation.len() < values
2583            || root_activation.ordinal() != root.ctx().ordinal()
2584        {
2585            return Err("device BF16 column root activation geometry mismatch".into());
2586        }
2587
2588        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2589        let root_input = {
2590            let _main = root.gpu.enter_main()?;
2591            let mut root_input = root.uninit(values)?;
2592            root.stream()
2593                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
2594            root_input
2595        };
2596        // PRODUCER FENCE (same discipline as the host-input twin): the peer broadcast
2597        // below reads this buffer from the OTHER ranks' streams while the root dtod may
2598        // still be in flight.
2599        {
2600            let _main = root.gpu.enter_main()?;
2601            root.stream().synchronize()?;
2602        }
2603        rank_inputs.push(root_input);
2604        for engine in &self.ranks[1..] {
2605            let peer_input = {
2606                let _main = engine.gpu.enter_main()?;
2607                let mut peer_input = engine.uninit(values)?;
2608                engine
2609                    .stream()
2610                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2611                peer_input
2612            };
2613            rank_inputs.push(peer_input);
2614        }
2615
2616        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2617        for rank in 0..self.ranks.len() {
2618            rank_outputs.push(run_resident_bf16_rank_device(
2619                &self.ranks[rank],
2620                &matrix.ranks[rank],
2621                &rank_inputs[rank],
2622                tokens,
2623                matrix.canonical_chunk_rows,
2624                self.bulk_p2p,
2625            )?);
2626        }
2627        Ok(rank_outputs)
2628    }
2629
2630    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2631    ///
2632    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2633    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2634    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2635    /// and cache ownership; callers must not treat its existence as serving qualification.
2636    pub fn bf16_column_parallel_resident_device_shards(
2637        &self,
2638        matrix: &ResidentBf16ColumnParallel,
2639        activations: &[f32],
2640        tokens: usize,
2641    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2642        if self.ranks.len() > 1 && !self.native_p2p {
2643            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2644        }
2645        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2646        validate_activations(activations, tokens, matrix.in_features)?;
2647
2648        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2649        let root_input = {
2650            let root = &self.ranks[0];
2651            let _main = root.gpu.enter_main()?;
2652            root.htod(activations)?
2653        };
2654        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2655        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2656        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2657        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2658        // `upload_replicated_device_rows`.
2659        {
2660            let root = &self.ranks[0];
2661            let _main = root.gpu.enter_main()?;
2662            root.stream().synchronize()?;
2663        }
2664        rank_inputs.push(root_input);
2665        for engine in &self.ranks[1..] {
2666            let peer_input = {
2667                let _main = engine.gpu.enter_main()?;
2668                let mut peer_input = engine.uninit(activations.len())?;
2669                engine
2670                    .stream()
2671                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2672                peer_input
2673            };
2674            rank_inputs.push(peer_input);
2675        }
2676
2677        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2678        for rank in 0..self.ranks.len() {
2679            rank_outputs.push(run_resident_bf16_rank_device(
2680                &self.ranks[rank],
2681                &matrix.ranks[rank],
2682                &rank_inputs[rank],
2683                tokens,
2684                matrix.canonical_chunk_rows,
2685                self.bulk_p2p,
2686            )?);
2687        }
2688        Ok(rank_outputs)
2689    }
2690
2691    /// Allocate one fixed-shape replicated batch without initializing its contents.
2692    ///
2693    /// Callers must refresh every rank before passing the batch to an operator.
2694    pub fn allocate_replicated_device_rows(
2695        &self,
2696        tokens: usize,
2697        width: usize,
2698    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2699        if self.ranks.len() > 1 && !self.native_p2p {
2700            return Err("replicated device rows require native P2P ranks".into());
2701        }
2702        let values = tokens
2703            .checked_mul(width)
2704            .ok_or("replicated device row size overflow")?;
2705        let rank_lengths = vec![values; self.ranks.len()];
2706        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2707        let mut ranks = Vec::with_capacity(self.ranks.len());
2708        for engine in &self.ranks {
2709            let _main = engine.gpu.enter_main()?;
2710            ranks.push(engine.uninit(values)?);
2711        }
2712        Ok(ResidentReplicatedDeviceRows {
2713            ranks,
2714            tokens,
2715            width,
2716        })
2717    }
2718
2719    /// Replace a fixed-shape replicated batch from a root-device source.
2720    pub fn refresh_replicated_device_rows_from_root(
2721        &self,
2722        rows: &mut ResidentReplicatedDeviceRows,
2723        source: &CudaSlice<f32>,
2724    ) -> Result<(), Box<dyn std::error::Error>> {
2725        if self.ranks.len() > 1 && !self.native_p2p {
2726            return Err("replicated device rows require native P2P ranks".into());
2727        }
2728        validate_replicated_device_rows(&self.ranks, rows)?;
2729        let root = self
2730            .ranks
2731            .first()
2732            .ok_or("replicated rows have no root rank")?;
2733        let values = replicated_device_row_source_values(
2734            rows.tokens,
2735            rows.width,
2736            source.len(),
2737            source.ordinal(),
2738            root.ctx().ordinal(),
2739        )?;
2740        let (root_rows, peer_rows) = rows
2741            .ranks
2742            .split_first_mut()
2743            .ok_or("replicated rows have no root allocation")?;
2744        {
2745            let _main = root.gpu.enter_main()?;
2746            let mut destination = root_rows.slice_mut(0..values);
2747            root.stream()
2748                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2749            root.stream().synchronize()?;
2750        }
2751        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2752            let _main = engine.gpu.enter_main()?;
2753            let mut destination = peer_rows.slice_mut(0..values);
2754            engine
2755                .stream()
2756                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2757        }
2758        Ok(())
2759    }
2760
2761    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2762    pub fn upload_replicated_device_rows(
2763        &self,
2764        rows: &[f32],
2765        tokens: usize,
2766        width: usize,
2767    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2768        if self.ranks.len() > 1 && !self.native_p2p {
2769            return Err("replicated device rows require native P2P ranks".into());
2770        }
2771        validate_activations(rows, tokens, width)?;
2772        let root = self
2773            .ranks
2774            .first()
2775            .ok_or("replicated rows have no root rank")?;
2776        let root_rows = {
2777            let _main = root.gpu.enter_main()?;
2778            root.htod(rows)?
2779        };
2780        {
2781            let _main = root.gpu.enter_main()?;
2782            root.stream().synchronize()?;
2783        }
2784        let mut ranks = Vec::with_capacity(self.ranks.len());
2785        ranks.push(root_rows);
2786        for engine in self.ranks.iter().skip(1) {
2787            let _main = engine.gpu.enter_main()?;
2788            let mut peer_rows = engine.uninit(rows.len())?;
2789            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2790            ranks.push(peer_rows);
2791        }
2792        Ok(ResidentReplicatedDeviceRows {
2793            ranks,
2794            tokens,
2795            width,
2796        })
2797    }
2798
2799    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2800    pub fn bf16_column_parallel_resident_replicated_device_shards(
2801        &self,
2802        matrix: &ResidentBf16ColumnParallel,
2803        activations: &ResidentReplicatedDeviceRows,
2804    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2805        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2806        validate_replicated_device_rows(&self.ranks, activations)?;
2807        if activations.width != matrix.in_features {
2808            return Err(format!(
2809                "replicated BF16 column input width {} != matrix width {}",
2810                activations.width, matrix.in_features
2811            )
2812            .into());
2813        }
2814        let mut outputs = Vec::with_capacity(self.ranks.len());
2815        for rank in 0..self.ranks.len() {
2816            outputs.push(run_resident_bf16_rank_device(
2817                &self.ranks[rank],
2818                &matrix.ranks[rank],
2819                &activations.ranks[rank],
2820                activations.tokens,
2821                matrix.canonical_chunk_rows,
2822                self.bulk_p2p,
2823            )?);
2824        }
2825        Ok(outputs)
2826    }
2827
2828    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2829    #[allow(clippy::too_many_arguments)]
2830    pub fn upload_sigmoid_topk_router(
2831        &self,
2832        weight: Bf16Matrix<'_>,
2833        correction_bias: &[f32],
2834        active: Option<&[bool]>,
2835        experts_per_token: usize,
2836        scaling_factor: f32,
2837        route_norm: bool,
2838    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2839        weight.validate()?;
2840        if correction_bias.len() != weight.out_features
2841            || experts_per_token == 0
2842            || experts_per_token > weight.out_features
2843            || !correction_bias.iter().all(|value| value.is_finite())
2844            || !scaling_factor.is_finite()
2845            || scaling_factor <= 0.0
2846        {
2847            return Err(format!(
2848                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2849                weight.out_features,
2850                weight.in_features,
2851                correction_bias.len(),
2852                experts_per_token,
2853            )
2854            .into());
2855        }
2856        let active_row = active
2857            .map(|mask| {
2858                if mask.len() != weight.out_features {
2859                    return Err(format!(
2860                        "sigmoid router active mask {} != experts {}",
2861                        mask.len(),
2862                        weight.out_features
2863                    ));
2864                }
2865                Ok(mask
2866                    .iter()
2867                    .map(|&enabled| u8::from(enabled))
2868                    .collect::<Vec<_>>())
2869            })
2870            .transpose()?
2871            .unwrap_or_else(|| vec![1; weight.out_features]);
2872        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2873        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2874
2875        let root = self
2876            .ranks
2877            .first()
2878            .ok_or("sigmoid router runtime has no root rank")?;
2879        let _main = root.gpu.enter_main()?;
2880        let bf16 = root.htod_bytes(weight.bytes)?;
2881        let weight_f32 = root.bf16_to_f32(
2882            &bf16.slice(0..bf16.len()),
2883            weight.out_features * weight.in_features,
2884        )?;
2885        Ok(ResidentSigmoidTopKRouter {
2886            weight: weight_f32,
2887            correction_bias: root.htod(correction_bias)?,
2888            active: root.htod_bytes(&active_row)?,
2889            root_device: root.ctx().ordinal(),
2890            input_width: weight.in_features,
2891            expert_count: weight.out_features,
2892            experts_per_token,
2893            active_count,
2894            scaling_factor,
2895            route_norm,
2896        })
2897    }
2898
2899    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2900    ///
2901    /// The logits readback exists for independent oracle comparison. This method is a correctness
2902    /// surface; a serving scheduler may retain logits and selected routes on device.
2903    pub fn sigmoid_topk_replicated_device_rows_host(
2904        &self,
2905        router: &ResidentSigmoidTopKRouter,
2906        input: &ResidentReplicatedDeviceRows,
2907    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2908        validate_replicated_device_rows(&self.ranks, input)?;
2909        if input.width != router.input_width {
2910            return Err(format!(
2911                "sigmoid router input width {} != resident width {}",
2912                input.width, router.input_width
2913            )
2914            .into());
2915        }
2916        let root = self
2917            .ranks
2918            .first()
2919            .ok_or("sigmoid router runtime has no root rank")?;
2920        let _main = root.gpu.enter_main()?;
2921        if root.ctx().ordinal() != router.root_device
2922            || router.weight.ordinal() != router.root_device
2923            || router.correction_bias.ordinal() != router.root_device
2924            || router.active.ordinal() != router.root_device
2925        {
2926            return Err("sigmoid router root residency changed".into());
2927        }
2928        let logits = root.router_gemv(
2929            &router.weight,
2930            &input.ranks[0],
2931            router.input_width,
2932            router.expert_count,
2933            input.tokens,
2934        )?;
2935        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2936            &logits,
2937            input.tokens,
2938            router.expert_count,
2939            router.experts_per_token,
2940            router.active_count,
2941            &router.correction_bias,
2942            &router.active,
2943            router.scaling_factor,
2944            router.route_norm,
2945        )?;
2946        Ok(SigmoidTopKHostOutput {
2947            logits: root.dtoh(&logits)?,
2948            selected,
2949            weights,
2950        })
2951    }
2952
2953    /// Replicate a full BF16 SwiGLU bank on every rank.
2954    pub fn upload_replicated_bf16_swiglu(
2955        &self,
2956        gate: Bf16Matrix<'_>,
2957        up: Bf16Matrix<'_>,
2958        down: Bf16Matrix<'_>,
2959    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2960        gate.validate()?;
2961        up.validate()?;
2962        down.validate()?;
2963        if gate.in_features != up.in_features
2964            || gate.out_features != up.out_features
2965            || down.in_features != gate.out_features
2966            || down.out_features != gate.in_features
2967        {
2968            return Err(format!(
2969                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2970                gate.out_features,
2971                gate.in_features,
2972                up.out_features,
2973                up.in_features,
2974                down.out_features,
2975                down.in_features,
2976            )
2977            .into());
2978        }
2979        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2980        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2981        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2982        for engine in &self.ranks {
2983            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2984            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2985            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2986        }
2987        Ok(ResidentReplicatedBf16SwiGlu {
2988            gate: gate_ranks,
2989            up: up_ranks,
2990            down: down_ranks,
2991            input_width: gate.in_features,
2992            intermediate_width: gate.out_features,
2993        })
2994    }
2995
2996    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2997    pub fn replicated_bf16_swiglu_resident_device(
2998        &self,
2999        mlp: &ResidentReplicatedBf16SwiGlu,
3000        input: &ResidentReplicatedDeviceRows,
3001        activation_limit: Option<f32>,
3002    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3003        validate_step_expert_activation_limit(activation_limit)?;
3004        validate_replicated_device_rows(&self.ranks, input)?;
3005        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
3006        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
3007        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
3008        if input.width != mlp.input_width
3009            || mlp.gate.len() != self.ranks.len()
3010            || mlp.up.len() != self.ranks.len()
3011            || mlp.down.len() != self.ranks.len()
3012        {
3013            return Err("replicated BF16 SwiGLU residency or input width changed".into());
3014        }
3015
3016        let mut outputs = Vec::with_capacity(self.ranks.len());
3017        for rank in 0..self.ranks.len() {
3018            let engine = &self.ranks[rank];
3019            let gate = run_resident_bf16_rank_device(
3020                engine,
3021                &mlp.gate[rank],
3022                &input.ranks[rank],
3023                input.tokens,
3024                None,
3025                self.bulk_p2p,
3026            )?;
3027            let up = run_resident_bf16_rank_device(
3028                engine,
3029                &mlp.up[rank],
3030                &input.ranks[rank],
3031                input.tokens,
3032                None,
3033                self.bulk_p2p,
3034            )?;
3035            let _main = engine.gpu.enter_main()?;
3036            let values = input
3037                .tokens
3038                .checked_mul(mlp.intermediate_width)
3039                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
3040            let mut activation = engine.uninit(values)?;
3041            if let Some(limit) = activation_limit {
3042                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
3043            } else {
3044                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
3045            }
3046            outputs.push(run_resident_bf16_rank_device(
3047                engine,
3048                &mlp.down[rank],
3049                &activation,
3050                input.tokens,
3051                None,
3052                self.bulk_p2p,
3053            )?);
3054        }
3055        Ok(ResidentReplicatedDeviceRows {
3056            ranks: outputs,
3057            tokens: input.tokens,
3058            width: mlp.input_width,
3059        })
3060    }
3061
3062    /// Apply the same RMS-norm row program independently on every replicated rank.
3063    pub fn rms_norm_replicated_device_rows(
3064        &self,
3065        input: &ResidentReplicatedDeviceRows,
3066        weight: &[f32],
3067        eps: f32,
3068    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3069        validate_replicated_device_rows(&self.ranks, input)?;
3070        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
3071            return Err(format!(
3072                "replicated RMS norm weight/eps {}/{} != width {}",
3073                weight.len(),
3074                eps,
3075                input.width
3076            )
3077            .into());
3078        }
3079        let mut ranks = Vec::with_capacity(self.ranks.len());
3080        for (rank, engine) in self.ranks.iter().enumerate() {
3081            let _main = engine.gpu.enter_main()?;
3082            let weight = engine.htod(weight)?;
3083            let mut output = engine.uninit(input.tokens * input.width)?;
3084            engine.rms_norm(
3085                &input.ranks[rank],
3086                &weight,
3087                &mut output,
3088                input.width,
3089                input.tokens,
3090                eps,
3091            )?;
3092            ranks.push(output);
3093        }
3094        Ok(ResidentReplicatedDeviceRows {
3095            ranks,
3096            tokens: input.tokens,
3097            width: input.width,
3098        })
3099    }
3100
3101    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
3102    pub fn add_rms_norm_replicated_device_rows(
3103        &self,
3104        input: &ResidentReplicatedDeviceRows,
3105        update: &ResidentReplicatedDeviceRows,
3106        weight: &[f32],
3107        eps: f32,
3108    ) -> Result<
3109        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
3110        Box<dyn std::error::Error>,
3111    > {
3112        validate_replicated_device_rows(&self.ranks, input)?;
3113        validate_replicated_device_rows(&self.ranks, update)?;
3114        if input.tokens != update.tokens
3115            || input.width != update.width
3116            || weight.len() != input.width
3117            || !eps.is_finite()
3118            || eps <= 0.0
3119        {
3120            return Err(format!(
3121                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
3122                input.tokens,
3123                input.width,
3124                update.tokens,
3125                update.width,
3126                weight.len(),
3127            )
3128            .into());
3129        }
3130        let values = input.tokens * input.width;
3131        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
3132        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
3133        for (rank, engine) in self.ranks.iter().enumerate() {
3134            let _main = engine.gpu.enter_main()?;
3135            let weight = engine.htod(weight)?;
3136            let mut residual = engine.uninit(values)?;
3137            let mut normalized = engine.uninit(values)?;
3138            engine.add_rms_norm(
3139                &input.ranks[rank],
3140                &update.ranks[rank],
3141                &weight,
3142                &mut residual,
3143                &mut normalized,
3144                input.width,
3145                input.tokens,
3146                eps,
3147            )?;
3148            residual_ranks.push(residual);
3149            normalized_ranks.push(normalized);
3150        }
3151        Ok((
3152            ResidentReplicatedDeviceRows {
3153                ranks: residual_ranks,
3154                tokens: input.tokens,
3155                width: input.width,
3156            },
3157            ResidentReplicatedDeviceRows {
3158                ranks: normalized_ranks,
3159                tokens: input.tokens,
3160                width: input.width,
3161            },
3162        ))
3163    }
3164
3165    pub fn collect_replicated_device_rows(
3166        &self,
3167        rows: &ResidentReplicatedDeviceRows,
3168    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
3169        validate_replicated_device_rows(&self.ranks, rows)?;
3170        let mut outputs = Vec::with_capacity(self.ranks.len());
3171        for (rank, engine) in self.ranks.iter().enumerate() {
3172            let _main = engine.gpu.enter_main()?;
3173            outputs.push(engine.dtoh(&rows.ranks[rank])?);
3174        }
3175        Ok(outputs)
3176    }
3177
3178    pub fn upload_bf16_row_parallel(
3179        &self,
3180        matrix: Bf16Matrix<'_>,
3181    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
3182        matrix.validate()?;
3183        let tp = self.ranks.len();
3184        if matrix.in_features % tp != 0 {
3185            return Err(format!(
3186                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
3187                matrix.in_features
3188            )
3189            .into());
3190        }
3191        let mut ranks = Vec::with_capacity(tp);
3192        for (rank, engine) in self.ranks.iter().enumerate() {
3193            let shard = bf16_row_shard(matrix, tp, rank)?;
3194            ranks.push(upload_bf16_rank(
3195                engine,
3196                Bf16Matrix {
3197                    bytes: &shard,
3198                    out_features: matrix.out_features,
3199                    in_features: matrix.in_features / tp,
3200                },
3201                false,
3202            )?);
3203        }
3204        Ok(ResidentBf16RowParallel {
3205            ranks,
3206            out_features: matrix.out_features,
3207            in_features: matrix.in_features,
3208        })
3209    }
3210
3211    pub fn bf16_row_parallel_resident(
3212        &self,
3213        matrix: &ResidentBf16RowParallel,
3214        activations: &[f32],
3215        tokens: usize,
3216    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3217        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3218        validate_activations(activations, tokens, matrix.in_features)?;
3219        let tp = self.ranks.len();
3220        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3221        let mut rank_partials = Vec::with_capacity(tp);
3222        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3223            let local_activations =
3224                activation_shard(activations, tokens, matrix.in_features, tp, rank);
3225            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
3226            for (sum, value) in reduced.iter_mut().zip(&partial) {
3227                *sum += value;
3228            }
3229            rank_partials.push(partial);
3230        }
3231        Ok(RowParallelResult {
3232            reduced,
3233            rank_partials,
3234        })
3235    }
3236
3237    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
3238    pub fn upload_step_bf16_row_parallel(
3239        &self,
3240        matrix: Bf16Matrix<'_>,
3241    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3242        self.upload_step_bf16_row_parallel_inner(matrix, false)
3243    }
3244
3245    pub fn upload_step_bf16_row_parallel_f32_mirror(
3246        &self,
3247        matrix: Bf16Matrix<'_>,
3248    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3249        self.upload_step_bf16_row_parallel_inner(matrix, true)
3250    }
3251
3252    fn upload_step_bf16_row_parallel_inner(
3253        &self,
3254        matrix: Bf16Matrix<'_>,
3255        f32_mirror: bool,
3256    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3257        matrix.validate()?;
3258        let tp = self.ranks.len();
3259        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
3260        let local_in = matrix.in_features / tp;
3261        let blocks_per_rank = local_in / canonical_chunk_cols;
3262        let mut ranks = Vec::with_capacity(tp);
3263        for (rank, engine) in self.ranks.iter().enumerate() {
3264            let mut blocks = Vec::with_capacity(blocks_per_rank);
3265            for block in 0..blocks_per_rank {
3266                let global_block = rank * blocks_per_rank + block;
3267                let col_start = global_block * canonical_chunk_cols;
3268                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
3269                blocks.push(upload_bf16_rank(
3270                    engine,
3271                    Bf16Matrix {
3272                        bytes: &bytes,
3273                        out_features: matrix.out_features,
3274                        in_features: canonical_chunk_cols,
3275                    },
3276                    f32_mirror,
3277                )?);
3278            }
3279            ranks.push(blocks);
3280        }
3281        Ok(ResidentStepBf16RowParallel {
3282            ranks,
3283            out_features: matrix.out_features,
3284            in_features: matrix.in_features,
3285            canonical_chunk_cols,
3286        })
3287    }
3288
3289    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
3290    ///
3291    /// Block inputs and partials cross host memory, but every partial is added on the root device
3292    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
3293    pub fn step_bf16_row_parallel_resident(
3294        &self,
3295        matrix: &ResidentStepBf16RowParallel,
3296        activations: &[f32],
3297        tokens: usize,
3298    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3299        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3300        validate_activations(activations, tokens, matrix.in_features)?;
3301        let root = &self.ranks[0];
3302        let output_len = tokens
3303            .checked_mul(matrix.out_features)
3304            .ok_or("Step BF16 row output size overflow")?;
3305        let mut reduced = {
3306            let _main = root.gpu.enter_main()?;
3307            root.htod(&vec![0.0f32; output_len])?
3308        };
3309        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3310        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3311            for (block, resident) in blocks.iter().enumerate() {
3312                let global_block = rank * blocks_per_rank + block;
3313                let input = activation_shard(
3314                    activations,
3315                    tokens,
3316                    matrix.in_features,
3317                    PRODUCT_MAX_CARDS,
3318                    global_block,
3319                );
3320                let partial =
3321                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
3322                let next = {
3323                    let _main = root.gpu.enter_main()?;
3324                    let partial = root.htod(&partial)?;
3325                    let mut next = root.uninit(output_len)?;
3326                    root.add(&reduced, &partial, &mut next, output_len)?;
3327                    next
3328                };
3329                reduced = next;
3330            }
3331        }
3332        let _main = root.gpu.enter_main()?;
3333        root.dtoh(&reduced)
3334    }
3335
3336    /// Native-P2P Step row projection with canonical global K-block reduction.
3337    ///
3338    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3339    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3340    /// replay the same eight-block order as TP1 and the host-staged oracle.
3341    pub fn step_bf16_row_parallel_resident_native(
3342        &self,
3343        matrix: &ResidentStepBf16RowParallel,
3344        activations: &[f32],
3345        tokens: usize,
3346    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3347        if self.ranks.len() > 1 && !self.native_p2p {
3348            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3349        }
3350        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3351        validate_activations(activations, tokens, matrix.in_features)?;
3352        let root = &self.ranks[0];
3353        let root_input = {
3354            let _main = root.gpu.enter_main()?;
3355            root.htod(activations)?
3356        };
3357        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3358        // from the other ranks' streams while root's clone_htod may still be in flight.
3359        {
3360            let _main = root.gpu.enter_main()?;
3361            root.stream().synchronize()?;
3362        }
3363        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3364        let _main = root.gpu.enter_main()?;
3365        root.dtoh(&reduced)
3366    }
3367
3368    /// Device-input twin of [`Self::step_bf16_row_parallel_resident_native`] (lane/
3369    /// hermes-perf-fixes, 2026-08-23): the full activation arrives as a ROOT-DEVICE buffer
3370    /// and the reduced output stays root-resident — no DtoH of the attention output, no
3371    /// host O staging, no re-upload. Byte-identical to the host-canonical arm by
3372    /// construction (same block scatter, kernels, and global TP8 reduction order; the root
3373    /// bytes are dtod-copied where the host arm htod'd the same bytes). Caller must have
3374    /// synchronized the producer stream; the root stream is synchronized before returning.
3375    pub fn step_bf16_row_parallel_resident_native_device(
3376        &self,
3377        matrix: &ResidentStepBf16RowParallel,
3378        root_activation: &CudaSlice<f32>,
3379        tokens: usize,
3380    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3381        if self.ranks.len() > 1 && !self.native_p2p {
3382            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3383        }
3384        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3385        let values = tokens
3386            .checked_mul(matrix.in_features)
3387            .ok_or("device Step BF16 row activation size overflow")?;
3388        let root = &self.ranks[0];
3389        if tokens == 0
3390            || root_activation.len() < values
3391            || root_activation.ordinal() != root.ctx().ordinal()
3392        {
3393            return Err("device Step BF16 row root activation geometry mismatch".into());
3394        }
3395        let root_input = {
3396            let _main = root.gpu.enter_main()?;
3397            let mut root_input = root.uninit(values)?;
3398            root.stream()
3399                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
3400            root.stream().synchronize()?; // producer fence, as the host-input twin
3401            root_input
3402        };
3403        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3404        let _main = root.gpu.enter_main()?;
3405        root.stream().synchronize()?;
3406        Ok(reduced)
3407    }
3408
3409    /// Shared core of the two native Step row arms above: block scatter + rank GEMMs +
3410    /// canonical global TP8-order root reduction, from a root-resident input, returning the
3411    /// root-resident reduced output. Extracted verbatim so the host and device twins cannot
3412    /// drift numerically.
3413    fn step_bf16_row_native_reduce_from_root(
3414        &self,
3415        matrix: &ResidentStepBf16RowParallel,
3416        root_input: &CudaSlice<f32>,
3417        tokens: usize,
3418    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3419        let root = &self.ranks[0];
3420        let output_len = tokens
3421            .checked_mul(matrix.out_features)
3422            .ok_or("native Step BF16 row output size overflow")?;
3423        let mut reduced = {
3424            let _main = root.gpu.enter_main()?;
3425            root.htod(&vec![0.0f32; output_len])?
3426        };
3427        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3428        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3429        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3430        let mut remote_partial_keepalive = Vec::new();
3431        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3432            for (block, resident) in blocks.iter().enumerate() {
3433                let global_block = rank * blocks_per_rank + block;
3434                let col_start = global_block * matrix.canonical_chunk_cols;
3435                let block_len = tokens
3436                    .checked_mul(matrix.canonical_chunk_cols)
3437                    .ok_or("native Step BF16 row block size overflow")?;
3438                let block_input = if self.bulk_p2p {
3439                    let root_packed = {
3440                        let _main = root.gpu.enter_main()?;
3441                        let mut root_packed = root.uninit(block_len)?;
3442                        root.copy_rows_strided(
3443                            &root_input,
3444                            &mut root_packed,
3445                            matrix.canonical_chunk_cols,
3446                            tokens,
3447                            matrix.in_features,
3448                            col_start,
3449                        )?;
3450                        root_packed
3451                    };
3452                    if rank == 0 {
3453                        root_packed
3454                    } else {
3455                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3456                        // root stream; this rank's peer read must not overtake it.
3457                        {
3458                            let _main = root.gpu.enter_main()?;
3459                            root.stream().synchronize()?;
3460                        }
3461                        let engine = &self.ranks[rank];
3462                        let _main = engine.gpu.enter_main()?;
3463                        let mut block_input = engine.uninit(block_len)?;
3464                        engine
3465                            .stream()
3466                            .memcpy_dtod(&root_packed, &mut block_input)?;
3467                        root_packed_keepalive.push(root_packed);
3468                        block_input
3469                    }
3470                } else {
3471                    let engine = &self.ranks[rank];
3472                    let _main = engine.gpu.enter_main()?;
3473                    let mut block_input = engine.uninit(block_len)?;
3474                    for token in 0..tokens {
3475                        let source_start = token * matrix.in_features + col_start;
3476                        let source = root_input
3477                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3478                        let destination_start = token * matrix.canonical_chunk_cols;
3479                        let mut destination = block_input.slice_mut(
3480                            destination_start..destination_start + matrix.canonical_chunk_cols,
3481                        );
3482                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3483                    }
3484                    block_input
3485                };
3486                let partial = run_resident_bf16_rank_device(
3487                    &self.ranks[rank],
3488                    resident,
3489                    &block_input,
3490                    tokens,
3491                    None,
3492                    self.bulk_p2p,
3493                )?;
3494                block_input_keepalive.push(block_input);
3495                let root_partial = if rank == 0 {
3496                    partial
3497                } else {
3498                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3499                    // rank's kernel on its own stream; root's peer read must not overtake it.
3500                    {
3501                        let engine = &self.ranks[rank];
3502                        let _main = engine.gpu.enter_main()?;
3503                        engine.stream().synchronize()?;
3504                    }
3505                    let _main = root.gpu.enter_main()?;
3506                    let mut peer_partial = root.uninit(output_len)?;
3507                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3508                    remote_partial_keepalive.push(partial);
3509                    peer_partial
3510                };
3511                let next = {
3512                    let _main = root.gpu.enter_main()?;
3513                    let mut next = root.uninit(output_len)?;
3514                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3515                    next
3516                };
3517                reduced = next;
3518            }
3519        }
3520        {
3521            let _main = root.gpu.enter_main()?;
3522            root.stream().synchronize()?;
3523        }
3524        drop(remote_partial_keepalive);
3525        drop(root_packed_keepalive);
3526        drop(block_input_keepalive);
3527        Ok(reduced)
3528    }
3529
3530    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3531    /// on the root device.
3532    pub fn step_bf16_row_parallel_resident_root_device(
3533        &self,
3534        matrix: &ResidentStepBf16RowParallel,
3535        rank_activations: &[CudaSlice<f32>],
3536        tokens: usize,
3537    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3538        if self.ranks.len() > 1 && !self.native_p2p {
3539            return Err(
3540                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3541            );
3542        }
3543        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3544        let local_width = matrix.in_features / self.ranks.len();
3545        let shard_len = tokens
3546            .checked_mul(local_width)
3547            .ok_or("device Step BF16 row shard size overflow")?;
3548        if tokens == 0
3549            || rank_activations.len() != self.ranks.len()
3550            || rank_activations
3551                .iter()
3552                .zip(&self.ranks)
3553                .any(|(rows, engine)| {
3554                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3555                })
3556        {
3557            return Err("device Step BF16 row activation shard geometry changed".into());
3558        }
3559
3560        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3561        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3562        let mut partials = Vec::with_capacity(self.ranks.len());
3563        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3564            if blocks.len() != blocks_per_rank {
3565                return Err(format!(
3566                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3567                    blocks.len()
3568                )
3569                .into());
3570            }
3571            let engine = &self.ranks[rank];
3572            let _main = engine.gpu.enter_main()?;
3573            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3574            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3575            for (block, resident) in blocks.iter().enumerate() {
3576                let block_len = tokens
3577                    .checked_mul(matrix.canonical_chunk_cols)
3578                    .ok_or("device Step BF16 row block size overflow")?;
3579                let mut block_input = engine.uninit(block_len)?;
3580                let local_col_start = block * matrix.canonical_chunk_cols;
3581                if self.bulk_p2p {
3582                    engine.copy_rows_strided(
3583                        &rank_activations[rank],
3584                        &mut block_input,
3585                        matrix.canonical_chunk_cols,
3586                        tokens,
3587                        local_width,
3588                        local_col_start,
3589                    )?;
3590                } else {
3591                    for token in 0..tokens {
3592                        let source_start = token * local_width + local_col_start;
3593                        let source = rank_activations[rank]
3594                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3595                        let destination_start = token * matrix.canonical_chunk_cols;
3596                        let mut destination = block_input.slice_mut(
3597                            destination_start..destination_start + matrix.canonical_chunk_cols,
3598                        );
3599                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3600                    }
3601                }
3602                let partial = run_resident_bf16_rank_device(
3603                    engine,
3604                    resident,
3605                    &block_input,
3606                    tokens,
3607                    None,
3608                    self.bulk_p2p,
3609                )?;
3610                rank_inputs.push(block_input);
3611                rank_partials.push(partial);
3612            }
3613            block_inputs.push(rank_inputs);
3614            partials.push(rank_partials);
3615        }
3616        for engine in self.ranks.iter().skip(1) {
3617            let _main = engine.gpu.enter_main()?;
3618            engine.stream().synchronize()?;
3619        }
3620
3621        let output_len = tokens
3622            .checked_mul(matrix.out_features)
3623            .ok_or("device Step BF16 row output size overflow")?;
3624        let root = &self.ranks[0];
3625        let _main = root.gpu.enter_main()?;
3626        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3627        let mut remote_partials = Vec::new();
3628        for (rank, rank_partials) in partials.into_iter().enumerate() {
3629            for partial in rank_partials {
3630                let root_partial = if rank == 0 {
3631                    partial
3632                } else {
3633                    let mut peer_partial = root.uninit(output_len)?;
3634                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3635                    remote_partials.push(partial);
3636                    peer_partial
3637                };
3638                let mut next = root.uninit(output_len)?;
3639                root.add(&reduced, &root_partial, &mut next, output_len)?;
3640                reduced = next;
3641            }
3642        }
3643        root.stream().synchronize()?;
3644        drop(remote_partials);
3645        drop(block_inputs);
3646        Ok(reduced)
3647    }
3648
3649    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3650    pub fn step_bf16_row_parallel_resident_replicated_device(
3651        &self,
3652        matrix: &ResidentStepBf16RowParallel,
3653        rank_activations: &[CudaSlice<f32>],
3654        tokens: usize,
3655    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3656        let reduced =
3657            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3658        let output_len = tokens
3659            .checked_mul(matrix.out_features)
3660            .ok_or("device Step BF16 row output size overflow")?;
3661        let mut ranks = Vec::with_capacity(self.ranks.len());
3662        ranks.push(reduced);
3663        for engine in self.ranks.iter().skip(1) {
3664            let _main = engine.gpu.enter_main()?;
3665            let mut peer_output = engine.uninit(output_len)?;
3666            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3667            ranks.push(peer_output);
3668        }
3669        Ok(ResidentReplicatedDeviceRows {
3670            ranks,
3671            tokens,
3672            width: matrix.out_features,
3673        })
3674    }
3675
3676    pub fn upload_expert(
3677        &self,
3678        gate: E4m3BlockMatrix<'_>,
3679        up: E4m3BlockMatrix<'_>,
3680        down: E4m3BlockMatrix<'_>,
3681    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3682        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3683            return Err("TP expert gate/up dimensions differ".into());
3684        }
3685        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3686            return Err(format!(
3687                "TP expert down {}x{} does not invert gate/up {}x{}",
3688                down.out_features, down.in_features, gate.out_features, gate.in_features
3689            )
3690            .into());
3691        }
3692        Ok(ResidentTpExpert {
3693            gate: self.upload_column_parallel(gate)?,
3694            up: self.upload_column_parallel(up)?,
3695            down: self.upload_row_parallel(down)?,
3696            input_width: gate.in_features,
3697            expert_width: gate.out_features,
3698        })
3699    }
3700
3701    pub fn run_expert(
3702        &self,
3703        expert: &ResidentTpExpert,
3704        input: &[f32],
3705        tokens: usize,
3706    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3707        validate_activations(input, tokens, expert.input_width)?;
3708        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3709        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3710        let activated: Vec<f32> = gate
3711            .gathered
3712            .iter()
3713            .zip(&up.gathered)
3714            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3715            .collect();
3716        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3717        Ok(self
3718            .row_parallel_resident(&expert.down, &activated, tokens)?
3719            .reduced)
3720    }
3721
3722    pub fn upload_expert_parallel(
3723        &self,
3724        gate: E4m3ExpertBank<'_>,
3725        up: E4m3ExpertBank<'_>,
3726        down: E4m3ExpertBank<'_>,
3727    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3728        gate.validate()?;
3729        up.validate()?;
3730        down.validate()?;
3731        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3732            return Err("EP gate/up/down expert counts differ".into());
3733        }
3734        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3735            return Err("EP gate/up dimensions differ".into());
3736        }
3737        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3738            return Err(format!(
3739                "EP down {}x{} does not invert gate/up {}x{}",
3740                down.out_features, down.in_features, gate.out_features, gate.in_features
3741            )
3742            .into());
3743        }
3744        if gate.expert_count % self.ranks.len() != 0 {
3745            return Err(format!(
3746                "EP expert count {} is not divisible by {} ranks",
3747                gate.expert_count,
3748                self.ranks.len()
3749            )
3750            .into());
3751        }
3752
3753        let per_rank = gate.expert_count / self.ranks.len();
3754        let mut ranks = Vec::with_capacity(self.ranks.len());
3755        for (rank, engine) in self.ranks.iter().enumerate() {
3756            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3757            ranks.push(ResidentEpRank {
3758                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3759                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3760                down: upload_expert_bank_rank(engine, down, expert_range)?,
3761            });
3762        }
3763        Ok(ResidentExpertParallel {
3764            ranks,
3765            expert_count: gate.expert_count,
3766            input_width: gate.in_features,
3767            expert_width: gate.out_features,
3768        })
3769    }
3770
3771    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3772    ///
3773    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3774    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3775    /// without routing, transport, or combine changing underneath it.
3776    #[allow(clippy::too_many_arguments)]
3777    pub fn prepare_step_grouped_fp8_gate(
3778        &self,
3779        gate: E4m3ExpertBank<'_>,
3780        up: E4m3ExpertBank<'_>,
3781        down: E4m3ExpertBank<'_>,
3782        input: &[f32],
3783        tokens: usize,
3784        selected: &[usize],
3785        activation_limit: Option<f32>,
3786    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3787        gate.validate()?;
3788        up.validate()?;
3789        down.validate()?;
3790        validate_step_expert_activation_limit(activation_limit)?;
3791        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3792            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3793            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3794        {
3795            return Err(format!(
3796                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3797                 got gate/up/down={}/{}/{}",
3798                gate.expert_count, up.expert_count, down.expert_count,
3799            )
3800            .into());
3801        }
3802        if gate.in_features != up.in_features
3803            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3804            || up.out_features != STEP_GROUPED_FP8_WIDTH
3805            || down.in_features != STEP_GROUPED_FP8_WIDTH
3806            || down.out_features != gate.in_features
3807        {
3808            return Err(format!(
3809                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3810                gate.out_features,
3811                gate.in_features,
3812                up.out_features,
3813                up.in_features,
3814                down.out_features,
3815                down.in_features,
3816            )
3817            .into());
3818        }
3819        validate_activations(input, tokens, gate.in_features)?;
3820        let pairs = tokens
3821            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3822            .ok_or("official Step grouped FP8 route count overflow")?;
3823        if selected.len() != pairs {
3824            return Err(format!(
3825                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3826                 ({pairs})",
3827                selected.len()
3828            )
3829            .into());
3830        }
3831        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3832            let mut unique = routes.to_vec();
3833            unique.sort_unstable();
3834            unique.dedup();
3835            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3836                return Err(format!(
3837                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3838                     {routes:?}"
3839                )
3840                .into());
3841            }
3842        }
3843
3844        let engine = self
3845            .ranks
3846            .first()
3847            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3848        let _main = engine.gpu.enter_main()?;
3849        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3850        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3851        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3852        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3853        let input = engine.htod(input)?;
3854        let route_csr = ExpertCsr::from_token_routes(
3855            STEP_GROUPED_FP8_EXPERTS,
3856            tokens,
3857            STEP_GROUPED_FP8_TOP_K,
3858            selected,
3859        )?
3860        .upload(engine)?;
3861        let pair_rows = (0..pairs).collect::<Vec<_>>();
3862        let down_csr =
3863            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3864                .upload(engine)?;
3865        let gate_workspace =
3866            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3867        let up_workspace =
3868            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3869        let down_workspace =
3870            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3871        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3872        Ok(PreparedStepGroupedFp8Gate {
3873            device: engine.ctx().ordinal(),
3874            gate,
3875            up,
3876            down,
3877            input,
3878            route_csr,
3879            down_csr,
3880            gate_workspace,
3881            up_workspace,
3882            down_workspace,
3883            activation,
3884            activation_limit,
3885            tokens,
3886            pairs,
3887        })
3888    }
3889
3890    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3891    pub fn run_step_grouped_fp8_gate(
3892        &self,
3893        plan: &mut PreparedStepGroupedFp8Gate,
3894    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3895        let engine = self
3896            .ranks
3897            .first()
3898            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3899        if engine.ctx().ordinal() != plan.device {
3900            return Err(format!(
3901                "official Step grouped FP8 plan device {} != rank-zero device {}",
3902                plan.device,
3903                engine.ctx().ordinal()
3904            )
3905            .into());
3906        }
3907        let _main = engine.gpu.enter_main()?;
3908
3909        plan.gate_workspace.quantize(engine, &plan.input)?;
3910        plan.gate_workspace.project(
3911            engine,
3912            &plan.gate.codes,
3913            &plan.gate.scales,
3914            &plan.route_csr,
3915            plan.gate.code_stride,
3916            plan.gate.scale_stride,
3917            1.0,
3918        )?;
3919        plan.up_workspace.quantize(engine, &plan.input)?;
3920        plan.up_workspace.project(
3921            engine,
3922            &plan.up.codes,
3923            &plan.up.scales,
3924            &plan.route_csr,
3925            plan.up.code_stride,
3926            plan.up.scale_stride,
3927            1.0,
3928        )?;
3929        if let Some(limit) = plan.activation_limit {
3930            engine.silu_clamped_mul_host_expf(
3931                plan.gate_workspace.output(),
3932                plan.up_workspace.output(),
3933                limit,
3934                &mut plan.activation,
3935                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3936            )?;
3937        } else {
3938            engine.silu_mul_host_expf(
3939                plan.gate_workspace.output(),
3940                plan.up_workspace.output(),
3941                &mut plan.activation,
3942                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3943            )?;
3944        }
3945        plan.down_workspace.quantize(engine, &plan.activation)?;
3946        plan.down_workspace.project(
3947            engine,
3948            &plan.down.codes,
3949            &plan.down.scales,
3950            &plan.down_csr,
3951            plan.down.code_stride,
3952            plan.down.scale_stride,
3953            1.0,
3954        )?;
3955
3956        Ok(StepGroupedFp8ProjectionOutput {
3957            gate: engine.dtoh(plan.gate_workspace.output())?,
3958            up: engine.dtoh(plan.up_workspace.output())?,
3959            down: engine.dtoh(plan.down_workspace.output())?,
3960        })
3961    }
3962
3963    pub fn prepare_step_grouped_expert_parallel_gate(
3964        &self,
3965        experts: &ResidentExpertParallel,
3966        input: &[f32],
3967        tokens: usize,
3968        selected: &[usize],
3969        activation_limit: Option<f32>,
3970    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3971        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3972            experts,
3973            input,
3974            tokens,
3975            selected,
3976            activation_limit,
3977            tokens,
3978        )
3979    }
3980
3981    #[allow(clippy::too_many_arguments)]
3982    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3983        &self,
3984        experts: &ResidentExpertParallel,
3985        input: &[f32],
3986        tokens: usize,
3987        selected: &[usize],
3988        activation_limit: Option<f32>,
3989        max_tokens: usize,
3990    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3991        if !self.native_p2p || !self.ep_device_arithmetic {
3992            return Err(
3993                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3994            );
3995        }
3996        validate_step_expert_activation_limit(activation_limit)?;
3997        validate_ep_residency(&self.ranks, experts)?;
3998        validate_activations(input, tokens, experts.input_width)?;
3999        if max_tokens < tokens || max_tokens > i32::MAX as usize {
4000            return Err(format!(
4001                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
4002            )
4003            .into());
4004        }
4005        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
4006            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
4007        {
4008            return Err(format!(
4009                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
4010                STEP_GROUPED_FP8_EXPERTS,
4011                STEP_GROUPED_FP8_WIDTH,
4012                experts.expert_count,
4013                experts.expert_width,
4014            )
4015            .into());
4016        }
4017        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4018        let max_pairs = max_tokens
4019            .checked_mul(STEP_GROUPED_FP8_TOP_K)
4020            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
4021        let input_capacity = max_tokens
4022            .checked_mul(experts.input_width)
4023            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
4024
4025        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
4026        for engine in &self.ranks {
4027            let _main = engine.gpu.enter_main()?;
4028            rank_inputs.push(engine.uninit(input_capacity)?);
4029        }
4030
4031        let mut owners = Vec::with_capacity(self.ranks.len());
4032        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
4033            if rank.gate.expert_range != rank.up.expert_range
4034                || rank.gate.expert_range != rank.down.expert_range
4035            {
4036                return Err(format!(
4037                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
4038                    owner_rank
4039                )
4040                .into());
4041            }
4042            let local_experts = rank.gate.expert_range.len();
4043            let engine = &self.ranks[owner_rank];
4044            let _main = engine.gpu.enter_main()?;
4045            let route_csr =
4046                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
4047            let down_csr =
4048                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
4049            let gate_workspace = Fp8GroupedWorkspace::new(
4050                engine,
4051                experts.input_width,
4052                experts.expert_width,
4053                max_tokens,
4054                max_pairs,
4055            )?;
4056            let up_workspace = Fp8GroupedWorkspace::new(
4057                engine,
4058                experts.input_width,
4059                experts.expert_width,
4060                max_tokens,
4061                max_pairs,
4062            )?;
4063            let down_workspace = Fp8GroupedWorkspace::new(
4064                engine,
4065                experts.expert_width,
4066                experts.input_width,
4067                max_pairs,
4068                max_pairs,
4069            )?;
4070            let activation = engine.uninit(
4071                max_pairs
4072                    .checked_mul(experts.expert_width)
4073                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
4074            )?;
4075            owners.push(PreparedStepGroupedExpertOwner {
4076                rank: owner_rank,
4077                global_pairs: Vec::new(),
4078                route_csr,
4079                down_csr,
4080                gate_workspace,
4081                up_workspace,
4082                down_workspace,
4083                activation,
4084            });
4085        }
4086
4087        let mut plan = PreparedStepGroupedExpertParallelGate {
4088            rank_inputs,
4089            owners,
4090            activation_limit,
4091            tokens: 0,
4092            pairs: 0,
4093            max_tokens,
4094            max_pairs,
4095            input_width: experts.input_width,
4096            expert_width: experts.expert_width,
4097            generation: 0,
4098            executed_generation: None,
4099            ready: false,
4100        };
4101        self.refresh_step_grouped_expert_parallel_gate(
4102            experts, &mut plan, input, tokens, selected,
4103        )?;
4104        Ok(plan)
4105    }
4106
4107    fn prepare_step_grouped_expert_parallel_refresh(
4108        &self,
4109        experts: &ResidentExpertParallel,
4110        plan: &PreparedStepGroupedExpertParallelGate,
4111        tokens: usize,
4112        selected: &[usize],
4113    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
4114    {
4115        validate_ep_residency(&self.ranks, experts)?;
4116        if plan.rank_inputs.len() != self.ranks.len()
4117            || plan.owners.len() != self.ranks.len()
4118            || plan.input_width != experts.input_width
4119            || plan.expert_width != experts.expert_width
4120            || tokens > plan.max_tokens
4121        {
4122            return Err(format!(
4123                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
4124                 input={}/{} expert={}/{} tokens={}/{}",
4125                plan.rank_inputs.len(),
4126                self.ranks.len(),
4127                plan.owners.len(),
4128                self.ranks.len(),
4129                plan.input_width,
4130                experts.input_width,
4131                plan.expert_width,
4132                experts.expert_width,
4133                tokens,
4134                plan.max_tokens,
4135            )
4136            .into());
4137        }
4138        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4139        if pairs > plan.max_pairs {
4140            return Err(format!(
4141                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
4142                plan.max_pairs
4143            )
4144            .into());
4145        }
4146        let next_generation = plan
4147            .generation
4148            .checked_add(1)
4149            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4150        let owner_routes = partition_expert_owner_routes(
4151            experts.expert_count,
4152            self.ranks.len(),
4153            tokens,
4154            STEP_GROUPED_FP8_TOP_K,
4155            selected,
4156        )?;
4157        let mut schedules = Vec::with_capacity(self.ranks.len());
4158        for routes in owner_routes {
4159            if routes.selected.is_empty() {
4160                schedules.push(None);
4161                continue;
4162            }
4163            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
4164            let local_pairs = routes.selected.len();
4165            let route_csr = ExpertCsr::from_pair_rows(
4166                local_experts,
4167                tokens,
4168                &routes.selected,
4169                &routes.token_rows,
4170            )?;
4171            let down_rows = (0..local_pairs).collect::<Vec<_>>();
4172            let down_csr = ExpertCsr::from_pair_rows(
4173                local_experts,
4174                local_pairs,
4175                &routes.selected,
4176                &down_rows,
4177            )?;
4178            schedules.push(Some(StepGroupedExpertOwnerSchedule {
4179                global_pairs: routes.global_pairs,
4180                route_csr,
4181                down_csr,
4182            }));
4183        }
4184        Ok((pairs, next_generation, schedules))
4185    }
4186
4187    fn commit_step_grouped_expert_parallel_refresh(
4188        &self,
4189        plan: &mut PreparedStepGroupedExpertParallelGate,
4190        tokens: usize,
4191        pairs: usize,
4192        next_generation: u64,
4193        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
4194    ) -> Result<(), Box<dyn std::error::Error>> {
4195        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
4196            let engine = &self.ranks[owner.rank];
4197            let _main = engine.gpu.enter_main()?;
4198            if let Some(schedule) = schedule {
4199                owner.route_csr.refresh(engine, &schedule.route_csr)?;
4200                owner.down_csr.refresh(engine, &schedule.down_csr)?;
4201                owner.global_pairs = schedule.global_pairs;
4202            } else {
4203                owner.route_csr.clear();
4204                owner.down_csr.clear();
4205                owner.global_pairs.clear();
4206            }
4207        }
4208        plan.tokens = tokens;
4209        plan.pairs = pairs;
4210        plan.generation = next_generation;
4211        plan.ready = true;
4212        Ok(())
4213    }
4214
4215    pub fn refresh_step_grouped_expert_parallel_gate(
4216        &self,
4217        experts: &ResidentExpertParallel,
4218        plan: &mut PreparedStepGroupedExpertParallelGate,
4219        input: &[f32],
4220        tokens: usize,
4221        selected: &[usize],
4222    ) -> Result<(), Box<dyn std::error::Error>> {
4223        validate_activations(input, tokens, experts.input_width)?;
4224        let (pairs, next_generation, schedules) =
4225            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4226
4227        plan.ready = false;
4228        plan.executed_generation = None;
4229        {
4230            let root = &self.ranks[0];
4231            let _main = root.gpu.enter_main()?;
4232            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
4233            root.stream().memcpy_htod(input, &mut destination)?;
4234            root.stream().synchronize()?;
4235        }
4236        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4237        let root_input = &root_inputs[0];
4238        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4239            let engine = &self.ranks[rank + 1];
4240            let _main = engine.gpu.enter_main()?;
4241            let mut destination = peer_input.slice_mut(0..input.len());
4242            engine
4243                .stream()
4244                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
4245        }
4246        self.commit_step_grouped_expert_parallel_refresh(
4247            plan,
4248            tokens,
4249            pairs,
4250            next_generation,
4251            schedules,
4252        )
4253    }
4254
4255    /// Refresh routes and inputs from an already-resident rank-zero activation.
4256    ///
4257    /// The caller must order the source producer before this call. The root copy is completed
4258    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
4259    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
4260        &self,
4261        experts: &ResidentExpertParallel,
4262        plan: &mut PreparedStepGroupedExpertParallelGate,
4263        input: &CudaSlice<f32>,
4264        tokens: usize,
4265        selected: &[usize],
4266    ) -> Result<(), Box<dyn std::error::Error>> {
4267        let input_values = tokens
4268            .checked_mul(experts.input_width)
4269            .ok_or("Step owner-grouped FP8 input size overflow")?;
4270        let root = self
4271            .ranks
4272            .first()
4273            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
4274        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
4275            return Err(format!(
4276                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
4277                 device {}",
4278                input.len(),
4279                input.ordinal(),
4280                input_values,
4281                root.ctx().ordinal(),
4282            )
4283            .into());
4284        }
4285        let (pairs, next_generation, schedules) =
4286            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4287
4288        plan.ready = false;
4289        plan.executed_generation = None;
4290        {
4291            let _main = root.gpu.enter_main()?;
4292            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
4293            root.stream()
4294                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
4295            root.stream().synchronize()?;
4296        }
4297        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4298        let root_input = &root_inputs[0];
4299        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4300            let engine = &self.ranks[rank + 1];
4301            let _main = engine.gpu.enter_main()?;
4302            let mut destination = peer_input.slice_mut(0..input_values);
4303            engine
4304                .stream()
4305                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
4306        }
4307        self.commit_step_grouped_expert_parallel_refresh(
4308            plan,
4309            tokens,
4310            pairs,
4311            next_generation,
4312            schedules,
4313        )
4314    }
4315
4316    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
4317    ///
4318    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
4319    /// and combine result, so callers must refresh combine metadata before executing again.
4320    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
4321        &self,
4322        experts: &ResidentExpertParallel,
4323        plan: &mut PreparedStepGroupedExpertParallelGate,
4324        input: &ResidentReplicatedDeviceRows,
4325    ) -> Result<(), Box<dyn std::error::Error>> {
4326        validate_ep_residency(&self.ranks, experts)?;
4327        validate_replicated_device_rows(&self.ranks, input)?;
4328        if !plan.ready
4329            || input.tokens != plan.tokens
4330            || input.width != plan.input_width
4331            || input.tokens > plan.max_tokens
4332            || plan.rank_inputs.len() != self.ranks.len()
4333            || plan.owners.len() != self.ranks.len()
4334            || plan.input_width != experts.input_width
4335            || plan.expert_width != experts.expert_width
4336        {
4337            return Err("Step owner-grouped replicated input geometry changed".into());
4338        }
4339        let values = input
4340            .tokens
4341            .checked_mul(input.width)
4342            .ok_or("Step owner-grouped replicated input size overflow")?;
4343        let next_generation = plan
4344            .generation
4345            .checked_add(1)
4346            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4347        plan.ready = false;
4348        plan.executed_generation = None;
4349        for (rank, engine) in self.ranks.iter().enumerate() {
4350            let _main = engine.gpu.enter_main()?;
4351            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
4352            engine
4353                .stream()
4354                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
4355        }
4356        plan.generation = next_generation;
4357        plan.ready = true;
4358        Ok(())
4359    }
4360
4361    pub fn execute_step_grouped_expert_parallel_gate(
4362        &self,
4363        experts: &ResidentExpertParallel,
4364        plan: &mut PreparedStepGroupedExpertParallelGate,
4365    ) -> Result<(), Box<dyn std::error::Error>> {
4366        validate_ep_residency(&self.ranks, experts)?;
4367        if !plan.ready
4368            || plan.rank_inputs.len() != self.ranks.len()
4369            || plan.owners.len() != self.ranks.len()
4370            || plan.input_width != experts.input_width
4371            || plan.expert_width != experts.expert_width
4372        {
4373            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
4374        }
4375        plan.executed_generation = None;
4376
4377        for owner in &mut plan.owners {
4378            if owner.global_pairs.is_empty() {
4379                continue;
4380            }
4381            let engine = &self.ranks[owner.rank];
4382            let bank = &experts.ranks[owner.rank];
4383            let _main = engine.gpu.enter_main()?;
4384            let local_pairs = owner.global_pairs.len();
4385            owner.gate_workspace.quantize_for_shape(
4386                engine,
4387                &plan.rank_inputs[owner.rank],
4388                plan.tokens,
4389                local_pairs,
4390            )?;
4391            owner.gate_workspace.project(
4392                engine,
4393                &bank.gate.codes,
4394                &bank.gate.scales,
4395                &owner.route_csr,
4396                bank.gate.code_stride,
4397                bank.gate.scale_stride,
4398                1.0,
4399            )?;
4400            owner.up_workspace.quantize_for_shape(
4401                engine,
4402                &plan.rank_inputs[owner.rank],
4403                plan.tokens,
4404                local_pairs,
4405            )?;
4406            owner.up_workspace.project(
4407                engine,
4408                &bank.up.codes,
4409                &bank.up.scales,
4410                &owner.route_csr,
4411                bank.up.code_stride,
4412                bank.up.scale_stride,
4413                1.0,
4414            )?;
4415        }
4416        for owner in &mut plan.owners {
4417            if owner.global_pairs.is_empty() {
4418                continue;
4419            }
4420            let engine = &self.ranks[owner.rank];
4421            let _main = engine.gpu.enter_main()?;
4422            let values = owner.global_pairs.len() * plan.expert_width;
4423            if let Some(limit) = plan.activation_limit {
4424                engine.silu_clamped_mul_host_expf(
4425                    owner.gate_workspace.output(),
4426                    owner.up_workspace.output(),
4427                    limit,
4428                    &mut owner.activation,
4429                    values,
4430                )?;
4431            } else {
4432                engine.silu_mul_host_expf(
4433                    owner.gate_workspace.output(),
4434                    owner.up_workspace.output(),
4435                    &mut owner.activation,
4436                    values,
4437                )?;
4438            }
4439        }
4440        for owner in &mut plan.owners {
4441            if owner.global_pairs.is_empty() {
4442                continue;
4443            }
4444            let engine = &self.ranks[owner.rank];
4445            let bank = &experts.ranks[owner.rank];
4446            let _main = engine.gpu.enter_main()?;
4447            let local_pairs = owner.global_pairs.len();
4448            owner.down_workspace.quantize_for_shape(
4449                engine,
4450                &owner.activation,
4451                local_pairs,
4452                local_pairs,
4453            )?;
4454            owner.down_workspace.project(
4455                engine,
4456                &bank.down.codes,
4457                &bank.down.scales,
4458                &owner.down_csr,
4459                bank.down.code_stride,
4460                bank.down.scale_stride,
4461                1.0,
4462            )?;
4463        }
4464        plan.executed_generation = Some(plan.generation);
4465        Ok(())
4466    }
4467
4468    pub fn collect_step_grouped_expert_parallel_gate(
4469        &self,
4470        plan: &PreparedStepGroupedExpertParallelGate,
4471    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4472        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4473            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4474        }
4475        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4476        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4477        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4478        for owner in &plan.owners {
4479            if owner.global_pairs.is_empty() {
4480                continue;
4481            }
4482            let engine = &self.ranks[owner.rank];
4483            let _main = engine.gpu.enter_main()?;
4484            let owner_gate = engine.dtoh_view(
4485                &owner
4486                    .gate_workspace
4487                    .output()
4488                    .slice(0..owner.gate_workspace.output_len()),
4489            )?;
4490            let owner_up = engine.dtoh_view(
4491                &owner
4492                    .up_workspace
4493                    .output()
4494                    .slice(0..owner.up_workspace.output_len()),
4495            )?;
4496            let owner_down = engine.dtoh_view(
4497                &owner
4498                    .down_workspace
4499                    .output()
4500                    .slice(0..owner.down_workspace.output_len()),
4501            )?;
4502            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4503                let local_expert = local_pair * plan.expert_width;
4504                let global_expert = global_pair * plan.expert_width;
4505                gate[global_expert..global_expert + plan.expert_width]
4506                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4507                up[global_expert..global_expert + plan.expert_width]
4508                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4509
4510                let local_hidden = local_pair * plan.input_width;
4511                let global_hidden = global_pair * plan.input_width;
4512                down[global_hidden..global_hidden + plan.input_width]
4513                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4514            }
4515        }
4516        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4517    }
4518
4519    pub fn run_step_grouped_expert_parallel_gate(
4520        &self,
4521        experts: &ResidentExpertParallel,
4522        plan: &mut PreparedStepGroupedExpertParallelGate,
4523    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4524        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4525        self.collect_step_grouped_expert_parallel_gate(plan)
4526    }
4527
4528    pub fn prepare_step_grouped_expert_parallel_combine(
4529        &self,
4530        plan: &PreparedStepGroupedExpertParallelGate,
4531        route_weights: &[f32],
4532    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4533        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4534            return Err(
4535                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4536            );
4537        }
4538        let owner_pairs = plan
4539            .owners
4540            .iter()
4541            .map(|owner| owner.global_pairs.as_slice())
4542            .collect::<Vec<_>>();
4543        let shape = validate_weighted_route_combine(
4544            plan.input_width,
4545            STEP_GROUPED_FP8_TOP_K,
4546            plan.max_tokens,
4547            plan.tokens,
4548            &owner_pairs,
4549            route_weights,
4550        )?;
4551        if shape.max_pairs != plan.max_pairs {
4552            return Err(format!(
4553                "Step owner-grouped combine capacity {} != projection capacity {}",
4554                shape.max_pairs, plan.max_pairs
4555            )
4556            .into());
4557        }
4558        let root = self
4559            .ranks
4560            .first()
4561            .ok_or("Step owner-grouped combine has no root rank")?;
4562        let slot_values = shape
4563            .max_pairs
4564            .checked_mul(plan.input_width)
4565            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4566        let output_values = plan
4567            .max_tokens
4568            .checked_mul(plan.input_width)
4569            .ok_or("Step owner-grouped combine output capacity overflow")?;
4570        let (root_device, owners, peer_staging, slots, weights, output) = {
4571            let _main = root.gpu.enter_main()?;
4572            let mut owners = Vec::with_capacity(plan.owners.len());
4573            for _ in &plan.owners {
4574                owners.push(PreparedPeerWeightedRouteOwner {
4575                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4576                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4577                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4578                    active_pairs: 0,
4579                });
4580            }
4581            (
4582                root.ctx().ordinal(),
4583                owners,
4584                root.uninit(slot_values)?,
4585                root.uninit(slot_values)?,
4586                root.uninit(shape.max_pairs)?,
4587                root.uninit(output_values)?,
4588            )
4589        };
4590        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4591        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4592        for engine in self.ranks.iter().skip(1) {
4593            let _main = engine.gpu.enter_main()?;
4594            peer_devices.push(engine.ctx().ordinal());
4595            peer_outputs.push(engine.uninit(output_values)?);
4596        }
4597        let mut combine = PreparedPeerWeightedRouteCombine {
4598            root_device,
4599            owners,
4600            peer_staging,
4601            slots,
4602            weights,
4603            output,
4604            peer_devices,
4605            peer_outputs,
4606            width: plan.input_width,
4607            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4608            max_tokens: plan.max_tokens,
4609            max_pairs: shape.max_pairs,
4610            tokens: 0,
4611            pairs: 0,
4612            projection_generation: 0,
4613            output_generation: None,
4614            broadcast_generation: None,
4615            ready: false,
4616        };
4617        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4618        Ok(combine)
4619    }
4620
4621    pub fn refresh_step_grouped_expert_parallel_combine(
4622        &self,
4623        plan: &PreparedStepGroupedExpertParallelGate,
4624        combine: &mut PreparedPeerWeightedRouteCombine,
4625        route_weights: &[f32],
4626    ) -> Result<(), Box<dyn std::error::Error>> {
4627        let output_capacity = combine
4628            .max_tokens
4629            .checked_mul(combine.width)
4630            .ok_or("Step owner-grouped combine output capacity overflow")?;
4631        if !plan.ready
4632            || combine.owners.len() != plan.owners.len()
4633            || combine.peer_devices.len() + 1 != self.ranks.len()
4634            || combine.peer_outputs.len() + 1 != self.ranks.len()
4635            || combine.width != plan.input_width
4636            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4637            || combine.max_tokens != plan.max_tokens
4638            || combine.max_pairs != plan.max_pairs
4639            || combine.output.len() < output_capacity
4640            || combine
4641                .peer_outputs
4642                .iter()
4643                .any(|output| output.len() < output_capacity)
4644        {
4645            return Err("Step owner-grouped combine/projection geometry changed".into());
4646        }
4647        if self
4648            .ranks
4649            .iter()
4650            .skip(1)
4651            .zip(&combine.peer_devices)
4652            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4653        {
4654            return Err("Step owner-grouped combine peer devices changed".into());
4655        }
4656        let owner_pairs = plan
4657            .owners
4658            .iter()
4659            .map(|owner| owner.global_pairs.as_slice())
4660            .collect::<Vec<_>>();
4661        let shape = validate_weighted_route_combine(
4662            combine.width,
4663            combine.experts_per_token,
4664            combine.max_tokens,
4665            plan.tokens,
4666            &owner_pairs,
4667            route_weights,
4668        )?;
4669        if shape.max_pairs != combine.max_pairs {
4670            return Err("Step owner-grouped combine capacity changed during refresh".into());
4671        }
4672        let metadata = owner_pairs
4673            .iter()
4674            .map(|pairs| {
4675                let token_rows = pairs
4676                    .iter()
4677                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4678                    .collect::<Vec<_>>();
4679                let slots = pairs
4680                    .iter()
4681                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4682                    .collect::<Vec<_>>();
4683                let weights = pairs
4684                    .iter()
4685                    .map(|&pair| route_weights[pair])
4686                    .collect::<Vec<_>>();
4687                (token_rows, slots, weights)
4688            })
4689            .collect::<Vec<_>>();
4690
4691        combine.ready = false;
4692        combine.output_generation = None;
4693        combine.broadcast_generation = None;
4694        let root = self
4695            .ranks
4696            .first()
4697            .ok_or("Step owner-grouped combine has no root rank")?;
4698        let _main = root.gpu.enter_main()?;
4699        if root.ctx().ordinal() != combine.root_device {
4700            return Err(format!(
4701                "Step owner-grouped combine root device changed {} != {}",
4702                root.ctx().ordinal(),
4703                combine.root_device
4704            )
4705            .into());
4706        }
4707        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4708            if token_rows.is_empty() {
4709                owner.active_pairs = 0;
4710                continue;
4711            }
4712            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4713            root.htod_i32_into(&mut owner.slots, &slots)?;
4714            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4715            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4716            owner.active_pairs = token_rows.len();
4717        }
4718        combine.tokens = plan.tokens;
4719        combine.pairs = shape.pairs;
4720        combine.projection_generation = plan.generation;
4721        combine.ready = true;
4722        Ok(())
4723    }
4724
4725    pub fn execute_step_grouped_expert_parallel_combine(
4726        &self,
4727        plan: &PreparedStepGroupedExpertParallelGate,
4728        combine: &mut PreparedPeerWeightedRouteCombine,
4729    ) -> Result<(), Box<dyn std::error::Error>> {
4730        if !plan.ready
4731            || plan.executed_generation != Some(plan.generation)
4732            || !combine.ready
4733            || combine.tokens != plan.tokens
4734            || combine.pairs != plan.pairs
4735            || combine.width != plan.input_width
4736            || combine.owners.len() != plan.owners.len()
4737            || combine.projection_generation != plan.generation
4738        {
4739            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4740        }
4741        combine.output_generation = None;
4742        combine.broadcast_generation = None;
4743        for owner in &plan.owners {
4744            if owner.rank == 0 || owner.global_pairs.is_empty() {
4745                continue;
4746            }
4747            let engine = &self.ranks[owner.rank];
4748            let _main = engine.gpu.enter_main()?;
4749            engine.stream().synchronize()?;
4750        }
4751        let root = self
4752            .ranks
4753            .first()
4754            .ok_or("Step owner-grouped combine has no root rank")?;
4755        let _main = root.gpu.enter_main()?;
4756        if root.ctx().ordinal() != combine.root_device {
4757            return Err("Step owner-grouped combine is not resident on the root device".into());
4758        }
4759        for (index, owner) in plan.owners.iter().enumerate() {
4760            let metadata = &combine.owners[index];
4761            if owner.global_pairs.len() != metadata.active_pairs {
4762                return Err(format!(
4763                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4764                    owner.global_pairs.len(),
4765                    metadata.active_pairs
4766                )
4767                .into());
4768            }
4769            if metadata.active_pairs == 0 {
4770                continue;
4771            }
4772            let values = metadata
4773                .active_pairs
4774                .checked_mul(combine.width)
4775                .ok_or("Step owner-grouped combine peer value count overflow")?;
4776            if owner.rank == 0 {
4777                root.scatter_slot(
4778                    owner.down_workspace.output(),
4779                    &metadata.token_rows,
4780                    &metadata.slots,
4781                    &metadata.weights,
4782                    &mut combine.slots,
4783                    &mut combine.weights,
4784                    combine.width,
4785                    combine.experts_per_token,
4786                    metadata.active_pairs,
4787                )?;
4788            } else {
4789                let source = owner.down_workspace.output().slice(0..values);
4790                let mut destination = combine.peer_staging.slice_mut(0..values);
4791                root.stream().memcpy_dtod(&source, &mut destination)?;
4792                root.scatter_slot(
4793                    &combine.peer_staging,
4794                    &metadata.token_rows,
4795                    &metadata.slots,
4796                    &metadata.weights,
4797                    &mut combine.slots,
4798                    &mut combine.weights,
4799                    combine.width,
4800                    combine.experts_per_token,
4801                    metadata.active_pairs,
4802                )?;
4803            }
4804        }
4805        root.reduce_slots_host(
4806            &combine.slots,
4807            &combine.weights,
4808            &mut combine.output,
4809            combine.width,
4810            combine.experts_per_token,
4811            combine.tokens,
4812        )?;
4813        combine.output_generation = Some(plan.generation);
4814        Ok(())
4815    }
4816
4817    pub fn collect_step_grouped_expert_parallel_combine(
4818        &self,
4819        plan: &PreparedStepGroupedExpertParallelGate,
4820        combine: &PreparedPeerWeightedRouteCombine,
4821    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4822        if !plan.ready
4823            || combine.output_generation != Some(plan.generation)
4824            || combine.projection_generation != plan.generation
4825        {
4826            return Err("Step owner-grouped combine output is stale or has not executed".into());
4827        }
4828        let root = self
4829            .ranks
4830            .first()
4831            .ok_or("Step owner-grouped combine has no root rank")?;
4832        let _main = root.gpu.enter_main()?;
4833        if root.ctx().ordinal() != combine.root_device {
4834            return Err("Step owner-grouped combine is not resident on the root device".into());
4835        }
4836        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4837    }
4838
4839    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4840    ///
4841    /// The persistent combine buffer remains reusable by the next route generation; the returned
4842    /// allocation follows the serving runtime's ordinary transient-output ownership.
4843    pub fn copy_step_grouped_expert_parallel_combine_root(
4844        &self,
4845        plan: &PreparedStepGroupedExpertParallelGate,
4846        combine: &PreparedPeerWeightedRouteCombine,
4847        destination: &Engine,
4848    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4849        if !plan.ready
4850            || combine.output_generation != Some(plan.generation)
4851            || combine.projection_generation != plan.generation
4852        {
4853            return Err("Step owner-grouped combine output is stale or has not executed".into());
4854        }
4855        let root = self
4856            .ranks
4857            .first()
4858            .ok_or("Step owner-grouped combine has no root rank")?;
4859        if root.ctx().ordinal() != combine.root_device
4860            || destination.ctx().ordinal() != combine.root_device
4861        {
4862            return Err(format!(
4863                "Step owner-grouped combine root/destination devices {}/{} != {}",
4864                root.ctx().ordinal(),
4865                destination.ctx().ordinal(),
4866                combine.root_device,
4867            )
4868            .into());
4869        }
4870        let values = combine
4871            .tokens
4872            .checked_mul(combine.width)
4873            .ok_or("Step owner-grouped combine copy size overflow")?;
4874        {
4875            let _main = root.gpu.enter_main()?;
4876            root.stream().synchronize()?;
4877        }
4878        let _main = destination.gpu.enter_main()?;
4879        let mut output = destination.uninit(values)?;
4880        destination
4881            .stream()
4882            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4883        Ok(output)
4884    }
4885
4886    pub fn broadcast_step_grouped_expert_parallel_combine(
4887        &self,
4888        plan: &PreparedStepGroupedExpertParallelGate,
4889        combine: &mut PreparedPeerWeightedRouteCombine,
4890    ) -> Result<(), Box<dyn std::error::Error>> {
4891        if !plan.ready
4892            || combine.output_generation != Some(plan.generation)
4893            || combine.projection_generation != plan.generation
4894            || combine.peer_devices.len() + 1 != self.ranks.len()
4895            || combine.peer_outputs.len() + 1 != self.ranks.len()
4896        {
4897            return Err("Step owner-grouped combine output cannot be broadcast".into());
4898        }
4899        combine.broadcast_generation = None;
4900        let values = combine
4901            .tokens
4902            .checked_mul(combine.width)
4903            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4904        {
4905            let root = self
4906                .ranks
4907                .first()
4908                .ok_or("Step owner-grouped combine has no root rank")?;
4909            let _main = root.gpu.enter_main()?;
4910            if root.ctx().ordinal() != combine.root_device {
4911                return Err("Step owner-grouped combine root device changed".into());
4912            }
4913            root.stream().synchronize()?;
4914        }
4915        let source = &combine.output;
4916        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4917            let engine = &self.ranks[index + 1];
4918            let _main = engine.gpu.enter_main()?;
4919            if engine.ctx().ordinal() != combine.peer_devices[index] {
4920                return Err(format!(
4921                    "Step owner-grouped combine peer {} device changed",
4922                    index + 1
4923                )
4924                .into());
4925            }
4926            let mut destination = destination_buffer.slice_mut(0..values);
4927            engine
4928                .stream()
4929                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4930        }
4931        combine.broadcast_generation = Some(plan.generation);
4932        Ok(())
4933    }
4934
4935    pub fn collect_step_grouped_expert_parallel_broadcast(
4936        &self,
4937        plan: &PreparedStepGroupedExpertParallelGate,
4938        combine: &PreparedPeerWeightedRouteCombine,
4939    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4940        if !plan.ready
4941            || combine.output_generation != Some(plan.generation)
4942            || combine.broadcast_generation != Some(plan.generation)
4943            || combine.peer_outputs.len() + 1 != self.ranks.len()
4944        {
4945            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4946        }
4947        let values = combine
4948            .tokens
4949            .checked_mul(combine.width)
4950            .ok_or("Step owner-grouped combine collection size overflow")?;
4951        let mut outputs = Vec::with_capacity(self.ranks.len());
4952        {
4953            let root = &self.ranks[0];
4954            let _main = root.gpu.enter_main()?;
4955            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4956        }
4957        for (index, output) in combine.peer_outputs.iter().enumerate() {
4958            let engine = &self.ranks[index + 1];
4959            let _main = engine.gpu.enter_main()?;
4960            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4961        }
4962        Ok(outputs)
4963    }
4964
4965    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4966    pub fn finish_step_grouped_expert_parallel_layer(
4967        &self,
4968        plan: &PreparedStepGroupedExpertParallelGate,
4969        combine: &PreparedPeerWeightedRouteCombine,
4970        shared: &ResidentReplicatedDeviceRows,
4971        residual: &ResidentReplicatedDeviceRows,
4972    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4973        validate_replicated_device_rows(&self.ranks, shared)?;
4974        validate_replicated_device_rows(&self.ranks, residual)?;
4975        if !plan.ready
4976            || plan.executed_generation != Some(plan.generation)
4977            || combine.output_generation != Some(plan.generation)
4978            || combine.broadcast_generation != Some(plan.generation)
4979            || combine.projection_generation != plan.generation
4980            || combine.peer_outputs.len() + 1 != self.ranks.len()
4981            || shared.tokens != combine.tokens
4982            || residual.tokens != combine.tokens
4983            || shared.width != combine.width
4984            || residual.width != combine.width
4985        {
4986            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4987        }
4988        let values = combine
4989            .tokens
4990            .checked_mul(combine.width)
4991            .ok_or("Step full-layer output size overflow")?;
4992        let mut ranks = Vec::with_capacity(self.ranks.len());
4993        for rank in 0..self.ranks.len() {
4994            let engine = &self.ranks[rank];
4995            let _main = engine.gpu.enter_main()?;
4996            let routed = if rank == 0 {
4997                &combine.output
4998            } else {
4999                &combine.peer_outputs[rank - 1]
5000            };
5001            let mut ffn = engine.uninit(values)?;
5002            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
5003            let mut output = engine.uninit(values)?;
5004            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
5005            ranks.push(output);
5006        }
5007        Ok(ResidentReplicatedDeviceRows {
5008            ranks,
5009            tokens: combine.tokens,
5010            width: combine.width,
5011        })
5012    }
5013
5014    pub fn run_step_grouped_expert_parallel_combine(
5015        &self,
5016        plan: &PreparedStepGroupedExpertParallelGate,
5017        combine: &mut PreparedPeerWeightedRouteCombine,
5018    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5019        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
5020        self.collect_step_grouped_expert_parallel_combine(plan, combine)
5021    }
5022
5023    pub fn upload_tensor_parallel(
5024        &self,
5025        gate: E4m3ExpertBank<'_>,
5026        up: E4m3ExpertBank<'_>,
5027        down: E4m3ExpertBank<'_>,
5028    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
5029        gate.validate()?;
5030        up.validate()?;
5031        down.validate()?;
5032        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
5033            return Err("TP gate/up/down expert counts differ".into());
5034        }
5035        if gate.in_features != up.in_features || gate.out_features != up.out_features {
5036            return Err("TP gate/up dimensions differ".into());
5037        }
5038        if down.in_features != gate.out_features || down.out_features != gate.in_features {
5039            return Err(format!(
5040                "TP down {}x{} does not invert gate/up {}x{}",
5041                down.out_features, down.in_features, gate.out_features, gate.in_features
5042            )
5043            .into());
5044        }
5045        let tp = self.ranks.len();
5046        validate_column_bank_shape(gate, tp)?;
5047        validate_column_bank_shape(up, tp)?;
5048        validate_row_bank_shape(down, tp)?;
5049
5050        let mut gate_ranks = Vec::with_capacity(tp);
5051        let mut up_ranks = Vec::with_capacity(tp);
5052        let mut down_ranks = Vec::with_capacity(tp);
5053        for (rank, engine) in self.ranks.iter().enumerate() {
5054            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
5055            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
5056            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
5057        }
5058        Ok(ResidentTensorParallel {
5059            bank: ResidentTpExpertBank {
5060                gate: gate_ranks,
5061                up: up_ranks,
5062                down: down_ranks,
5063                expert_count: gate.expert_count,
5064                input_width: gate.in_features,
5065                expert_width: gate.out_features,
5066            },
5067        })
5068    }
5069
5070    pub fn run_tensor_parallel_routes(
5071        &self,
5072        experts: &ResidentTensorParallel,
5073        input: &[f32],
5074        tokens: usize,
5075        selected: &[usize],
5076        route_weights: &[f32],
5077        experts_per_token: usize,
5078    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5079        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
5080        validate_activations(input, tokens, experts.bank.input_width)?;
5081        let pairs = tokens
5082            .checked_mul(experts_per_token)
5083            .ok_or("TP route count overflow")?;
5084        if selected.len() != pairs || route_weights.len() != pairs {
5085            return Err(format!(
5086                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
5087                 {experts_per_token} ({pairs})",
5088                selected.len(),
5089                route_weights.len(),
5090            )
5091            .into());
5092        }
5093        if !route_weights.iter().all(|weight| weight.is_finite()) {
5094            return Err("TP route weights contain a non-finite value".into());
5095        }
5096
5097        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
5098        for token in 0..tokens {
5099            let input_row =
5100                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
5101            for slot in 0..experts_per_token {
5102                let pair = token * experts_per_token + slot;
5103                let expert = selected[pair];
5104                if expert >= experts.bank.expert_count {
5105                    return Err(format!(
5106                        "TP selected expert {expert} outside 0..{}",
5107                        experts.bank.expert_count
5108                    )
5109                    .into());
5110                }
5111                let down = if self.native_p2p {
5112                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
5113                } else {
5114                    let gate =
5115                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
5116                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
5117                    let activated: Vec<f32> = gate
5118                        .iter()
5119                        .zip(&up)
5120                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5121                        .collect();
5122                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
5123                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
5124                };
5125                let weight = route_weights[pair];
5126                for (sum, value) in output
5127                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
5128                    .iter_mut()
5129                    .zip(down)
5130                {
5131                    *sum += weight * value;
5132                }
5133            }
5134        }
5135        Ok(output)
5136    }
5137
5138    fn run_column_bank_expert(
5139        &self,
5140        ranks: &[ResidentE4m3ExpertBankRank],
5141        expert: usize,
5142        input: &[f32],
5143    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5144        let local_out = ranks
5145            .first()
5146            .ok_or("TP column bank has no ranks")?
5147            .out_features;
5148        let mut gathered = vec![0.0f32; local_out * ranks.len()];
5149        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5150            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
5151            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
5152        }
5153        Ok(gathered)
5154    }
5155
5156    fn run_row_bank_expert(
5157        &self,
5158        ranks: &[ResidentE4m3ExpertBankRank],
5159        expert: usize,
5160        input: &[f32],
5161    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5162        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
5163        if input.len() != local_in * ranks.len() {
5164            return Err(format!(
5165                "TP row input {} != {} ranks x {local_in}",
5166                input.len(),
5167                ranks.len()
5168            )
5169            .into());
5170        }
5171        let out_features = ranks[0].out_features;
5172        let mut reduced = vec![0.0f32; out_features];
5173        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5174            let blocks = bank
5175                .k_blocks
5176                .ok_or("TP row bank is not packed in native K-block order")?;
5177            if blocks * FP8_BLOCK != local_in {
5178                return Err(format!(
5179                    "TP row bank has {blocks} blocks but local input width is {local_in}"
5180                )
5181                .into());
5182            }
5183            for block in 0..blocks {
5184                let global_start = rank * local_in + block * FP8_BLOCK;
5185                let partial = run_resident_bank_expert_block(
5186                    engine,
5187                    bank,
5188                    expert,
5189                    block,
5190                    &input[global_start..global_start + FP8_BLOCK],
5191                )?;
5192                for (sum, value) in reduced.iter_mut().zip(partial) {
5193                    *sum += value;
5194                }
5195            }
5196        }
5197        Ok(reduced)
5198    }
5199
5200    fn run_tensor_parallel_expert_native(
5201        &self,
5202        bank: &ResidentTpExpertBank,
5203        expert: usize,
5204        input: &[f32],
5205    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5206        if !self.native_p2p || self.ranks.len() < 2 {
5207            return Err("native TP expert execution requires at least two P2P ranks".into());
5208        }
5209        let local_out = bank
5210            .gate
5211            .first()
5212            .ok_or("native TP gate bank has no ranks")?
5213            .out_features;
5214        if local_out * self.ranks.len() != bank.expert_width {
5215            return Err(format!(
5216                "native TP gate shards {}x{local_out} != expert width {}",
5217                self.ranks.len(),
5218                bank.expert_width
5219            )
5220            .into());
5221        }
5222
5223        // The caller's routed input is already host-canonical. Upload once on rank zero, then
5224        // broadcast over peer copies so no other rank receives a host-staged duplicate.
5225        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
5226        let root_input = {
5227            let root = &self.ranks[0];
5228            let _main = root.gpu.enter_main()?;
5229            root.htod(input)?
5230        };
5231        rank_inputs.push(root_input);
5232        for engine in &self.ranks[1..] {
5233            let peer_input = {
5234                let _main = engine.gpu.enter_main()?;
5235                let mut peer_input = engine.uninit(input.len())?;
5236                engine
5237                    .stream()
5238                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
5239                peer_input
5240            };
5241            rank_inputs.push(peer_input);
5242        }
5243
5244        let mut gate_shards = Vec::with_capacity(self.ranks.len());
5245        let mut up_shards = Vec::with_capacity(self.ranks.len());
5246        for rank in 0..self.ranks.len() {
5247            gate_shards.push(run_resident_bank_expert_device(
5248                &self.ranks[rank],
5249                &bank.gate[rank],
5250                expert,
5251                &rank_inputs[rank],
5252                1,
5253            )?);
5254            up_shards.push(run_resident_bank_expert_device(
5255                &self.ranks[rank],
5256                &bank.up[rank],
5257                expert,
5258                &rank_inputs[rank],
5259                1,
5260            )?);
5261        }
5262
5263        // Preserve the established canonical activation program for the first native transport
5264        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
5265        // executes on host. A later device-activation increment must earn its own exactness gate.
5266        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
5267        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
5268        let activated = gate
5269            .iter()
5270            .zip(&up)
5271            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5272            .collect::<Vec<_>>();
5273        debug_assert_eq!(activated.len(), bank.expert_width);
5274
5275        let root_activated = {
5276            let root = &self.ranks[0];
5277            let _main = root.gpu.enter_main()?;
5278            root.htod(&activated)?
5279        };
5280        let mut rank_activated = Vec::with_capacity(self.ranks.len());
5281        for (rank, engine) in self.ranks.iter().enumerate() {
5282            let start = rank * local_out;
5283            let source = root_activated.slice(start..start + local_out);
5284            let local = {
5285                let _main = engine.gpu.enter_main()?;
5286                let mut local = engine.uninit(local_out)?;
5287                engine.stream().memcpy_dtod(&source, &mut local)?;
5288                local
5289            };
5290            rank_activated.push(local);
5291        }
5292
5293        let out_features = bank
5294            .down
5295            .first()
5296            .ok_or("native TP down bank has no ranks")?
5297            .out_features;
5298        let mut reduced = {
5299            let root = &self.ranks[0];
5300            let _main = root.gpu.enter_main()?;
5301            root.htod(&vec![0.0f32; out_features])?
5302        };
5303        let mut remote_partial_keepalive = Vec::new();
5304        for rank in 0..self.ranks.len() {
5305            let down = &bank.down[rank];
5306            let blocks = down
5307                .k_blocks
5308                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
5309            if blocks * FP8_BLOCK != local_out {
5310                return Err(format!(
5311                    "native TP rank {rank} has {blocks} blocks but local activation width is \
5312                     {local_out}"
5313                )
5314                .into());
5315            }
5316            for block in 0..blocks {
5317                let start = block * FP8_BLOCK;
5318                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
5319                let partial = run_resident_bank_expert_block_device(
5320                    &self.ranks[rank],
5321                    down,
5322                    expert,
5323                    block,
5324                    &input_block,
5325                )?;
5326                let root_partial = if rank == 0 {
5327                    partial
5328                } else {
5329                    let root = &self.ranks[0];
5330                    let _main = root.gpu.enter_main()?;
5331                    let mut peer_partial = root.uninit(out_features)?;
5332                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
5333                    remote_partial_keepalive.push(partial);
5334                    peer_partial
5335                };
5336                let next = {
5337                    let root = &self.ranks[0];
5338                    let _main = root.gpu.enter_main()?;
5339                    let mut next = root.uninit(out_features)?;
5340                    root.add(&reduced, &root_partial, &mut next, out_features)?;
5341                    next
5342                };
5343                reduced = next;
5344            }
5345        }
5346        let output = {
5347            let root = &self.ranks[0];
5348            let _main = root.gpu.enter_main()?;
5349            root.dtoh(&reduced)?
5350        };
5351        drop(remote_partial_keepalive);
5352        Ok(output)
5353    }
5354
5355    /// Gather token-major rank-local columns into one canonical root-device matrix.
5356    pub fn gather_native_column_shards_device(
5357        &self,
5358        shards: &[CudaSlice<f32>],
5359        tokens: usize,
5360        local_out: usize,
5361    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5362        let shard_len = tokens
5363            .checked_mul(local_out)
5364            .ok_or("native TP gather shard size overflow")?;
5365        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
5366            return Err("native TP gather shard geometry mismatch".into());
5367        }
5368        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
5369        // the other ranks' streams; without fencing those producers the copy can read a partial
5370        // kernel output.
5371        for engine in &self.ranks[1..] {
5372            let _main = engine.gpu.enter_main()?;
5373            engine.stream().synchronize()?;
5374        }
5375        let root = &self.ranks[0];
5376        let _main = root.gpu.enter_main()?;
5377        let global_out = shards
5378            .len()
5379            .checked_mul(local_out)
5380            .ok_or("native TP gather output width overflow")?;
5381        let gathered_len = tokens
5382            .checked_mul(global_out)
5383            .ok_or("native TP gather output size overflow")?;
5384        let mut gathered = root.uninit(gathered_len)?;
5385        if self.bulk_p2p {
5386            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
5387            if shards.len() > 1 {
5388                let mut staging = root.uninit(shard_len)?;
5389                for (rank, shard) in shards.iter().enumerate().skip(1) {
5390                    root.stream().memcpy_dtod(shard, &mut staging)?;
5391                    root.place_rows_strided(
5392                        &staging,
5393                        &mut gathered,
5394                        local_out,
5395                        tokens,
5396                        global_out,
5397                        rank * local_out,
5398                    )?;
5399                }
5400            }
5401        } else {
5402            for token in 0..tokens {
5403                for (rank, shard) in shards.iter().enumerate() {
5404                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5405                    let start = token * global_out + rank * local_out;
5406                    let mut destination = gathered.slice_mut(start..start + local_out);
5407                    root.stream().memcpy_dtod(&source, &mut destination)?;
5408                }
5409            }
5410        }
5411        Ok(gathered)
5412    }
5413
5414    pub fn gather_native_column_shards(
5415        &self,
5416        shards: &[CudaSlice<f32>],
5417        tokens: usize,
5418        local_out: usize,
5419    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5420        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5421        let root = &self.ranks[0];
5422        let _main = root.gpu.enter_main()?;
5423        root.dtoh(&gathered)
5424    }
5425
5426    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5427        &self.decode_v2
5428    }
5429
5430    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5431    /// return the index of the matching one. Attention geometry varies across the trunk
5432    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5433    /// handful exist per model, never one per layer.
5434    ///
5435    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5436    /// holds per residency class, and only the mirror class has no per-call weight expansion
5437    /// to hide allocation churn behind.
5438    pub(crate) fn decode_v2_ensure(
5439        &self,
5440        e: &Engine,
5441        q_m: &ResidentBf16ColumnParallel,
5442        k_m: &ResidentBf16ColumnParallel,
5443        v_m: &ResidentBf16ColumnParallel,
5444        o_m: &ResidentStepBf16RowParallel,
5445        heads: usize,
5446    ) -> Result<usize, Box<dyn std::error::Error>> {
5447        if self.ranks.len() > 1 && !self.native_p2p {
5448            return Err("step TP decode v2 requires native P2P ranks".into());
5449        }
5450        let ranks = self.ranks.len();
5451        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5452        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5453        // traffic), so bf16 residency is accepted when that door is on.
5454        let fused_door = step_tp_qkv_fused_enabled()?;
5455        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5456            ResidentBf16Weight::F32(_) => true,
5457            ResidentBf16Weight::Bf16(_) => fused_door,
5458        };
5459        for matrix in [q_m, k_m, v_m] {
5460            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5461            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5462                return Err("step TP decode v2 QKV geometry mismatch".into());
5463            }
5464            for rank in &matrix.ranks {
5465                if !arm_ok(&rank.weight) {
5466                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5467                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5468                        .into());
5469                }
5470            }
5471        }
5472        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5473        for blocks in &o_m.ranks {
5474            for block in blocks {
5475                if !arm_ok(&block.weight) {
5476                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5477                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5478                        .into());
5479                }
5480            }
5481        }
5482        if v_m.out_features != k_m.out_features
5483            || o_m.in_features != q_m.out_features
5484            || heads == 0
5485            || heads % ranks != 0
5486        {
5487            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5488        }
5489        let local_q_dim = q_m.out_features / ranks;
5490        let local_kv_dim = k_m.out_features / ranks;
5491        let o_out = o_m.out_features;
5492        let o_block_cols = o_m.canonical_chunk_cols;
5493        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5494        if blocks_per_rank == 0
5495            || o_m
5496                .ranks
5497                .iter()
5498                .any(|blocks| blocks.len() != blocks_per_rank)
5499            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5500        {
5501            return Err("step TP decode v2 O canonical block grid mismatch".into());
5502        }
5503
5504        let mut guard = self
5505            .decode_v2
5506            .lock()
5507            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5508        if let Some(index) = guard.iter().position(|ws| {
5509            ws.local_q_dim == local_q_dim
5510                && ws.local_kv_dim == local_kv_dim
5511                && ws.heads == heads
5512                && ws.o_out == o_out
5513                && ws.o_block_cols == o_block_cols
5514                && ws.blocks_per_rank == blocks_per_rank
5515                && ws.e_device == e.ctx().ordinal()
5516                && ws.q.len() == ranks
5517        }) {
5518            return Ok(index);
5519        }
5520
5521        let mut q_raw = Vec::with_capacity(ranks);
5522        let mut k_raw = Vec::with_capacity(ranks);
5523        let mut v_raw = Vec::with_capacity(ranks);
5524        let mut q = Vec::with_capacity(ranks);
5525        let mut k = Vec::with_capacity(ranks);
5526        let mut pos = Vec::with_capacity(ranks);
5527        let mut gate = Vec::with_capacity(ranks);
5528        let mut attn_out = Vec::with_capacity(ranks);
5529        let mut gated = Vec::with_capacity(ranks);
5530        let mut fuse_ctr = Vec::with_capacity(ranks);
5531        let mut o_partials = Vec::with_capacity(ranks);
5532        let mut ev_rank = Vec::with_capacity(ranks);
5533        let direct_join = oproj_direct_on();
5534        for (rank, engine) in self.ranks.iter().enumerate() {
5535            let _main = engine.gpu.enter_main()?;
5536            q_raw.push(engine.uninit(local_q_dim)?);
5537            k_raw.push(engine.uninit(local_kv_dim)?);
5538            v_raw.push(engine.uninit(local_kv_dim)?);
5539            q.push(engine.uninit(local_q_dim)?);
5540            k.push(engine.uninit(local_kv_dim)?);
5541            pos.push(engine.htod_i32(&[0])?);
5542            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5543            gate.push(engine.uninit(heads / ranks)?);
5544            attn_out.push(engine.uninit(local_q_dim)?);
5545            gated.push(engine.uninit(local_q_dim)?);
5546            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5547            for _ in 0..blocks_per_rank {
5548                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5549                // stores land there over P2P (UVA) and no pull copy is needed.
5550                if direct_join && rank != 0 {
5551                    let root = &self.ranks[0];
5552                    let _root_main = root.gpu.enter_main()?;
5553                    rank_partials.push(root.uninit(o_out)?);
5554                } else {
5555                    rank_partials.push(engine.uninit(o_out)?);
5556                }
5557            }
5558            o_partials.push(rank_partials);
5559            ev_rank.push(engine.ctx().new_event(None)?);
5560        }
5561        let root = &self.ranks[0];
5562        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5563            let _main = root.gpu.enter_main()?;
5564            (
5565                root.uninit(o_out)?,
5566                root.uninit(o_out)?,
5567                root.uninit(o_out)?,
5568                root.htod(&vec![0.0f32; o_out])?,
5569                root.uninit(ranks * local_kv_dim)?,
5570                root.uninit(ranks * local_kv_dim)?,
5571                root.ctx().new_event(None)?,
5572                root.ctx().new_event(None)?,
5573            )
5574        };
5575        let (gate_e, ev_entry) = {
5576            let _main = e.gpu.enter_main()?;
5577            (e.uninit(heads)?, e.ctx().new_event(None)?)
5578        };
5579        let raw_attn_in = Vec::new();
5580        let raw_pos = Vec::new();
5581        guard.push(StepTpDecodeV2Ws {
5582            tcol_q: Vec::new(),
5583            tcol_k: Vec::new(),
5584            tcol_v: Vec::new(),
5585            tcol_g: Vec::new(),
5586            tcol_in: Vec::new(),
5587            tcol_cap: 0,
5588            w8_aq: Vec::new(),
5589            w8_ad: Vec::new(),
5590            w8_in: 0,
5591            w8o_aq: Vec::new(),
5592            w8o_ad: Vec::new(),
5593            w8o_in: 0,
5594            w8t_aq: Vec::new(),
5595            w8t_ad: Vec::new(),
5596            w8t_in: 0,
5597            w8t_oaq: Vec::new(),
5598            w8t_oad: Vec::new(),
5599            w8t_oin: 0,
5600            w8t_cap: 0,
5601            fa2_q: Vec::new(),
5602            fa2_gate: Vec::new(),
5603            fa2_gated: Vec::new(),
5604            fa2_cap: 0,
5605            rope_k_t: Vec::new(),
5606            rope_ctr_t: Vec::new(),
5607            rope_pos_t: Vec::new(),
5608            rows_tabs: Vec::new(),
5609            rows_tab_t: Vec::new(),
5610            rows_tab_shadow: Vec::new(),
5611            tcol_gated: Vec::new(),
5612            tcol_opart: Vec::new(),
5613            tcol_opeer: None,
5614            tcol_omix: None,
5615            tcol_ocap: 0,
5616            q_raw,
5617            k_raw,
5618            v_raw,
5619            q,
5620            k,
5621            pos,
5622            fuse_ctr,
5623            gate,
5624            attn_out,
5625            gated,
5626            o_partials,
5627            ev_rank,
5628            peer_partial,
5629            reduce_a,
5630            reduce_b,
5631            zeros,
5632            k_shadow,
5633            v_shadow,
5634            ev_refresh,
5635            ev_oproj,
5636            gate_e,
5637            attn_in: Vec::new(),
5638            h_stage: None,
5639            pos_stage: None,
5640            raw_h_stage: 0,
5641            raw_pos_stage: 0,
5642            raw_attn_in,
5643            raw_pos,
5644            raw_o_partial1: 0,
5645            raw_peer_partial: 0,
5646            raw_k1: 0,
5647            raw_v1: 0,
5648            raw_k_shadow: 0,
5649            raw_v_shadow: 0,
5650            raw_mixed_stage_e: 0,
5651            raw_reduce_a: 0,
5652            raw_shadow_stage_e: (0, 0),
5653            ev_entry,
5654            e_device: e.ctx().ordinal(),
5655            local_q_dim,
5656            local_kv_dim,
5657            heads,
5658            o_out,
5659            o_block_cols,
5660            blocks_per_rank,
5661        });
5662        eprintln!(
5663            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5664             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5665             residency=persistent ordering=evented performance_claim=false"
5666        );
5667        Ok(guard.len() - 1)
5668    }
5669
5670    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5671    /// all into the persistent workspace, ordered by events instead of host syncs.
5672    ///
5673    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5674    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5675    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5676    /// previous layer's outputs was queued on `e`'s stream before this record).
5677    #[allow(clippy::too_many_arguments)]
5678    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5679    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5680    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5681    /// column vs the t=1 kernel by construction.
5682    #[allow(clippy::too_many_arguments)]
5683    pub fn decode_v2_input_qkv_tcol(
5684        &self,
5685        ws_index: usize,
5686        e: &Engine,
5687        h_t: &CudaSlice<f32>,
5688        t: usize,
5689        q_m: &ResidentBf16ColumnParallel,
5690        k_m: &ResidentBf16ColumnParallel,
5691        v_m: &ResidentBf16ColumnParallel,
5692        gate_shards: Option<StepTpGateShards<'_>>,
5693    ) -> Result<(), Box<dyn std::error::Error>> {
5694        let ranks = self.ranks.len();
5695        let mut guard = self
5696            .decode_v2
5697            .lock()
5698            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5699        let ws = guard
5700            .get_mut(ws_index)
5701            .ok_or("step TP decode v2 workspace index out of range")?;
5702        let in_f = q_m.in_features;
5703        if h_t.len() < t * in_f || t == 0 || t > 32 {
5704            return Err("decode_v2_input_qkv_tcol geometry".into());
5705        }
5706        // Lazily arm the slabs to capacity.
5707        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5708            ws.tcol_q.clear();
5709            ws.tcol_k.clear();
5710            ws.tcol_v.clear();
5711            ws.tcol_g.clear();
5712            ws.tcol_in.clear();
5713            for engine in &self.ranks {
5714                let _m = engine.gpu.enter_main()?;
5715                ws.tcol_q.push(engine.uninit(32 * ws.local_q_dim)?);
5716                ws.tcol_k.push(engine.uninit(32 * ws.local_kv_dim)?);
5717                ws.tcol_v.push(engine.uninit(32 * ws.local_kv_dim)?);
5718                ws.tcol_g
5719                    .push(engine.uninit(32 * (ws.heads / ranks).max(1))?);
5720                ws.tcol_in.push(engine.uninit(32 * in_f)?);
5721            }
5722            ws.tcol_cap = 32;
5723        }
5724        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5725        use cudarc::driver::DevicePtr;
5726        let raw_src = {
5727            let _main = e.gpu.enter_main()?;
5728            let stream = e.stream();
5729            let (p, _g) = h_t.device_ptr(&stream);
5730            ws.ev_entry.record(&stream)?;
5731            p as u64
5732        };
5733        for rank in 0..ranks {
5734            let engine = &self.ranks[rank];
5735            let _main = engine.gpu.enter_main()?;
5736            engine.stream().wait(&ws.ev_entry)?;
5737            let raw_dst = {
5738                let stream = engine.stream();
5739                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5740                p as u64
5741            };
5742            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5743            let out_g = match &gate_shards {
5744                Some(_) => ws.heads / ranks,
5745                None => 0,
5746            };
5747            match (
5748                &q_m.ranks[rank].weight,
5749                &k_m.ranks[rank].weight,
5750                &v_m.ranks[rank].weight,
5751            ) {
5752                (
5753                    ResidentBf16Weight::Bf16(wq),
5754                    ResidentBf16Weight::Bf16(wk),
5755                    ResidentBf16Weight::Bf16(wv),
5756                ) => {
5757                    let wg = match &gate_shards {
5758                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5759                        Some(StepTpGateShards::F32(_)) => {
5760                            return Err(
5761                                "tcol verify: gate shard class does not match bf16 QKV".into()
5762                            );
5763                        }
5764                        None => wq,
5765                    };
5766                    let StepTpDecodeV2Ws {
5767                        tcol_q,
5768                        tcol_k,
5769                        tcol_v,
5770                        tcol_g,
5771                        tcol_in,
5772                        local_q_dim,
5773                        local_kv_dim,
5774                        w8t_aq,
5775                        w8t_ad,
5776                        w8t_in,
5777                        w8t_cap,
5778                        ..
5779                    } = &mut *ws;
5780                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5781                    // column — separates driver bugs from tcol-kernel bugs.
5782                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5783                    let refk = *REFK
5784                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5785                    if refk {
5786                        let lq = *local_q_dim;
5787                        let lkv = *local_kv_dim;
5788                        let mut hrow = engine.uninit(in_f)?;
5789                        let mut qr = engine.uninit(lq)?;
5790                        let mut kr = engine.uninit(lkv)?;
5791                        let mut vr = engine.uninit(lkv)?;
5792                        let mut gr = engine.uninit(out_g.max(1))?;
5793                        for c in 0..t {
5794                            {
5795                                let mut dst = hrow.slice_mut(0..in_f);
5796                                engine.stream().memcpy_dtod(
5797                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5798                                    &mut dst,
5799                                )?;
5800                            }
5801                            engine.matvec_bf16_qkvg_into(
5802                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5803                                lq, lkv, out_g,
5804                            )?;
5805                            let stream = engine.stream();
5806                            {
5807                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5808                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5809                            }
5810                            {
5811                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5812                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5813                            }
5814                            {
5815                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5816                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5817                            }
5818                            if out_g > 0 {
5819                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5820                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5821                            }
5822                        }
5823                    } else if crate::step_tp_w8_on()
5824                        && q_m.ranks[rank].q8.is_some()
5825                        && k_m.ranks[rank].q8.is_some()
5826                        && v_m.ranks[rank].q8.is_some()
5827                        && in_f % 32 == 0
5828                    {
5829                        // MEMRA_STEP_TP_W8 on the VERIFY walk. nsys put the bf16 tcol QKV at
5830                        // 12.3% of spec GPU time and the bf16 tcol o_proj at 24.8% — the door
5831                        // had only ever replaced the DECODE kernels, so 37% of the verify still
5832                        // streamed bf16 weights. One q8 launch over all t columns; the gate rows
5833                        // stay bf16 as on the decode side.
5834                        if *w8t_in != in_f || *w8t_cap < t || w8t_aq.len() != ranks {
5835                            w8t_aq.clear();
5836                            w8t_ad.clear();
5837                            for e_rank in &self.ranks {
5838                                let _m = e_rank.gpu.enter_main()?;
5839                                w8t_aq.push(e_rank.alloc_i8_uninit(32 * in_f)?);
5840                                w8t_ad.push(e_rank.alloc_uninit::<f32>(32 * (in_f / 32))?);
5841                            }
5842                            *w8t_in = in_f;
5843                            *w8t_cap = 32;
5844                        }
5845                        engine.quantize_q8_1_into(
5846                            &tcol_in[rank],
5847                            t,
5848                            in_f,
5849                            &mut w8t_aq[rank],
5850                            &mut w8t_ad[rank],
5851                        )?;
5852                        engine.qmatvec_q8_0_qkv_rp_t_into(
5853                            q_m.ranks[rank].q8.as_ref().unwrap(),
5854                            k_m.ranks[rank].q8.as_ref().unwrap(),
5855                            v_m.ranks[rank].q8.as_ref().unwrap(),
5856                            &w8t_aq[rank],
5857                            &w8t_ad[rank],
5858                            &mut tcol_q[rank],
5859                            &mut tcol_k[rank],
5860                            &mut tcol_v[rank],
5861                            in_f,
5862                            *local_q_dim,
5863                            *local_kv_dim,
5864                            t,
5865                        )?;
5866                        if out_g > 0 {
5867                            engine.matvec_bf16_rows_into(
5868                                wg,
5869                                &tcol_in[rank],
5870                                &mut tcol_g[rank],
5871                                in_f,
5872                                out_g,
5873                                t,
5874                            )?;
5875                        }
5876                    } else {
5877                        engine.matvec_bf16_qkvg_tcol_into(
5878                            wq,
5879                            wk,
5880                            wv,
5881                            wg,
5882                            &tcol_in[rank],
5883                            &mut tcol_q[rank],
5884                            &mut tcol_k[rank],
5885                            &mut tcol_v[rank],
5886                            &mut tcol_g[rank],
5887                            in_f,
5888                            *local_q_dim,
5889                            *local_kv_dim,
5890                            out_g,
5891                            t,
5892                        )?;
5893                    }
5894                }
5895                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5896            }
5897        }
5898        Ok(())
5899    }
5900
5901    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5902    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5903    /// skipped — so it requires the same doors that arm dictate that finish shape.
5904    pub(crate) fn decode_v2_oproj_tcol_eligible(
5905        &self,
5906        ws: &StepTpDecodeV2Ws,
5907        o_m: &ResidentStepBf16RowParallel,
5908    ) -> bool {
5909        self.ranks.len() == 2
5910            && ws.blocks_per_rank == 4
5911            && step_tp_qkv_fused_enabled().unwrap_or(false)
5912            && no_local_shadow_on()
5913            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5914            && o_m
5915                .ranks
5916                .iter()
5917                .flatten()
5918                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5919    }
5920
5921    /// MEMRA_SPEC_FA2 stash: copy this column's per-rank post-rope q and gate rows into
5922    /// the fa2 slabs (rank-stream ordered behind the rope/append that produced them), and
5923    /// give `e` the same anti-dependency wait the skipped finish provided (next column's
5924    /// h/pos re-staging must not overtake this column's rank pulls).
5925    pub(crate) fn decode_v2_stash_fa2(
5926        &self,
5927        ws: &mut StepTpDecodeV2Ws,
5928        e: &Engine,
5929        col: usize,
5930    ) -> Result<(), Box<dyn std::error::Error>> {
5931        let ranks = self.ranks.len();
5932        if col >= 32 {
5933            return Err("decode_v2_stash_fa2 column out of range".into());
5934        }
5935        let lq = ws.local_q_dim;
5936        let lg = (ws.heads / ranks).max(1);
5937        if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks || ws.rows_tab_t.len() != ranks {
5938            ws.fa2_q.clear();
5939            ws.fa2_gate.clear();
5940            ws.fa2_gated.clear();
5941            ws.rope_k_t.clear();
5942            ws.rope_ctr_t.clear();
5943            ws.rope_pos_t.clear();
5944            ws.rows_tab_t.clear();
5945            for engine in &self.ranks {
5946                let _m = engine.gpu.enter_main()?;
5947                ws.fa2_q.push(engine.uninit(32 * lq)?);
5948                ws.fa2_gate.push(engine.uninit(32 * lg)?);
5949                ws.fa2_gated.push(engine.uninit(32 * lq)?);
5950                ws.rope_k_t.push(engine.uninit(32 * ws.local_kv_dim)?);
5951                ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
5952                ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
5953                ws.rows_tab_t
5954                    .push(engine.stream().clone_htod(&[0u64; 32 * 6])?);
5955            }
5956            ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
5957            ws.fa2_cap = 32;
5958        }
5959        for rank in 0..ranks {
5960            let engine = &self.ranks[rank];
5961            let _main = engine.gpu.enter_main()?;
5962            {
5963                let mut dst = ws.fa2_q[rank].slice_mut(col * lq..(col + 1) * lq);
5964                engine
5965                    .stream()
5966                    .memcpy_dtod(&ws.q[rank].slice(0..lq), &mut dst)?;
5967            }
5968            {
5969                let mut dst = ws.fa2_gate[rank].slice_mut(col * lg..(col + 1) * lg);
5970                engine
5971                    .stream()
5972                    .memcpy_dtod(&ws.gate[rank].slice(0..lg), &mut dst)?;
5973            }
5974            ws.ev_rank[rank].record(&engine.stream())?;
5975        }
5976        {
5977            let _main = e.gpu.enter_main()?;
5978            for ev in ws.ev_rank.iter() {
5979                e.stream().wait(ev)?;
5980            }
5981        }
5982        Ok(())
5983    }
5984
5985    /// MEMRA_SPEC_FA2 join: after BOTH verify columns stashed (their appends landed in
5986    /// rank-stream order), run ONE fa_decode_dcw2 per rank over the shared KV stream —
5987    /// two query rows, per-row causal bounds, per-row combine+gate — then land the two
5988    /// gated rows in the o-tcol slabs and reuse the weight-amortized o_proj join.
5989    /// Returns the [2, o_out] `mixed` slab on `e`. The caller's precheck enforced the
5990    /// equal-partition guard (boundary rounds never arm the defer).
5991    #[allow(clippy::too_many_arguments)]
5992    pub(crate) fn decode_v2_spec_fa2_join(
5993        &self,
5994        ws_index: usize,
5995        e: &Engine,
5996        o_m: &ResidentStepBf16RowParallel,
5997        kv: &ResidentTpKvCache,
5998        head_dim: usize,
5999        window: usize,
6000        bucket_max: usize,
6001        scale: f32,
6002    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6003        let ranks = self.ranks.len();
6004        // Engagement receipt: a vacuous gate (precheck never passing) must be visible.
6005        static ONCE: std::sync::Once = std::sync::Once::new();
6006        ONCE.call_once(|| eprintln!("[spec-fa2] joined T=2 attention ENGAGED"));
6007        {
6008            let mut guard = self
6009                .decode_v2
6010                .lock()
6011                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6012            let ws = guard
6013                .get_mut(ws_index)
6014                .ok_or("step TP decode v2 workspace index out of range")?;
6015            if ws.fa2_cap < 2 || ws.fa2_q.len() != ranks {
6016                return Err("spec fa2 join without stashed columns".into());
6017            }
6018            let lq = ws.local_q_dim;
6019            let local_heads = (ws.heads / ranks).max(1);
6020            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6021            let capacity = kv.physical_capacity();
6022            let (k_tok_bytes, v_tok_bytes) = (kv.k_tok_bytes(), kv.v_tok_bytes());
6023            // Arm the o-tcol slabs if the oproj door never ran this boot (same shapes).
6024            if ws.tcol_ocap < 2 || ws.tcol_gated.len() != ranks {
6025                ws.tcol_gated.clear();
6026                ws.tcol_opart.clear();
6027                for engine in &self.ranks {
6028                    let _m = engine.gpu.enter_main()?;
6029                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6030                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6031                }
6032                let root = &self.ranks[0];
6033                let _m = root.gpu.enter_main()?;
6034                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6035                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6036                ws.tcol_ocap = 32;
6037            }
6038            for rank in 0..ranks {
6039                let engine = &self.ranks[rank];
6040                let _main = engine.gpu.enter_main()?;
6041                let rank_cache = kv
6042                    .rank(rank)
6043                    .ok_or("spec fa2 join lost its KV cache rank")?;
6044                let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes);
6045                let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes);
6046                {
6047                    let StepTpDecodeV2Ws {
6048                        fa2_q,
6049                        fa2_gate,
6050                        fa2_gated,
6051                        ..
6052                    } = &mut *ws;
6053                    engine.fa_decode_dcw2(
6054                        &fa2_q[rank],
6055                        &k_ring,
6056                        &v_ring,
6057                        &mut fa2_gated[rank],
6058                        head_dim,
6059                        local_heads,
6060                        local_kv_heads,
6061                        rank_cache.len_d(),
6062                        rank_cache.base_d(),
6063                        window,
6064                        bucket_max,
6065                        scale,
6066                        k_tok_bytes,
6067                        v_tok_bytes,
6068                        &fa2_gate[rank],
6069                    )?;
6070                }
6071                // Both gated rows are contiguous [2, lq] — exactly columns 0..2 of the
6072                // o-tcol slab layout. One dtod, in rank-stream order behind the fa.
6073                let StepTpDecodeV2Ws {
6074                    fa2_gated,
6075                    tcol_gated,
6076                    ..
6077                } = &mut *ws;
6078                let mut dst = tcol_gated[rank].slice_mut(0..2 * lq);
6079                engine
6080                    .stream()
6081                    .memcpy_dtod(&fa2_gated[rank].slice(0..2 * lq), &mut dst)?;
6082            }
6083        }
6084        self.decode_v2_oproj_tcol(ws_index, e, o_m, 2)
6085    }
6086
6087    /// FULL T-ROW ATTENTION PASS over per-row session tables (batched serving): reads
6088    /// the tcol raw-projection slabs, runs ONE rope/append rows launch + ONE fa rows
6089    /// launch + ONE combine per rank (gate straight from the tcol gate slab), then the
6090    /// o_proj tcol join — the whole per-row attention loop in 3 launches/rank/layer.
6091    /// Per-(row, head) programs are the t=1 kernels verbatim; each row appends to and
6092    /// attends its OWN session. `session_parts[rank][row]` = {k_plane, v_plane, len_ptr,
6093    /// base_ptr}; `tab_keys[rank]` keys the per-rank combined-table cache (caller folds
6094    /// layer + session-set + base-arming into it); `stage_pos` stages the position slab
6095    /// (positions are constant across layers within a tick — stage on the first layer).
6096    #[allow(clippy::too_many_arguments)]
6097    pub(crate) fn decode_v2_rope_fa_rows(
6098        &self,
6099        ws_index: usize,
6100        e: &Engine,
6101        o_m: &ResidentStepBf16RowParallel,
6102        session_parts: &[Vec<[u64; 4]>],
6103        tab_keys: &[u64],
6104        positions: &[i32],
6105        stage_pos: bool,
6106        same_session: bool,
6107        q_norms: &[CudaSlice<f32>],
6108        k_norms: &[CudaSlice<f32>],
6109        rope_freqs: &[Option<&crate::CudaSlice<f32>>],
6110        t: usize,
6111        head_dim: usize,
6112        n_rot: usize,
6113        window: usize,
6114        max_ns: usize,
6115        scale: f32,
6116        k_tok_bytes: usize,
6117        v_tok_bytes: usize,
6118        eps: f32,
6119        rope_base: f32,
6120    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6121        use cudarc::driver::DevicePtr;
6122        let ranks = self.ranks.len();
6123        if session_parts.len() != ranks || tab_keys.len() != ranks || positions.len() < t {
6124            return Err("rope fa rows geometry".into());
6125        }
6126        {
6127            let mut guard = self
6128                .decode_v2
6129                .lock()
6130                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6131            let ws = guard
6132                .get_mut(ws_index)
6133                .ok_or("step TP decode v2 workspace index out of range")?;
6134            if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
6135                return Err("rope fa rows without tcol slabs".into());
6136            }
6137            let lq = ws.local_q_dim;
6138            let lkv = ws.local_kv_dim;
6139            let lg = (ws.heads / ranks).max(1);
6140            let local_heads = (ws.heads / ranks).max(1);
6141            let local_kv_heads = (lkv / head_dim).max(1);
6142            // Arm the fa2/rope slabs (shared with the stash path).
6143            if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks || ws.rows_tab_t.len() != ranks {
6144                ws.fa2_q.clear();
6145                ws.fa2_gate.clear();
6146                ws.fa2_gated.clear();
6147                ws.rope_k_t.clear();
6148                ws.rope_ctr_t.clear();
6149                ws.rope_pos_t.clear();
6150                ws.rows_tab_t.clear();
6151                for engine in &self.ranks {
6152                    let _m = engine.gpu.enter_main()?;
6153                    ws.fa2_q.push(engine.uninit(32 * lq)?);
6154                    ws.fa2_gate.push(engine.uninit(32 * lg)?);
6155                    ws.fa2_gated.push(engine.uninit(32 * lq)?);
6156                    ws.rope_k_t.push(engine.uninit(32 * lkv)?);
6157                    ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
6158                    ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
6159                    ws.rows_tab_t
6160                        .push(engine.stream().clone_htod(&[0u64; 32 * 6])?);
6161                }
6162                ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
6163                ws.fa2_cap = 32;
6164            }
6165            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6166                ws.tcol_gated.clear();
6167                ws.tcol_opart.clear();
6168                for engine in &self.ranks {
6169                    let _m = engine.gpu.enter_main()?;
6170                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6171                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6172                }
6173                let root = &self.ranks[0];
6174                let _m = root.gpu.enter_main()?;
6175                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6176                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6177                ws.tcol_ocap = 32;
6178            }
6179            for rank in 0..ranks {
6180                let engine = &self.ranks[rank];
6181                let _main = engine.gpu.enter_main()?;
6182                if stage_pos {
6183                    let host: Vec<i32> = positions[..t].to_vec();
6184                    let mut view = ws.rope_pos_t[rank].slice_mut(0..t);
6185                    engine.stream().memcpy_htod(&host, &mut view)?;
6186                }
6187                // Combined 6-word table {k, v, len, base, ctr, back}; ctr = this rank's
6188                // per-row counter slab. Built from the pointers the CALLER just read off
6189                // the live distributed cache, and RESTAGED into a persistent slab before
6190                // every launch (MEMRA_ROWS_TAB_RESTAGE, default ON).
6191                //
6192                // The `rows_tabs` memo this replaces was keyed by a hash of
6193                // (k pointer, base pointer, layer, t) but the table it handed back ALSO
6194                // carried the V and LEN pointers, and nothing invalidated it when a
6195                // session's KV cache was dropped. A later session whose K buffer landed on
6196                // a recycled address therefore hit a dead entry, and
6197                // `qk_norm_rope_append_inc_dcw_rows` WROTE this session's K/V rows through
6198                // the freed V/len pointers it still held while `fa_decode_dcw_rows` read
6199                // them back: a whole non-finite row when the freed pages were re-mapped,
6200                // CUDA_ERROR_ILLEGAL_ADDRESS when they were not. The row-table twin in
6201                // `step35_verify_fa_rows_join` was cured of exactly this in 8c8397e0b2
6202                // ("a process-lifetime map cannot prove allocation generation", Hermes
6203                // `11339f5cd3c132a3`); this fused rope+append+fa path was left out of it,
6204                // and MEMRA_FUSE_ROPE_APPEND=1 makes it the arm that actually runs.
6205                let ctr_base = {
6206                    let s = engine.stream();
6207                    let (p, _g) = ws.rope_ctr_t[rank].device_ptr(&s);
6208                    p as u64
6209                };
6210                let host = rows_tab_host(&session_parts[rank], ctr_base, same_session, t);
6211                // STALE-HIT RECEIPT (MEMRA_ROWS_TAB_STALE_SCAN=1, default OFF): replay the
6212                // retired key against the contents we are about to stage. `engaged` proves
6213                // this path executes at all; `STALE` proves the retired memo would have
6214                // handed a live launch another allocation's pointers, and names which word
6215                // moved. Diagnostic only: it never feeds a kernel.
6216                if rows_tab_stale_scan() {
6217                    if ws.rows_tab_shadow.len() != ranks {
6218                        ws.rows_tab_shadow = (0..ranks).map(|_| Default::default()).collect();
6219                    }
6220                    let n = ROWS_TAB_ENGAGED.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
6221                    if let Some(prev) = ws.rows_tab_shadow[rank].get(&tab_keys[rank]) {
6222                        if prev != &host {
6223                            let words = ["k", "v", "len", "base", "ctr", "back"];
6224                            let moved: Vec<String> = (0..host.len())
6225                                .filter(|&i| prev.get(i) != Some(&host[i]))
6226                                .map(|i| format!("{}[row{}]", words[i % 6], i / 6))
6227                                .collect();
6228                            let stale =
6229                                ROWS_TAB_STALE.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
6230                            eprintln!(
6231                                "[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",
6232                                tab_keys[rank],
6233                                moved.join(",")
6234                            );
6235                        }
6236                    }
6237                    ws.rows_tab_shadow[rank].insert(tab_keys[rank], host.clone());
6238                }
6239                let legacy_memo = !rows_tab_restage_on();
6240                if legacy_memo && !ws.rows_tabs[rank].contains_key(&tab_keys[rank]) {
6241                    let tab = engine.stream().clone_htod(&host)?;
6242                    ws.rows_tabs[rank].insert(tab_keys[rank], tab);
6243                }
6244                if !legacy_memo {
6245                    let mut view = ws.rows_tab_t[rank].slice_mut(0..t * 6);
6246                    engine.stream().memcpy_htod(&host, &mut view)?;
6247                }
6248                let StepTpDecodeV2Ws {
6249                    tcol_q,
6250                    tcol_k,
6251                    tcol_v,
6252                    tcol_g,
6253                    fa2_q,
6254                    fa2_gated,
6255                    rope_k_t,
6256                    rope_pos_t,
6257                    rows_tabs,
6258                    rows_tab_t,
6259                    ..
6260                } = &mut *ws;
6261                let tab = if legacy_memo {
6262                    rows_tabs[rank]
6263                        .get(&tab_keys[rank])
6264                        .ok_or("rows tab memo lost its entry")?
6265                } else {
6266                    &rows_tab_t[rank]
6267                };
6268                engine.qk_norm_rope_append_inc_dcw_rows(
6269                    &tcol_q[rank],
6270                    &tcol_k[rank],
6271                    &tcol_v[rank],
6272                    &q_norms[rank],
6273                    &k_norms[rank],
6274                    &mut fa2_q[rank],
6275                    &mut rope_k_t[rank],
6276                    tab,
6277                    &rope_pos_t[rank],
6278                    same_session,
6279                    t,
6280                    lkv,
6281                    lkv,
6282                    k_tok_bytes,
6283                    v_tok_bytes,
6284                    head_dim,
6285                    n_rot,
6286                    local_heads,
6287                    local_kv_heads,
6288                    eps,
6289                    rope_base,
6290                    1.0,
6291                    rope_freqs[rank],
6292                )?;
6293                engine.fa_decode_dcw_rows(
6294                    &fa2_q[rank],
6295                    tab,
6296                    &mut fa2_gated[rank],
6297                    t,
6298                    head_dim,
6299                    local_heads,
6300                    local_kv_heads,
6301                    window,
6302                    max_ns,
6303                    scale,
6304                    k_tok_bytes,
6305                    v_tok_bytes,
6306                    &tcol_g[rank],
6307                )?;
6308                let StepTpDecodeV2Ws {
6309                    fa2_gated,
6310                    tcol_gated,
6311                    ..
6312                } = &mut *ws;
6313                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6314                engine
6315                    .stream()
6316                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6317            }
6318        }
6319        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6320    }
6321
6322    /// T-ROW fa join over per-row session tables (the per-session distributed-KV
6323    /// primitive): after all t rows stashed q+gate (their appends landed in rank-stream
6324    /// order), ONE fa_decode_dcw_rows per rank walks every row's own ring with its own
6325    /// geometry — bit-identical per row to its per-row launch — then the o_proj tcol
6326    /// join lands the [t, o_out] `mixed` slab on `e`. `tabs[rank]` is the pre-staged
6327    /// device table on that rank.
6328    #[allow(clippy::too_many_arguments)]
6329    pub(crate) fn decode_v2_fa_rows_join(
6330        &self,
6331        ws_index: usize,
6332        e: &Engine,
6333        o_m: &ResidentStepBf16RowParallel,
6334        tabs: &[&crate::CudaSlice<u64>],
6335        t: usize,
6336        head_dim: usize,
6337        window: usize,
6338        max_ns: usize,
6339        scale: f32,
6340        k_tok_bytes: usize,
6341        v_tok_bytes: usize,
6342    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6343        let ranks = self.ranks.len();
6344        if tabs.len() != ranks {
6345            return Err("fa rows join needs one table per rank".into());
6346        }
6347        {
6348            let mut guard = self
6349                .decode_v2
6350                .lock()
6351                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6352            let ws = guard
6353                .get_mut(ws_index)
6354                .ok_or("step TP decode v2 workspace index out of range")?;
6355            if ws.fa2_cap < t || ws.fa2_q.len() != ranks {
6356                return Err("fa rows join without stashed rows".into());
6357            }
6358            let lq = ws.local_q_dim;
6359            let local_heads = (ws.heads / ranks).max(1);
6360            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6361            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6362                ws.tcol_gated.clear();
6363                ws.tcol_opart.clear();
6364                for engine in &self.ranks {
6365                    let _m = engine.gpu.enter_main()?;
6366                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6367                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6368                }
6369                let root = &self.ranks[0];
6370                let _m = root.gpu.enter_main()?;
6371                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6372                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6373                ws.tcol_ocap = 32;
6374            }
6375            for rank in 0..ranks {
6376                let engine = &self.ranks[rank];
6377                let _main = engine.gpu.enter_main()?;
6378                {
6379                    let StepTpDecodeV2Ws {
6380                        fa2_q,
6381                        fa2_gate,
6382                        fa2_gated,
6383                        ..
6384                    } = &mut *ws;
6385                    engine.fa_decode_dcw_rows(
6386                        &fa2_q[rank],
6387                        tabs[rank],
6388                        &mut fa2_gated[rank],
6389                        t,
6390                        head_dim,
6391                        local_heads,
6392                        local_kv_heads,
6393                        window,
6394                        max_ns,
6395                        scale,
6396                        k_tok_bytes,
6397                        v_tok_bytes,
6398                        &fa2_gate[rank],
6399                    )?;
6400                }
6401                let StepTpDecodeV2Ws {
6402                    fa2_gated,
6403                    tcol_gated,
6404                    ..
6405                } = &mut *ws;
6406                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6407                engine
6408                    .stream()
6409                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6410            }
6411        }
6412        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6413    }
6414
6415    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
6416    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
6417    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
6418    /// every column afterwards.
6419    pub(crate) fn decode_v2_stash_gated(
6420        &self,
6421        ws: &mut StepTpDecodeV2Ws,
6422        e: &Engine,
6423        col: usize,
6424    ) -> Result<(), Box<dyn std::error::Error>> {
6425        let ranks = self.ranks.len();
6426        // 32, not 8: the slabs below have been 32 rows since the slab-width fix, and the walk now
6427        // runs chunks up to t=32 (the w=16 arm died here on a guard three widths staler than its
6428        // own allocation, 2026-08-27).
6429        if col >= 32 {
6430            return Err("decode_v2_stash_gated column out of range".into());
6431        }
6432        let lq = ws.local_q_dim;
6433        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
6434            ws.tcol_gated.clear();
6435            ws.tcol_opart.clear();
6436            for engine in &self.ranks {
6437                let _m = engine.gpu.enter_main()?;
6438                ws.tcol_gated.push(engine.uninit(32 * lq)?);
6439                ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6440            }
6441            let root = &self.ranks[0];
6442            let _m = root.gpu.enter_main()?;
6443            ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6444            ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6445            ws.tcol_ocap = 32;
6446        }
6447        for rank in 0..ranks {
6448            let engine = &self.ranks[rank];
6449            let _main = engine.gpu.enter_main()?;
6450            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
6451            engine
6452                .stream()
6453                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
6454            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
6455            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
6456            // Record each rank here and make e wait — same protection, no o_proj work.
6457            ws.ev_rank[rank].record(&engine.stream())?;
6458        }
6459        {
6460            let _main = e.gpu.enter_main()?;
6461            for ev in ws.ev_rank.iter() {
6462                e.stream().wait(ev)?;
6463            }
6464        }
6465        Ok(())
6466    }
6467
6468    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
6469    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
6470    /// partial slab, one elementwise slab add on the root (independent elements — each
6471    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
6472    /// lands on `e`. Returns [t, o_out] on the model engine.
6473    pub(crate) fn decode_v2_oproj_tcol(
6474        &self,
6475        ws_index: usize,
6476        e: &Engine,
6477        o_m: &ResidentStepBf16RowParallel,
6478        t: usize,
6479    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6480        let ranks = self.ranks.len();
6481        let mut guard = self
6482            .decode_v2
6483            .lock()
6484            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6485        let ws = guard
6486            .get_mut(ws_index)
6487            .ok_or("step TP decode v2 workspace index out of range")?;
6488        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 32 || ws.tcol_ocap < t {
6489            return Err("decode_v2_oproj_tcol geometry".into());
6490        }
6491        for rank in 0..ranks {
6492            let engine = &self.ranks[rank];
6493            let _main = engine.gpu.enter_main()?;
6494            let mut weights = Vec::with_capacity(4);
6495            for block in 0..4 {
6496                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6497                    return Err("tcol o_proj requires bf16-resident O blocks".into());
6498                };
6499                weights.push(weight);
6500            }
6501            {
6502                let StepTpDecodeV2Ws {
6503                    tcol_gated,
6504                    tcol_opart,
6505                    local_q_dim,
6506                    o_block_cols,
6507                    o_out,
6508                    w8t_oaq,
6509                    w8t_oad,
6510                    w8t_oin,
6511                    w8t_cap,
6512                    ..
6513                } = &mut *ws;
6514                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
6515                // kernel per column — separates choreography bugs from tcol-kernel bugs.
6516                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6517                let refk = *REFK
6518                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
6519                if refk {
6520                    let lq = *local_q_dim;
6521                    let mut xr = engine.uninit(lq)?;
6522                    let mut yr = engine.uninit(*o_out)?;
6523                    for c in 0..t {
6524                        {
6525                            let mut dst = xr.slice_mut(0..lq);
6526                            engine.stream().memcpy_dtod(
6527                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
6528                                &mut dst,
6529                            )?;
6530                        }
6531                        engine.matvec_bf16_b4_into(
6532                            [weights[0], weights[1], weights[2], weights[3]],
6533                            &xr,
6534                            &mut yr,
6535                            *o_block_cols,
6536                            *o_out,
6537                        )?;
6538                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
6539                        engine
6540                            .stream()
6541                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
6542                    }
6543                } else if crate::step_tp_w8_on()
6544                    && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some())
6545                    && (4 * *o_block_cols) % 32 == 0
6546                {
6547                    // The verify walk's biggest single kernel: bf16 tcol o_proj was 24.8% of
6548                    // spec GPU time. Same planar q8_0 mirrors the decode arm uses, one launch
6549                    // over all t columns.
6550                    let in_f = 4 * *o_block_cols;
6551                    if *w8t_oin != in_f || *w8t_cap < t || w8t_oaq.len() != ranks {
6552                        w8t_oaq.clear();
6553                        w8t_oad.clear();
6554                        for e_rank in &self.ranks {
6555                            let _m = e_rank.gpu.enter_main()?;
6556                            w8t_oaq.push(e_rank.alloc_i8_uninit(32 * in_f)?);
6557                            w8t_oad.push(e_rank.alloc_uninit::<f32>(32 * (in_f / 32))?);
6558                        }
6559                        *w8t_oin = in_f;
6560                        *w8t_cap = (*w8t_cap).max(32);
6561                    }
6562                    engine.quantize_q8_1_into(
6563                        &tcol_gated[rank],
6564                        t,
6565                        in_f,
6566                        &mut w8t_oaq[rank],
6567                        &mut w8t_oad[rank],
6568                    )?;
6569                    engine.qmatvec_q8_0_b4_rp_t_into(
6570                        [
6571                            o_m.ranks[rank][0].q8.as_ref().unwrap(),
6572                            o_m.ranks[rank][1].q8.as_ref().unwrap(),
6573                            o_m.ranks[rank][2].q8.as_ref().unwrap(),
6574                            o_m.ranks[rank][3].q8.as_ref().unwrap(),
6575                        ],
6576                        &w8t_oaq[rank],
6577                        &w8t_oad[rank],
6578                        &mut tcol_opart[rank],
6579                        *o_block_cols,
6580                        *o_out,
6581                        t,
6582                    )?;
6583                } else {
6584                    engine.matvec_bf16_b4_tcol_into(
6585                        [weights[0], weights[1], weights[2], weights[3]],
6586                        &tcol_gated[rank],
6587                        &mut tcol_opart[rank],
6588                        *o_block_cols,
6589                        *o_out,
6590                        t,
6591                    )?;
6592                }
6593            }
6594            if rank != 0 {
6595                ws.ev_rank[rank].record(&engine.stream())?;
6596            }
6597        }
6598        let root = &self.ranks[0];
6599        {
6600            let _main = root.gpu.enter_main()?;
6601            for ev in ws.ev_rank.iter().skip(1) {
6602                root.stream().wait(ev)?;
6603            }
6604            {
6605                let StepTpDecodeV2Ws {
6606                    tcol_opart,
6607                    tcol_opeer,
6608                    tcol_omix,
6609                    o_out,
6610                    ..
6611                } = &mut *ws;
6612                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
6613                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
6614                {
6615                    let mut dst = opeer.slice_mut(0..t * *o_out);
6616                    root.stream()
6617                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
6618                }
6619                // Elementwise over the whole slab: per element identical to the per-column
6620                // direct-join add (independent lanes, same operand values).
6621                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
6622            }
6623            ws.ev_oproj.record(&root.stream())?;
6624        }
6625        let _main = e.gpu.enter_main()?;
6626        e.stream().wait(&ws.ev_oproj)?;
6627        let mut out = e.uninit(t * ws.o_out)?;
6628        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
6629        e.stream().memcpy_dtod(
6630            &omix.slice(0..t * ws.o_out),
6631            &mut out.slice_mut(0..t * ws.o_out),
6632        )?;
6633        Ok(out)
6634    }
6635
6636    pub(crate) fn decode_v2_input_qkv(
6637        &self,
6638        ws: &mut StepTpDecodeV2Ws,
6639        e: &Engine,
6640        h: &CudaSlice<f32>,
6641        pos_d: &CudaSlice<i32>,
6642        gate_raw: Option<&CudaSlice<f32>>,
6643        gate_shards: Option<StepTpGateShards<'_>>,
6644        decode_input: &mut ResidentReplicatedDeviceRows,
6645        q_m: &ResidentBf16ColumnParallel,
6646        k_m: &ResidentBf16ColumnParallel,
6647        v_m: &ResidentBf16ColumnParallel,
6648        q_norm: &[CudaSlice<f32>],
6649        k_norm: &[CudaSlice<f32>],
6650        head_dim: usize,
6651        n_rot: usize,
6652        rope_base: f32,
6653        rope_freqs: &[Option<&CudaSlice<f32>>],
6654        rms_eps: f32,
6655        defer_norm_rope: bool,
6656        tcol_col: Option<usize>,
6657    ) -> Result<(), Box<dyn std::error::Error>> {
6658        let ranks = self.ranks.len();
6659        validate_replicated_device_rows(&self.ranks, decode_input)?;
6660        if decode_input.tokens != 1
6661            || decode_input.width != q_m.in_features
6662            || pos_d.len() != 1
6663            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
6664            || gate_raw.is_none() != gate_shards.is_some()
6665            || gate_shards.as_ref().is_some_and(|shards| match shards {
6666                StepTpGateShards::F32(shards) => shards.len() != ranks,
6667                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
6668            })
6669            || q_norm.len() != ranks
6670            || k_norm.len() != ranks
6671            || rope_freqs.len() != ranks
6672            || e.ctx().ordinal() != ws.e_device
6673        {
6674            return Err("step TP decode v2 input geometry mismatch".into());
6675        }
6676
6677        let qkv_fused = step_tp_qkv_fused_enabled()?;
6678        if gate_shards.is_some() && !qkv_fused {
6679            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
6680        }
6681        let values = decode_input.width;
6682        if h.len() != values {
6683            return Err(format!(
6684                "step TP decode v2 hidden width {} != replicated width {values}",
6685                h.len()
6686            )
6687            .into());
6688        }
6689
6690        if qkv_fused {
6691            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
6692            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
6693            // from the stages on its own stream — exactly the shape graph capture wraps.
6694            if ws.h_stage.is_none() {
6695                use cudarc::driver::DevicePtr;
6696                let _main = e.gpu.enter_main()?;
6697                let h_stage = e.uninit(values)?;
6698                let pos_stage = e.htod_i32(&[0])?;
6699                {
6700                    let stream = e.stream();
6701                    let (hp, _g0) = h_stage.device_ptr(&stream);
6702                    let (pp, _g1) = pos_stage.device_ptr(&stream);
6703                    ws.raw_h_stage = hp as u64;
6704                    ws.raw_pos_stage = pp as u64;
6705                }
6706                ws.h_stage = Some(h_stage);
6707                ws.pos_stage = Some(pos_stage);
6708                for rank in 0..ranks {
6709                    use cudarc::driver::DevicePtr;
6710                    let engine = &self.ranks[rank];
6711                    let _rmain = engine.gpu.enter_main()?;
6712                    let attn_in = engine.uninit(values)?;
6713                    let (dp, pp) = {
6714                        let stream = engine.stream();
6715                        let (dp, _g2) = attn_in.device_ptr(&stream);
6716                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
6717                        (dp as u64, pp as u64)
6718                    };
6719                    ws.raw_attn_in.push(dp);
6720                    ws.raw_pos.push(pp);
6721                    ws.attn_in.push(attn_in);
6722                }
6723                {
6724                    use cudarc::driver::DevicePtr;
6725                    let root = &self.ranks[0];
6726                    let _rmain = root.gpu.enter_main()?;
6727                    let stream = root.stream();
6728                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
6729                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
6730                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
6731                    ws.raw_peer_partial = a as u64;
6732                    ws.raw_k_shadow = b as u64;
6733                    ws.raw_v_shadow = c as u64;
6734                }
6735                {
6736                    use cudarc::driver::DevicePtr;
6737                    let rank1 = &self.ranks[1];
6738                    let _rmain = rank1.gpu.enter_main()?;
6739                    let stream = rank1.stream();
6740                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
6741                    let (b, _g) = ws.k[1].device_ptr(&stream);
6742                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
6743                    ws.raw_o_partial1 = a as u64;
6744                    ws.raw_k1 = b as u64;
6745                    ws.raw_v1 = c as u64;
6746                }
6747            }
6748            {
6749                let _main = e.gpu.enter_main()?;
6750                {
6751                    // (Always staged: a tcol column below the dcw floor falls back to the
6752                    // normal fused arm, which reads h through this stage.)
6753                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
6754                    let mut dst = h_stage.slice_mut(0..values);
6755                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
6756                }
6757                {
6758                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
6759                    let mut dst = pos_stage.slice_mut(0..1);
6760                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
6761                }
6762                ws.ev_entry.record(&e.stream())?;
6763            }
6764            for rank in 0..ranks {
6765                let engine = &self.ranks[rank];
6766                let _main = engine.gpu.enter_main()?;
6767                engine.stream().wait(&ws.ev_entry)?;
6768            }
6769        } else {
6770            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
6771            {
6772                let _main = e.gpu.enter_main()?;
6773                if let Some(gate_raw) = gate_raw {
6774                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
6775                    e.stream()
6776                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
6777                }
6778                ws.ev_entry.record(&e.stream())?;
6779            }
6780            {
6781                let root = &self.ranks[0];
6782                let _main = root.gpu.enter_main()?;
6783                root.stream().wait(&ws.ev_entry)?;
6784                let mut destination = decode_input.ranks[0].slice_mut(0..values);
6785                root.stream()
6786                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
6787                ws.ev_refresh.record(&root.stream())?;
6788            }
6789            for rank in 1..ranks {
6790                let engine = &self.ranks[rank];
6791                let _main = engine.gpu.enter_main()?;
6792                engine.stream().wait(&ws.ev_refresh)?;
6793                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
6794                let mut destination = peer_rows[0].slice_mut(0..values);
6795                engine
6796                    .stream()
6797                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
6798            }
6799        }
6800        for rank in 0..ranks {
6801            self.decode_v2_input_qkv_rank(
6802                ws,
6803                pos_d,
6804                decode_input,
6805                q_m,
6806                k_m,
6807                v_m,
6808                q_norm,
6809                k_norm,
6810                head_dim,
6811                n_rot,
6812                rope_base,
6813                rope_freqs,
6814                rms_eps,
6815                gate_shards.as_ref(),
6816                qkv_fused,
6817                defer_norm_rope,
6818                rank,
6819                tcol_col,
6820            )?;
6821        }
6822        Ok(())
6823    }
6824
6825    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
6826    /// per-device issue unit the whole-token graph captures on that rank's stream.
6827    #[allow(clippy::too_many_arguments)]
6828    pub(crate) fn decode_v2_input_qkv_rank(
6829        &self,
6830        ws: &mut StepTpDecodeV2Ws,
6831        pos_d: &CudaSlice<i32>,
6832        decode_input: &mut ResidentReplicatedDeviceRows,
6833        q_m: &ResidentBf16ColumnParallel,
6834        k_m: &ResidentBf16ColumnParallel,
6835        v_m: &ResidentBf16ColumnParallel,
6836        q_norm: &[CudaSlice<f32>],
6837        k_norm: &[CudaSlice<f32>],
6838        head_dim: usize,
6839        n_rot: usize,
6840        rope_base: f32,
6841        rope_freqs: &[Option<&CudaSlice<f32>>],
6842        rms_eps: f32,
6843        gate_shards: Option<&StepTpGateShards<'_>>,
6844        qkv_fused: bool,
6845        defer_norm_rope: bool,
6846        rank: usize,
6847        tcol_col: Option<usize>,
6848    ) -> Result<(), Box<dyn std::error::Error>> {
6849        let ranks = self.ranks.len();
6850        let local_heads = ws.local_q_dim / head_dim;
6851        let local_kv_heads = ws.local_kv_dim / head_dim;
6852        let engine = &self.ranks[rank];
6853        let _main = engine.gpu.enter_main()?;
6854        let ws_e_device = ws.e_device;
6855        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
6856        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
6857        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
6858        // below exactly as in the t=1 program.
6859        if qkv_fused && tcol_col.is_some() {
6860            let c = tcol_col.expect("checked");
6861            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
6862                return Err("tcol select without precompute".into());
6863            }
6864            // The select skips the matvec but NOT the position: rope/append below still
6865            // read this rank's pos buffer, which only the (skipped) stage path fills for
6866            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
6867            if engine.ctx().ordinal() != ws_e_device {
6868                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6869            }
6870            let StepTpDecodeV2Ws {
6871                tcol_q,
6872                tcol_k,
6873                tcol_v,
6874                tcol_g,
6875                q_raw,
6876                k_raw,
6877                v_raw,
6878                gate,
6879                local_q_dim,
6880                local_kv_dim,
6881                heads,
6882                ..
6883            } = &mut *ws;
6884            let lg = *heads / ranks;
6885            let stream = engine.stream();
6886            {
6887                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
6888                stream.memcpy_dtod(
6889                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
6890                    &mut dst,
6891                )?;
6892            }
6893            {
6894                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
6895                stream.memcpy_dtod(
6896                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6897                    &mut dst,
6898                )?;
6899            }
6900            {
6901                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
6902                stream.memcpy_dtod(
6903                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6904                    &mut dst,
6905                )?;
6906            }
6907            if lg > 0 {
6908                let mut dst = gate[rank].slice_mut(0..lg);
6909                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
6910            }
6911            if !defer_norm_rope {
6912                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
6913                // fall through and recompute this column's QKV from the REAL h row — the
6914                // caller always passes it. The slab copies above are dead stores.
6915            } else {
6916                return Ok(());
6917            }
6918        }
6919        if qkv_fused {
6920            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
6921            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
6922            // SHARING e's device reads the stages directly — same context (probed), ordering
6923            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
6924            let same_dev = engine.ctx().ordinal() == ws.e_device;
6925            if !same_dev {
6926                raw_copy_bytes(
6927                    ws.raw_attn_in[rank],
6928                    ws.raw_h_stage,
6929                    q_m.in_features * 4,
6930                    engine,
6931                )?;
6932                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6933            }
6934            let StepTpDecodeV2Ws {
6935                q_raw,
6936                k_raw,
6937                v_raw,
6938                gate,
6939                gate_e,
6940                attn_in,
6941                h_stage,
6942                heads,
6943                local_q_dim,
6944                local_kv_dim,
6945                w8_aq,
6946                w8_ad,
6947                w8_in,
6948                ..
6949            } = &mut *ws;
6950            let input_ref: &CudaSlice<f32> = if same_dev {
6951                h_stage
6952                    .as_ref()
6953                    .ok_or("step TP decode v2 stage not armed")?
6954            } else {
6955                &attn_in[rank]
6956            };
6957            match (
6958                &q_m.ranks[rank].weight,
6959                &k_m.ranks[rank].weight,
6960                &v_m.ranks[rank].weight,
6961            ) {
6962                (
6963                    ResidentBf16Weight::F32(wq),
6964                    ResidentBf16Weight::F32(wk),
6965                    ResidentBf16Weight::F32(wv),
6966                ) => {
6967                    let (wg, out_g) = match &gate_shards {
6968                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
6969                        Some(StepTpGateShards::Bf16(_)) => {
6970                            return Err("step TP decode v2 gate shard class does not \
6971                                            match the F32 projections"
6972                                .into());
6973                        }
6974                        // out_g = 0: the kernel never reads wg; any resident buffer works.
6975                        None => (&*gate_e, 0),
6976                    };
6977                    engine.matvec_f32_qkv_into(
6978                        wq,
6979                        wk,
6980                        wv,
6981                        wg,
6982                        input_ref,
6983                        &mut q_raw[rank],
6984                        &mut k_raw[rank],
6985                        &mut v_raw[rank],
6986                        &mut gate[rank],
6987                        q_m.in_features,
6988                        *local_q_dim,
6989                        *local_kv_dim,
6990                        out_g,
6991                    )?;
6992                }
6993                (
6994                    ResidentBf16Weight::Bf16(wq),
6995                    ResidentBf16Weight::Bf16(wk),
6996                    ResidentBf16Weight::Bf16(wv),
6997                ) => {
6998                    let (wg, out_g) = match &gate_shards {
6999                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
7000                        Some(StepTpGateShards::F32(_)) => {
7001                            return Err("step TP decode v2 gate shard class does not \
7002                                            match the bf16 projections"
7003                                .into());
7004                        }
7005                        None => (wq, 0),
7006                    };
7007                    // MEMRA_STEP_TP_W8: q8_0 weights + q8_1 activation through mmvq instead of
7008                    // the fused bf16 qkvg. NUMERIC CLASS (int8 dp4a with per-32 scales, not a
7009                    // bf16 fma chain) — argmax-gated, never a bit-tape flip. Q, K and V each
7010                    // get their own launch because the fused kernel has no q8 twin; the gate
7011                    // rows stay bf16 (32 rows, ~0.3 MB, nothing to win and one less class to
7012                    // qualify). Measured motive: 23.0 us bf16 -> 14.0 us q8 at this shape.
7013                    let in_f = q_m.in_features;
7014                    let q8_ready = crate::step_tp_w8_on()
7015                        && q_m.ranks[rank].q8.is_some()
7016                        && k_m.ranks[rank].q8.is_some()
7017                        && v_m.ranks[rank].q8.is_some();
7018                    if q8_ready {
7019                        if *w8_in != in_f || w8_aq.len() != ranks {
7020                            w8_aq.clear();
7021                            w8_ad.clear();
7022                            for e_rank in &self.ranks {
7023                                let _m = e_rank.gpu.enter_main()?;
7024                                w8_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
7025                                w8_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
7026                            }
7027                            *w8_in = in_f;
7028                        }
7029                        engine.quantize_q8_1_into(
7030                            input_ref,
7031                            1,
7032                            in_f,
7033                            &mut w8_aq[rank],
7034                            &mut w8_ad[rank],
7035                        )?;
7036                        // ONE launch over the stacked q/k/v rows. The three-call version
7037                        // measured 79.52 vs 80.72 tok/s — SLOWER than the bf16 fused kernel —
7038                        // because three launches plus the activation quantize cost more than
7039                        // the halved weight bytes save. Bit-identical to those three calls.
7040                        engine.qmatvec_q8_0_qkv_rp_into(
7041                            q_m.ranks[rank].q8.as_ref().unwrap(),
7042                            k_m.ranks[rank].q8.as_ref().unwrap(),
7043                            v_m.ranks[rank].q8.as_ref().unwrap(),
7044                            &w8_aq[rank],
7045                            &w8_ad[rank],
7046                            &mut q_raw[rank],
7047                            &mut k_raw[rank],
7048                            &mut v_raw[rank],
7049                            in_f,
7050                            *local_q_dim,
7051                            *local_kv_dim,
7052                        )?;
7053                        if out_g > 0 {
7054                            engine.matvec_bf16_into(wg, input_ref, &mut gate[rank], in_f, out_g)?;
7055                        }
7056                    } else {
7057                        engine.matvec_bf16_qkvg_into(
7058                            wq,
7059                            wk,
7060                            wv,
7061                            wg,
7062                            input_ref,
7063                            &mut q_raw[rank],
7064                            &mut k_raw[rank],
7065                            &mut v_raw[rank],
7066                            &mut gate[rank],
7067                            q_m.in_features,
7068                            *local_q_dim,
7069                            *local_kv_dim,
7070                            out_g,
7071                        )?;
7072                    }
7073                }
7074                _ => {
7075                    return Err("step TP decode v2 QKV projections mix residency classes".into());
7076                }
7077            }
7078        } else {
7079            for (matrix, local_out, raw) in [
7080                (q_m, ws.local_q_dim, &mut ws.q_raw),
7081                (k_m, ws.local_kv_dim, &mut ws.k_raw),
7082                (v_m, ws.local_kv_dim, &mut ws.v_raw),
7083            ] {
7084                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
7085                    return Err("step TP decode v2 lost its F32 projection residency".into());
7086                };
7087                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
7088                engine.linear_f32_resident_canonical_rows_t1_into(
7089                    &decode_input.ranks[rank],
7090                    values_w,
7091                    &mut raw[rank],
7092                    matrix.in_features,
7093                    local_out,
7094                    chunk_rows,
7095                )?;
7096            }
7097        }
7098        if qkv_fused && defer_norm_rope {
7099            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
7100        } else if qkv_fused {
7101            // Fused norm+rope: one launch; the position comes from the rank-local staged
7102            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
7103            let StepTpDecodeV2Ws {
7104                q_raw,
7105                k_raw,
7106                q,
7107                k,
7108                pos,
7109                pos_stage,
7110                ..
7111            } = &mut *ws;
7112            let same_dev = engine.ctx().ordinal() == ws_e_device;
7113            let pos_ref: &CudaSlice<i32> = if same_dev {
7114                pos_stage
7115                    .as_ref()
7116                    .ok_or("step TP decode v2 pos stage not armed")?
7117            } else {
7118                &pos[rank]
7119            };
7120            engine.qk_norm_rope_into(
7121                &q_raw[rank],
7122                &k_raw[rank],
7123                &q_norm[rank],
7124                &k_norm[rank],
7125                &mut q[rank],
7126                &mut k[rank],
7127                pos_ref,
7128                head_dim,
7129                n_rot,
7130                local_heads,
7131                local_kv_heads,
7132                rms_eps,
7133                rope_base,
7134                1.0,
7135                rope_freqs[rank],
7136            )?;
7137        } else {
7138            engine.rms_norm(
7139                &ws.q_raw[rank],
7140                &q_norm[rank],
7141                &mut ws.q[rank],
7142                head_dim,
7143                local_heads,
7144                rms_eps,
7145            )?;
7146            engine.rms_norm(
7147                &ws.k_raw[rank],
7148                &k_norm[rank],
7149                &mut ws.k[rank],
7150                head_dim,
7151                local_kv_heads,
7152                rms_eps,
7153            )?;
7154            {
7155                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
7156                engine
7157                    .stream()
7158                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
7159            }
7160            engine.rope_neox2(
7161                &mut ws.q[rank],
7162                &mut ws.k[rank],
7163                &ws.pos[rank],
7164                head_dim,
7165                n_rot,
7166                local_heads,
7167                local_kv_heads,
7168                1,
7169                rope_base,
7170                1.0,
7171                rope_freqs[rank],
7172            )?;
7173        }
7174        if gate_shards.is_none() {
7175            let gate_start = rank * (ws.heads / ranks);
7176            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
7177            engine.stream().memcpy_dtod(
7178                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
7179                &mut gate_dst,
7180            )?;
7181        }
7182        Ok(())
7183    }
7184
7185    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
7186    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
7187    /// eager caller; graphs order via parent edges instead).
7188    pub(crate) fn decode_v2_finish_rank_partial(
7189        &self,
7190        ws: &mut StepTpDecodeV2Ws,
7191        o_m: &ResidentStepBf16RowParallel,
7192        o_fused: bool,
7193        rank: usize,
7194    ) -> Result<(), Box<dyn std::error::Error>> {
7195        let engine = &self.ranks[rank];
7196        let _main = engine.gpu.enter_main()?;
7197        if o_fused {
7198            let StepTpDecodeV2Ws {
7199                gated,
7200                o_partials,
7201                o_block_cols,
7202                o_out,
7203                w8o_aq,
7204                w8o_ad,
7205                w8o_in,
7206                ..
7207            } = &mut *ws;
7208            let all_f32 = o_m.ranks[rank]
7209                .iter()
7210                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
7211            if all_f32 {
7212                let mut weights = Vec::with_capacity(4);
7213                for block in 0..4 {
7214                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
7215                        unreachable!("all_f32 checked above");
7216                    };
7217                    weights.push(weight);
7218                }
7219                engine.matvec_f32_b4_into(
7220                    [weights[0], weights[1], weights[2], weights[3]],
7221                    &gated[rank],
7222                    &mut o_partials[rank][0],
7223                    *o_block_cols,
7224                    *o_out,
7225                )?;
7226            } else if crate::step_tp_w8_on() && (0..4).all(|b| o_m.ranks[rank][b].q8.is_some()) {
7227                // MEMRA_STEP_TP_W8, o_proj half: quantize the gated attention output once and
7228                // run all four HEAD_SPLIT blocks in one q8 launch. Measured motive: bf16 b4 is
7229                // 24.2 us/layer against 11.7 for the q8 shape — the largest decode line left
7230                // after the QKV arm banked +2.9%.
7231                let in_f = 4 * *o_block_cols;
7232                if *w8o_in != in_f || w8o_aq.len() != self.ranks.len() {
7233                    w8o_aq.clear();
7234                    w8o_ad.clear();
7235                    for e_rank in &self.ranks {
7236                        let _m = e_rank.gpu.enter_main()?;
7237                        w8o_aq.push(e_rank.alloc_uninit::<i8>(in_f)?);
7238                        w8o_ad.push(e_rank.alloc_uninit::<f32>(in_f / 32)?);
7239                    }
7240                    *w8o_in = in_f;
7241                }
7242                engine.quantize_q8_1_into(
7243                    &gated[rank],
7244                    1,
7245                    in_f,
7246                    &mut w8o_aq[rank],
7247                    &mut w8o_ad[rank],
7248                )?;
7249                engine.qmatvec_q8_0_b4_rp_into(
7250                    [
7251                        o_m.ranks[rank][0].q8.as_ref().unwrap(),
7252                        o_m.ranks[rank][1].q8.as_ref().unwrap(),
7253                        o_m.ranks[rank][2].q8.as_ref().unwrap(),
7254                        o_m.ranks[rank][3].q8.as_ref().unwrap(),
7255                    ],
7256                    &w8o_aq[rank],
7257                    &w8o_ad[rank],
7258                    &mut o_partials[rank][0],
7259                    *o_block_cols,
7260                    *o_out,
7261                )?;
7262            } else {
7263                let mut weights = Vec::with_capacity(4);
7264                for block in 0..4 {
7265                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
7266                        return Err("step TP decode v2 O projections mix residency classes".into());
7267                    };
7268                    weights.push(weight);
7269                }
7270                engine.matvec_bf16_b4_into(
7271                    [weights[0], weights[1], weights[2], weights[3]],
7272                    &gated[rank],
7273                    &mut o_partials[rank][0],
7274                    *o_block_cols,
7275                    *o_out,
7276                )?;
7277            }
7278        } else {
7279            for block in 0..ws.blocks_per_rank {
7280                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
7281                    return Err("step TP decode v2 lost its F32 O residency".into());
7282                };
7283                let x =
7284                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
7285                let w = weight.slice(0..weight.len());
7286                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
7287                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
7288            }
7289        }
7290        Ok(())
7291    }
7292
7293    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
7294    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
7295    ///
7296    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
7297    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
7298    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
7299    /// rank's blocks, one `add` per block.
7300    pub(crate) fn decode_v2_finish(
7301        &self,
7302        ws: &mut StepTpDecodeV2Ws,
7303        e: &Engine,
7304        o_m: &ResidentStepBf16RowParallel,
7305    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7306        let ranks = self.ranks.len();
7307        if e.ctx().ordinal() != ws.e_device {
7308            return Err("step TP decode v2 finish engine changed".into());
7309        }
7310        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
7311        // (in-order canonical block accumulation per element) and a single peer-copy + add on
7312        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
7313        // numeric-class door and gate as the fused QKV projection.
7314        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
7315
7316        // Per-rank O block partials on the owning rank's stream (serial after the attention
7317        // kernels the driver queued there), then the rank-done event for root's peer reads.
7318        for rank in 0..ranks {
7319            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
7320            if rank == 0 {
7321                // root == rank0: its own stream order covers the partial; only peers need
7322                // the record/wait pair (host-op diet, matches the routes-arm skip).
7323                continue;
7324            }
7325            let engine = &self.ranks[rank];
7326            let _main = engine.gpu.enter_main()?;
7327            ws.ev_rank[rank].record(&engine.stream())?;
7328        }
7329
7330        // Root reduce in canonical order + shadow gathers, all on the root stream.
7331        let root = &self.ranks[0];
7332        #[allow(unused_assignments)]
7333        let mut final_in_a = false;
7334        {
7335            let _main = root.gpu.enter_main()?;
7336            for ev in ws.ev_rank.iter().skip(1) {
7337                root.stream().wait(ev)?;
7338            }
7339            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
7340                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
7341                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
7342                // partial is root-stream-ordered — record ONE event and let the model
7343                // engine do the single add itself, straight into its own output row.
7344                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
7345                ws.ev_oproj.record(&root.stream())?;
7346                let _main = e.gpu.enter_main()?;
7347                e.stream().wait(&ws.ev_oproj)?;
7348                let mut output = e.uninit(ws.o_out)?;
7349                if oproj_tail_on() && oproj_tail_eligible() {
7350                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
7351                    // only the arithmetic moves). `output` is returned unwritten.
7352                    use cudarc::driver::DevicePtr;
7353                    let stream = e.stream();
7354                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
7355                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
7356                    set_oproj_tail((p0 as u64, p1 as u64));
7357                    return Ok(output);
7358                }
7359                e.add(
7360                    &ws.o_partials[0][0],
7361                    &ws.o_partials[1][0],
7362                    &mut output,
7363                    ws.o_out,
7364                )?;
7365                return Ok(output);
7366            }
7367            if o_fused {
7368                self.decode_v2_finish_root_fused(ws)?;
7369                ws.ev_oproj.record(&root.stream())?;
7370                let _main = e.gpu.enter_main()?;
7371                e.stream().wait(&ws.ev_oproj)?;
7372                let mut output = e.uninit(ws.o_out)?;
7373                e.stream().memcpy_dtod(
7374                    &ws.reduce_a.slice(0..ws.o_out),
7375                    &mut output.slice_mut(0..ws.o_out),
7376                )?;
7377                return Ok(output);
7378            }
7379            let mut first = true;
7380            let mut current_is_a = false;
7381            for rank in 0..ranks {
7382                for block in 0..ws.blocks_per_rank {
7383                    let use_peer = rank != 0;
7384                    if use_peer {
7385                        root.stream()
7386                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
7387                    }
7388                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
7389                    match (first, current_is_a, use_peer) {
7390                        (true, _, true) => {
7391                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7392                        }
7393                        (true, _, false) => root.add(
7394                            &ws.zeros,
7395                            &ws.o_partials[0][block],
7396                            &mut ws.reduce_a,
7397                            ws.o_out,
7398                        )?,
7399                        (false, true, true) => {
7400                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
7401                        }
7402                        (false, true, false) => root.add(
7403                            &ws.reduce_a,
7404                            &ws.o_partials[0][block],
7405                            &mut ws.reduce_b,
7406                            ws.o_out,
7407                        )?,
7408                        (false, false, true) => {
7409                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7410                        }
7411                        (false, false, false) => root.add(
7412                            &ws.reduce_b,
7413                            &ws.o_partials[0][block],
7414                            &mut ws.reduce_a,
7415                            ws.o_out,
7416                        )?,
7417                    }
7418                    current_is_a = first || !current_is_a;
7419                    first = false;
7420                }
7421            }
7422            final_in_a = current_is_a;
7423
7424            for rank in 0..ranks {
7425                let start = rank * ws.local_kv_dim;
7426                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
7427                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
7428                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
7429                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
7430            }
7431            ws.ev_oproj.record(&root.stream())?;
7432        }
7433
7434        // Model-engine output: e waits the root event, then copies the reduced row into a
7435        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
7436        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
7437        let _main = e.gpu.enter_main()?;
7438        e.stream().wait(&ws.ev_oproj)?;
7439        let mut output = e.uninit(ws.o_out)?;
7440        let source = if final_in_a {
7441            &ws.reduce_a
7442        } else {
7443            &ws.reduce_b
7444        };
7445        e.stream().memcpy_dtod(
7446            &source.slice(0..ws.o_out),
7447            &mut output.slice_mut(0..ws.o_out),
7448        )?;
7449        Ok(output)
7450    }
7451
7452    pub fn run_routed_experts(
7453        &self,
7454        experts: &ResidentExpertParallel,
7455        input: &[f32],
7456        tokens: usize,
7457        selected: &[usize],
7458        route_weights: &[f32],
7459        experts_per_token: usize,
7460        activation_limit: Option<f32>,
7461    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7462        validate_step_expert_activation_limit(activation_limit)?;
7463        validate_ep_residency(&self.ranks, experts)?;
7464        validate_activations(input, tokens, experts.input_width)?;
7465        let pairs = tokens
7466            .checked_mul(experts_per_token)
7467            .ok_or("EP route count overflow")?;
7468        if selected.len() != pairs || route_weights.len() != pairs {
7469            return Err(format!(
7470                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
7471                 {experts_per_token} ({pairs})",
7472                selected.len(),
7473                route_weights.len(),
7474            )
7475            .into());
7476        }
7477        if !route_weights.iter().all(|weight| weight.is_finite()) {
7478            return Err("EP route weights contain a non-finite value".into());
7479        }
7480        if self.native_p2p {
7481            return self.run_routed_experts_native(
7482                experts,
7483                input,
7484                tokens,
7485                selected,
7486                route_weights,
7487                experts_per_token,
7488                activation_limit,
7489            );
7490        }
7491
7492        let mut output = vec![0.0f32; tokens * experts.input_width];
7493        let per_rank = experts.expert_count / experts.ranks.len();
7494        for token in 0..tokens {
7495            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7496            for slot in 0..experts_per_token {
7497                let pair = token * experts_per_token + slot;
7498                let expert = selected[pair];
7499                if expert >= experts.expert_count {
7500                    return Err(format!(
7501                        "EP selected expert {expert} outside 0..{}",
7502                        experts.expert_count
7503                    )
7504                    .into());
7505                }
7506                let owner = expert / per_rank;
7507                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7508                let rank = &experts.ranks[owner];
7509                let engine = &self.ranks[owner];
7510                let gate =
7511                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
7512                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
7513                let activated: Vec<f32> = gate
7514                    .iter()
7515                    .zip(&up)
7516                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7517                    .collect();
7518                debug_assert_eq!(activated.len(), experts.expert_width);
7519                let down =
7520                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
7521                let weight = route_weights[pair];
7522                for (sum, value) in output
7523                    [token * experts.input_width..(token + 1) * experts.input_width]
7524                    .iter_mut()
7525                    .zip(down)
7526                {
7527                    *sum += weight * value;
7528                }
7529            }
7530        }
7531        Ok(output)
7532    }
7533
7534    fn run_routed_experts_native(
7535        &self,
7536        experts: &ResidentExpertParallel,
7537        input: &[f32],
7538        tokens: usize,
7539        selected: &[usize],
7540        route_weights: &[f32],
7541        experts_per_token: usize,
7542        activation_limit: Option<f32>,
7543    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7544        if !self.native_p2p || self.ranks.len() < 2 {
7545            return Err("native EP execution requires at least two P2P ranks".into());
7546        }
7547        if self.ep_device_arithmetic {
7548            return self.run_routed_experts_native_device(
7549                experts,
7550                input,
7551                tokens,
7552                selected,
7553                route_weights,
7554                experts_per_token,
7555                activation_limit,
7556            );
7557        }
7558        let mut output = vec![0.0f32; tokens * experts.input_width];
7559        let per_rank = experts.expert_count / experts.ranks.len();
7560        for token in 0..tokens {
7561            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7562            let mut rank_inputs = (0..self.ranks.len())
7563                .map(|_| None)
7564                .collect::<Vec<Option<CudaSlice<f32>>>>();
7565            rank_inputs[0] = Some({
7566                let root = &self.ranks[0];
7567                let _main = root.gpu.enter_main()?;
7568                root.htod(input_row)?
7569            });
7570
7571            for slot in 0..experts_per_token {
7572                let pair = token * experts_per_token + slot;
7573                let expert = selected[pair];
7574                if expert >= experts.expert_count {
7575                    return Err(format!(
7576                        "EP selected expert {expert} outside 0..{}",
7577                        experts.expert_count
7578                    )
7579                    .into());
7580                }
7581                let owner = expert / per_rank;
7582                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7583                if rank_inputs[owner].is_none() {
7584                    let peer_input = {
7585                        let root_input = rank_inputs[0]
7586                            .as_ref()
7587                            .ok_or("native EP lost its root input")?;
7588                        let engine = &self.ranks[owner];
7589                        let _main = engine.gpu.enter_main()?;
7590                        let mut peer_input = engine.uninit(experts.input_width)?;
7591                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7592                        peer_input
7593                    };
7594                    rank_inputs[owner] = Some(peer_input);
7595                }
7596
7597                let rank = &experts.ranks[owner];
7598                let engine = &self.ranks[owner];
7599                let owner_input = rank_inputs[owner]
7600                    .as_ref()
7601                    .ok_or("native EP owner input is absent after dispatch")?;
7602                let gate = run_resident_bank_expert_device(
7603                    engine,
7604                    &rank.gate,
7605                    local_expert,
7606                    owner_input,
7607                    1,
7608                )?;
7609                let up = run_resident_bank_expert_device(
7610                    engine,
7611                    &rank.up,
7612                    local_expert,
7613                    owner_input,
7614                    1,
7615                )?;
7616                let (gate, up) = {
7617                    let _main = engine.gpu.enter_main()?;
7618                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
7619                };
7620                let activated = gate
7621                    .iter()
7622                    .zip(&up)
7623                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7624                    .collect::<Vec<_>>();
7625                debug_assert_eq!(activated.len(), experts.expert_width);
7626                let activated = {
7627                    let _main = engine.gpu.enter_main()?;
7628                    engine.htod(&activated)?
7629                };
7630                let down = run_resident_bank_expert_device(
7631                    engine,
7632                    &rank.down,
7633                    local_expert,
7634                    &activated,
7635                    1,
7636                )?;
7637                let down = if owner == 0 {
7638                    let _main = engine.gpu.enter_main()?;
7639                    engine.dtoh(&down)?
7640                } else {
7641                    let root = &self.ranks[0];
7642                    let _main = root.gpu.enter_main()?;
7643                    let mut root_down = root.uninit(experts.input_width)?;
7644                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7645                    root.dtoh(&root_down)?
7646                };
7647                let weight = route_weights[pair];
7648                for (sum, value) in output
7649                    [token * experts.input_width..(token + 1) * experts.input_width]
7650                    .iter_mut()
7651                    .zip(down)
7652                {
7653                    *sum += weight * value;
7654                }
7655            }
7656        }
7657        Ok(output)
7658    }
7659
7660    fn run_routed_experts_native_device(
7661        &self,
7662        experts: &ResidentExpertParallel,
7663        input: &[f32],
7664        tokens: usize,
7665        selected: &[usize],
7666        route_weights: &[f32],
7667        experts_per_token: usize,
7668        activation_limit: Option<f32>,
7669    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7670        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
7671            return Err(
7672                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
7673            );
7674        }
7675        let mut output = Vec::with_capacity(tokens * experts.input_width);
7676        let per_rank = experts.expert_count / experts.ranks.len();
7677        let root = &self.ranks[0];
7678        for token in 0..tokens {
7679            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7680            let mut rank_inputs = (0..self.ranks.len())
7681                .map(|_| None)
7682                .collect::<Vec<Option<CudaSlice<f32>>>>();
7683            rank_inputs[0] = Some({
7684                let _main = root.gpu.enter_main()?;
7685                root.htod(input_row)?
7686            });
7687            let mut root_output = {
7688                let _main = root.gpu.enter_main()?;
7689                root.zeros(experts.input_width)?
7690            };
7691            let mut remote_down_keepalive = Vec::new();
7692
7693            for slot in 0..experts_per_token {
7694                let pair = token * experts_per_token + slot;
7695                let expert = selected[pair];
7696                if expert >= experts.expert_count {
7697                    return Err(format!(
7698                        "EP selected expert {expert} outside 0..{}",
7699                        experts.expert_count
7700                    )
7701                    .into());
7702                }
7703                let owner = expert / per_rank;
7704                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7705                if rank_inputs[owner].is_none() {
7706                    let peer_input = {
7707                        let root_input = rank_inputs[0]
7708                            .as_ref()
7709                            .ok_or("native EP lost its root input")?;
7710                        let engine = &self.ranks[owner];
7711                        let _main = engine.gpu.enter_main()?;
7712                        let mut peer_input = engine.uninit(experts.input_width)?;
7713                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7714                        peer_input
7715                    };
7716                    rank_inputs[owner] = Some(peer_input);
7717                }
7718
7719                let rank = &experts.ranks[owner];
7720                let engine = &self.ranks[owner];
7721                let owner_input = rank_inputs[owner]
7722                    .as_ref()
7723                    .ok_or("native EP owner input is absent after dispatch")?;
7724                let gate = run_resident_bank_expert_device(
7725                    engine,
7726                    &rank.gate,
7727                    local_expert,
7728                    owner_input,
7729                    1,
7730                )?;
7731                let up = run_resident_bank_expert_device(
7732                    engine,
7733                    &rank.up,
7734                    local_expert,
7735                    owner_input,
7736                    1,
7737                )?;
7738                let activated = {
7739                    let _main = engine.gpu.enter_main()?;
7740                    let mut activated = engine.uninit(experts.expert_width)?;
7741                    if let Some(limit) = activation_limit {
7742                        engine.silu_clamped_mul_host_expf(
7743                            &gate,
7744                            &up,
7745                            limit,
7746                            &mut activated,
7747                            experts.expert_width,
7748                        )?;
7749                    } else {
7750                        engine.silu_mul_host_expf(
7751                            &gate,
7752                            &up,
7753                            &mut activated,
7754                            experts.expert_width,
7755                        )?;
7756                    }
7757                    activated
7758                };
7759                let down = run_resident_bank_expert_device(
7760                    engine,
7761                    &rank.down,
7762                    local_expert,
7763                    &activated,
7764                    1,
7765                )?;
7766                let root_down = if owner == 0 {
7767                    down
7768                } else {
7769                    let _main = root.gpu.enter_main()?;
7770                    let mut root_down = root.uninit(experts.input_width)?;
7771                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7772                    // The peer copy runs on the root stream. Keep its remote source alive until
7773                    // the final root readback synchronizes that stream; otherwise async free can
7774                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
7775                    remote_down_keepalive.push(down);
7776                    root_down
7777                };
7778                let _main = root.gpu.enter_main()?;
7779                let mut destination = root_output.slice_mut(0..experts.input_width);
7780                root.axpy_host_into(
7781                    &root_down.slice(0..root_down.len()),
7782                    route_weights[pair],
7783                    &mut destination,
7784                    experts.input_width,
7785                )?;
7786            }
7787
7788            let _main = root.gpu.enter_main()?;
7789            let root_output = root.dtoh(&root_output)?;
7790            drop(remote_down_keepalive);
7791            output.extend(root_output);
7792        }
7793        Ok(output)
7794    }
7795}
7796
7797fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7798    if matrix.out_features % tp != 0 {
7799        return Err(format!(
7800            "column-parallel out_features {} is not divisible by TP={tp}",
7801            matrix.out_features
7802        ));
7803    }
7804    let local_out = matrix.out_features / tp;
7805    if local_out % FP8_BLOCK != 0 {
7806        return Err(format!(
7807            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
7808             E4M3 scale block"
7809        ));
7810    }
7811    Ok(())
7812}
7813
7814fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
7815    if !matches!(tp, 1 | 2 | 4 | 8) {
7816        return Err(format!(
7817            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7818        ));
7819    }
7820    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
7821        return Err(format!(
7822            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
7823        ));
7824    }
7825    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
7826    let local_out = out_features / tp;
7827    if local_out % canonical_rows != 0 {
7828        return Err(format!(
7829            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
7830             {canonical_rows}-row chunks"
7831        ));
7832    }
7833    Ok(canonical_rows)
7834}
7835
7836fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
7837    if !matches!(tp, 1 | 2 | 4 | 8) {
7838        return Err(format!(
7839            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7840        ));
7841    }
7842    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
7843        return Err(format!(
7844            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
7845        ));
7846    }
7847    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
7848    let local_in = in_features / tp;
7849    if local_in % canonical_cols != 0 {
7850        return Err(format!(
7851            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
7852             {canonical_cols}-column chunks"
7853        ));
7854    }
7855    Ok(canonical_cols)
7856}
7857
7858fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7859    if matrix.in_features % tp != 0 {
7860        return Err(format!(
7861            "row-parallel in_features {} is not divisible by TP={tp}",
7862            matrix.in_features
7863        ));
7864    }
7865    let local_in = matrix.in_features / tp;
7866    if local_in % FP8_BLOCK != 0 {
7867        return Err(format!(
7868            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
7869             E4M3 scale block"
7870        ));
7871    }
7872    Ok(())
7873}
7874
7875fn upload_rank(
7876    engine: &Engine,
7877    matrix: E4m3BlockMatrix<'_>,
7878) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
7879    let _main = engine.gpu.enter_main()?;
7880    matrix.validate()?;
7881    Ok(ResidentE4m3Rank {
7882        codes: engine.htod_bytes(matrix.codes)?,
7883        scales: engine.htod(matrix.scales)?,
7884        out_features: matrix.out_features,
7885        in_features: matrix.in_features,
7886    })
7887}
7888
7889fn upload_bf16_rank(
7890    engine: &Engine,
7891    matrix: Bf16Matrix<'_>,
7892    f32_mirror: bool,
7893) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
7894    let _main = engine.gpu.enter_main()?;
7895    matrix.validate()?;
7896    let bytes = engine.htod_bytes(matrix.bytes)?;
7897    let weight = if f32_mirror {
7898        let values = matrix
7899            .out_features
7900            .checked_mul(matrix.in_features)
7901            .ok_or("resident BF16 mirror element count overflow")?;
7902        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
7903    } else {
7904        ResidentBf16Weight::Bf16(bytes)
7905    };
7906    // MEMRA_STEP_TP_W8: encode the q8_0 decode mirror once, here, while the bf16 bytes are
7907    // already resident. Rows whose in_features is not a multiple of 32 have no q8_0 form and
7908    // simply keep the bf16 program (the decode arm checks for the mirror, never assumes it).
7909    let q8 = if crate::step_tp_w8_on() && matrix.in_features % 32 == 0 {
7910        if let ResidentBf16Weight::Bf16(bytes) = &weight {
7911            // Two steps, because the mmvq rp kernel does NOT read ggml-interleaved 34-byte
7912            // blocks: it reads a PLANAR mirror (all quants, then all half scales — the
7913            // q4_0/NVFP4 rp convention). The encoder writes the interleaved form and
7914            // `build_q8_rp4_raw` — the same kernel the GGUF loader uses — splits it into
7915            // planes. Skipping the split is what made the first W8 gate return zeros
7916            // (verify-prefill argmax=0, maxdiff=0.000e0).
7917            let row_bytes = Engine::q8_0_row_bytes(matrix.in_features);
7918            let mut interleaved = engine.alloc_u8_uninit(matrix.out_features * row_bytes)?;
7919            engine.encode_q8_0_from_bf16(
7920                bytes,
7921                &mut interleaved,
7922                matrix.in_features,
7923                matrix.out_features,
7924            )?;
7925            let mirror =
7926                engine.build_q8_rp4_raw(&interleaved, matrix.in_features, matrix.out_features)?;
7927            Some(mirror)
7928        } else {
7929            None
7930        }
7931    } else {
7932        None
7933    };
7934    Ok(ResidentBf16Rank {
7935        weight,
7936        out_features: matrix.out_features,
7937        in_features: matrix.in_features,
7938        q8,
7939    })
7940}
7941
7942fn upload_expert_bank_rank(
7943    engine: &Engine,
7944    bank: E4m3ExpertBank<'_>,
7945    expert_range: Range<usize>,
7946) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7947    let _main = engine.gpu.enter_main()?;
7948    bank.validate()?;
7949    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
7950        return Err(format!(
7951            "invalid EP expert range {expert_range:?} for {} experts",
7952            bank.expert_count
7953        )
7954        .into());
7955    }
7956    let code_stride = bank.out_features * bank.in_features;
7957    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
7958    Ok(ResidentE4m3ExpertBankRank {
7959        codes: engine.htod_bytes(
7960            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
7961        )?,
7962        scales: engine.htod(
7963            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
7964        )?,
7965        expert_range,
7966        out_features: bank.out_features,
7967        in_features: bank.in_features,
7968        code_stride,
7969        scale_stride,
7970        k_blocks: None,
7971    })
7972}
7973
7974fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7975    if bank.out_features % tp != 0 {
7976        return Err(format!(
7977            "TP expert output width {} is not divisible by TP={tp}",
7978            bank.out_features
7979        ));
7980    }
7981    let local_out = bank.out_features / tp;
7982    if local_out % FP8_BLOCK != 0 {
7983        return Err(format!(
7984            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
7985        ));
7986    }
7987    Ok(())
7988}
7989
7990fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7991    if bank.in_features % tp != 0 {
7992        return Err(format!(
7993            "TP expert input width {} is not divisible by TP={tp}",
7994            bank.in_features
7995        ));
7996    }
7997    let local_in = bank.in_features / tp;
7998    if local_in % FP8_BLOCK != 0 {
7999        return Err(format!(
8000            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
8001        ));
8002    }
8003    Ok(())
8004}
8005
8006fn upload_column_bank_rank(
8007    engine: &Engine,
8008    bank: E4m3ExpertBank<'_>,
8009    tp: usize,
8010    rank: usize,
8011) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8012    let _main = engine.gpu.enter_main()?;
8013    let packed = pack_column_bank_rank(bank, tp, rank)?;
8014    Ok(ResidentE4m3ExpertBankRank {
8015        codes: engine.htod_bytes(&packed.codes)?,
8016        scales: engine.htod(&packed.scales)?,
8017        expert_range: packed.expert_range,
8018        out_features: packed.out_features,
8019        in_features: packed.in_features,
8020        code_stride: packed.code_stride,
8021        scale_stride: packed.scale_stride,
8022        k_blocks: packed.k_blocks,
8023    })
8024}
8025
8026fn pack_column_bank_rank(
8027    bank: E4m3ExpertBank<'_>,
8028    tp: usize,
8029    rank: usize,
8030) -> Result<PackedE4m3ExpertBankRank, String> {
8031    bank.validate()?;
8032    validate_column_bank_shape(bank, tp)?;
8033    if rank >= tp {
8034        return Err(format!("TP rank {rank} outside 0..{tp}"));
8035    }
8036    let local_out = bank.out_features / tp;
8037    let full_code_stride = bank.out_features * bank.in_features;
8038    let local_code_stride = local_out * bank.in_features;
8039    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
8040    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
8041    let local_scale_rows = local_out / FP8_BLOCK;
8042    let local_scale_stride = local_scale_rows * scale_cols;
8043    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
8044    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
8045    let row_start = rank * local_out;
8046    let scale_row_start = rank * local_scale_rows;
8047    for expert in 0..bank.expert_count {
8048        let code_start = expert * full_code_stride + row_start * bank.in_features;
8049        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
8050        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
8051        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
8052    }
8053    Ok(PackedE4m3ExpertBankRank {
8054        codes,
8055        scales,
8056        expert_range: 0..bank.expert_count,
8057        out_features: local_out,
8058        in_features: bank.in_features,
8059        code_stride: local_code_stride,
8060        scale_stride: local_scale_stride,
8061        k_blocks: None,
8062    })
8063}
8064
8065fn upload_row_bank_rank(
8066    engine: &Engine,
8067    bank: E4m3ExpertBank<'_>,
8068    tp: usize,
8069    rank: usize,
8070) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
8071    let _main = engine.gpu.enter_main()?;
8072    let packed = pack_row_bank_rank(bank, tp, rank)?;
8073    Ok(ResidentE4m3ExpertBankRank {
8074        codes: engine.htod_bytes(&packed.codes)?,
8075        scales: engine.htod(&packed.scales)?,
8076        expert_range: packed.expert_range,
8077        out_features: packed.out_features,
8078        in_features: packed.in_features,
8079        code_stride: packed.code_stride,
8080        scale_stride: packed.scale_stride,
8081        k_blocks: packed.k_blocks,
8082    })
8083}
8084
8085fn pack_row_bank_rank(
8086    bank: E4m3ExpertBank<'_>,
8087    tp: usize,
8088    rank: usize,
8089) -> Result<PackedE4m3ExpertBankRank, String> {
8090    bank.validate()?;
8091    validate_row_bank_shape(bank, tp)?;
8092    if rank >= tp {
8093        return Err(format!("TP rank {rank} outside 0..{tp}"));
8094    }
8095    let local_in = bank.in_features / tp;
8096    let full_code_stride = bank.out_features * bank.in_features;
8097    let local_code_stride = bank.out_features * local_in;
8098    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
8099    let local_scale_cols = local_in / FP8_BLOCK;
8100    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
8101    let full_scale_stride = scale_rows * full_scale_cols;
8102    let local_scale_stride = scale_rows * local_scale_cols;
8103    let global_block_start = rank * local_scale_cols;
8104    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
8105    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
8106    for expert in 0..bank.expert_count {
8107        let expert_code_start = expert * full_code_stride;
8108        let expert_scale_start = expert * full_scale_stride;
8109        for local_block in 0..local_scale_cols {
8110            let global_block = global_block_start + local_block;
8111            let column_start = global_block * FP8_BLOCK;
8112            for row in 0..bank.out_features {
8113                let start = expert_code_start + row * bank.in_features + column_start;
8114                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
8115            }
8116            for row in 0..scale_rows {
8117                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
8118            }
8119        }
8120    }
8121    Ok(PackedE4m3ExpertBankRank {
8122        codes,
8123        scales,
8124        expert_range: 0..bank.expert_count,
8125        out_features: bank.out_features,
8126        in_features: local_in,
8127        code_stride: local_code_stride,
8128        scale_stride: local_scale_stride,
8129        k_blocks: Some(local_scale_cols),
8130    })
8131}
8132
8133fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
8134    if engines.len() != ranks.len() {
8135        return Err(format!(
8136            "resident TP rank count {} != runtime rank count {}",
8137            ranks.len(),
8138            engines.len()
8139        ));
8140    }
8141    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
8142        let device = engine.ctx().ordinal();
8143        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
8144            return Err(format!(
8145                "resident TP rank {rank} is not owned by runtime device {device}"
8146            ));
8147        }
8148    }
8149    Ok(())
8150}
8151
8152fn validate_tp_bank_residency(
8153    engines: &[Engine],
8154    experts: &ResidentTpExpertBank,
8155) -> Result<(), String> {
8156    if engines.len() != experts.gate.len()
8157        || engines.len() != experts.up.len()
8158        || engines.len() != experts.down.len()
8159    {
8160        return Err(format!(
8161            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
8162            experts.gate.len(),
8163            experts.up.len(),
8164            experts.down.len(),
8165            engines.len()
8166        ));
8167    }
8168    for (rank, engine) in engines.iter().enumerate() {
8169        let device = engine.ctx().ordinal();
8170        for (projection, bank) in [
8171            ("gate", &experts.gate[rank]),
8172            ("up", &experts.up[rank]),
8173            ("down", &experts.down[rank]),
8174        ] {
8175            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
8176                return Err(format!(
8177                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
8178                     {device}"
8179                ));
8180            }
8181        }
8182    }
8183    Ok(())
8184}
8185
8186fn validate_ep_residency(
8187    engines: &[Engine],
8188    experts: &ResidentExpertParallel,
8189) -> Result<(), String> {
8190    if engines.len() != experts.ranks.len() {
8191        return Err(format!(
8192            "resident EP rank count {} != runtime rank count {}",
8193            experts.ranks.len(),
8194            engines.len()
8195        ));
8196    }
8197    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
8198        let device = engine.ctx().ordinal();
8199        for (projection, bank) in [
8200            ("gate", &resident.gate),
8201            ("up", &resident.up),
8202            ("down", &resident.down),
8203        ] {
8204            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
8205                return Err(format!(
8206                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
8207                     {device}"
8208                ));
8209            }
8210        }
8211    }
8212    Ok(())
8213}
8214
8215fn run_rank(
8216    engine: &Engine,
8217    matrix: E4m3BlockMatrix<'_>,
8218    activations: &[f32],
8219    tokens: usize,
8220) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8221    let _main = engine.gpu.enter_main()?;
8222    let codes = engine.htod_bytes(matrix.codes)?;
8223    let scales = engine.htod(matrix.scales)?;
8224    let activations = engine.htod(activations)?;
8225    let output = engine.qmatvec_mmq_fp8_blk(
8226        &codes,
8227        &scales,
8228        &activations,
8229        tokens,
8230        matrix.in_features,
8231        matrix.out_features,
8232    )?;
8233    engine.dtoh(&output)
8234}
8235
8236fn run_resident_rank(
8237    engine: &Engine,
8238    matrix: &ResidentE4m3Rank,
8239    activations: &[f32],
8240    tokens: usize,
8241) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8242    let _main = engine.gpu.enter_main()?;
8243    let activations = engine.htod(activations)?;
8244    let output = engine.qmatvec_mmq_fp8_blk(
8245        &matrix.codes,
8246        &matrix.scales,
8247        &activations,
8248        tokens,
8249        matrix.in_features,
8250        matrix.out_features,
8251    )?;
8252    engine.dtoh(&output)
8253}
8254
8255fn run_resident_bf16_rank(
8256    engine: &Engine,
8257    matrix: &ResidentBf16Rank,
8258    activations: &[f32],
8259    tokens: usize,
8260    canonical_chunk_rows: Option<usize>,
8261) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8262    let _main = engine.gpu.enter_main()?;
8263    let activations = engine.htod(activations)?;
8264    let output = run_resident_bf16_rank_device(
8265        engine,
8266        matrix,
8267        &activations,
8268        tokens,
8269        canonical_chunk_rows,
8270        false,
8271    )?;
8272    engine.dtoh(&output)
8273}
8274
8275fn run_resident_bf16_rank_device(
8276    engine: &Engine,
8277    matrix: &ResidentBf16Rank,
8278    activations: &CudaSlice<f32>,
8279    tokens: usize,
8280    canonical_chunk_rows: Option<usize>,
8281    strided_chunk_output: bool,
8282) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8283    let _main = engine.gpu.enter_main()?;
8284    if activations.ordinal() != engine.ctx().ordinal() {
8285        return Err(format!(
8286            "resident BF16 activation device {} != rank device {}",
8287            activations.ordinal(),
8288            engine.ctx().ordinal()
8289        )
8290        .into());
8291    }
8292    if activations.len() != tokens * matrix.in_features {
8293        return Err(format!(
8294            "resident BF16 activation count {} != {tokens}x{}",
8295            activations.len(),
8296            matrix.in_features
8297        )
8298        .into());
8299    }
8300    match (&matrix.weight, canonical_chunk_rows) {
8301        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
8302            .linear_bf16_resident_canonical_rows(
8303                activations,
8304                bytes,
8305                tokens,
8306                matrix.in_features,
8307                matrix.out_features,
8308                rows,
8309            ),
8310        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
8311            activations,
8312            bytes,
8313            tokens,
8314            matrix.in_features,
8315            matrix.out_features,
8316        ),
8317        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
8318            .linear_f32_resident_canonical_rows_strided(
8319                activations,
8320                values,
8321                tokens,
8322                matrix.in_features,
8323                matrix.out_features,
8324                rows,
8325            ),
8326        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
8327            activations,
8328            values,
8329            tokens,
8330            matrix.in_features,
8331            matrix.out_features,
8332            rows,
8333        ),
8334        (ResidentBf16Weight::F32(values), None) => engine.linear(
8335            activations,
8336            values,
8337            tokens,
8338            matrix.in_features,
8339            matrix.out_features,
8340        ),
8341    }
8342}
8343
8344fn validate_resident_bf16_ranks(
8345    engines: &[Engine],
8346    ranks: &[ResidentBf16Rank],
8347) -> Result<(), String> {
8348    if engines.len() != ranks.len() {
8349        return Err(format!(
8350            "resident BF16 TP rank count {} != runtime rank count {}",
8351            ranks.len(),
8352            engines.len(),
8353        ));
8354    }
8355    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
8356        let device = engine.ctx().ordinal();
8357        if matrix.weight.ordinal() != device {
8358            return Err(format!(
8359                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
8360            ));
8361        }
8362    }
8363    Ok(())
8364}
8365
8366fn validate_step_bf16_row_residency(
8367    engines: &[Engine],
8368    matrix: &ResidentStepBf16RowParallel,
8369) -> Result<(), String> {
8370    if engines.len() != matrix.ranks.len() {
8371        return Err(format!(
8372            "resident Step BF16 row rank count {} != runtime rank count {}",
8373            matrix.ranks.len(),
8374            engines.len(),
8375        ));
8376    }
8377    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
8378    if matrix.canonical_chunk_cols != canonical_cols {
8379        return Err(format!(
8380            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
8381            matrix.canonical_chunk_cols
8382        ));
8383    }
8384    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
8385    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
8386        if blocks.len() != blocks_per_rank {
8387            return Err(format!(
8388                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
8389                blocks.len()
8390            ));
8391        }
8392        let device = engine.ctx().ordinal();
8393        for (block, resident) in blocks.iter().enumerate() {
8394            if resident.weight.ordinal() != device
8395                || resident.in_features != canonical_cols
8396                || resident.out_features != matrix.out_features
8397            {
8398                return Err(format!(
8399                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
8400                     device or geometry"
8401                ));
8402            }
8403        }
8404    }
8405    Ok(())
8406}
8407
8408fn validate_replicated_device_rows(
8409    engines: &[Engine],
8410    rows: &ResidentReplicatedDeviceRows,
8411) -> Result<(), String> {
8412    let rank_lengths = rows
8413        .ranks
8414        .iter()
8415        .map(|rank_rows| rank_rows.len())
8416        .collect::<Vec<_>>();
8417    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
8418    if rows
8419        .ranks
8420        .iter()
8421        .zip(engines)
8422        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
8423    {
8424        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
8425    }
8426    Ok(())
8427}
8428
8429fn replicated_device_row_values(
8430    tokens: usize,
8431    width: usize,
8432    expected_ranks: usize,
8433    rank_lengths: &[usize],
8434) -> Result<usize, String> {
8435    let values = tokens
8436        .checked_mul(width)
8437        .ok_or("replicated device row size overflow")?;
8438    if tokens == 0
8439        || width == 0
8440        || expected_ranks == 0
8441        || rank_lengths.len() != expected_ranks
8442        || rank_lengths.iter().any(|&rank_len| rank_len != values)
8443    {
8444        return Err(format!(
8445            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
8446            tokens,
8447            width,
8448            rank_lengths.len(),
8449            expected_ranks
8450        ));
8451    }
8452    Ok(values)
8453}
8454
8455fn replicated_device_row_source_values(
8456    tokens: usize,
8457    width: usize,
8458    source_len: usize,
8459    source_device: usize,
8460    root_device: usize,
8461) -> Result<usize, String> {
8462    let values = tokens
8463        .checked_mul(width)
8464        .ok_or("replicated device row size overflow")?;
8465    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
8466        return Err(format!(
8467            "replicated device row source has inconsistent geometry/device \
8468             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
8469        ));
8470    }
8471    Ok(values)
8472}
8473
8474fn bf16_column_shard(
8475    matrix: Bf16Matrix<'_>,
8476    tp: usize,
8477    rank: usize,
8478) -> Result<Bf16Matrix<'_>, String> {
8479    matrix.validate()?;
8480    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
8481        return Err(format!(
8482            "invalid BF16 column shard out={} TP={tp} rank={rank}",
8483            matrix.out_features
8484        ));
8485    }
8486    let local_out = matrix.out_features / tp;
8487    let row_bytes = matrix.in_features * 2;
8488    let start = rank * local_out * row_bytes;
8489    Ok(Bf16Matrix {
8490        bytes: &matrix.bytes[start..start + local_out * row_bytes],
8491        out_features: local_out,
8492        in_features: matrix.in_features,
8493    })
8494}
8495
8496fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
8497    matrix.validate()?;
8498    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
8499        return Err(format!(
8500            "invalid BF16 row shard in={} TP={tp} rank={rank}",
8501            matrix.in_features
8502        ));
8503    }
8504    let local_in = matrix.in_features / tp;
8505    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
8506    for row in 0..matrix.out_features {
8507        let start = (row * matrix.in_features + rank * local_in) * 2;
8508        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
8509    }
8510    Ok(bytes)
8511}
8512
8513fn bf16_row_block(
8514    matrix: Bf16Matrix<'_>,
8515    col_start: usize,
8516    block_cols: usize,
8517) -> Result<Vec<u8>, String> {
8518    matrix.validate()?;
8519    let col_end = col_start
8520        .checked_add(block_cols)
8521        .ok_or("BF16 row block column overflow")?;
8522    if block_cols == 0 || col_end > matrix.in_features {
8523        return Err(format!(
8524            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
8525            matrix.in_features
8526        ));
8527    }
8528    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
8529    for row in 0..matrix.out_features {
8530        let start = (row * matrix.in_features + col_start) * 2;
8531        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
8532    }
8533    Ok(bytes)
8534}
8535
8536fn run_resident_bank_expert(
8537    engine: &Engine,
8538    bank: &ResidentE4m3ExpertBankRank,
8539    local_expert: usize,
8540    activations: &[f32],
8541    tokens: usize,
8542) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8543    let _main = engine.gpu.enter_main()?;
8544    if bank.k_blocks.is_some() {
8545        return Err("block-major TP row bank requires canonical block execution".into());
8546    }
8547    let local_count = bank.expert_range.end - bank.expert_range.start;
8548    if local_expert >= local_count {
8549        return Err(format!(
8550            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
8551            bank.expert_range
8552        )
8553        .into());
8554    }
8555    validate_activations(activations, tokens, bank.in_features)?;
8556    let activations = engine.htod(activations)?;
8557    let weight = bank
8558        .codes
8559        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8560    let scales = bank
8561        .scales
8562        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8563    let input = activations.slice(0..activations.len());
8564    let output = engine.qmatvec_mmq_fp8_blk_view(
8565        &weight,
8566        &scales,
8567        &input,
8568        tokens,
8569        bank.in_features,
8570        bank.out_features,
8571    )?;
8572    engine.dtoh(&output)
8573}
8574
8575fn run_resident_bank_expert_block(
8576    engine: &Engine,
8577    bank: &ResidentE4m3ExpertBankRank,
8578    local_expert: usize,
8579    block: usize,
8580    activations: &[f32],
8581) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8582    let _main = engine.gpu.enter_main()?;
8583    let local_count = bank.expert_range.end - bank.expert_range.start;
8584    if local_expert >= local_count {
8585        return Err(format!(
8586            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8587            bank.expert_range
8588        )
8589        .into());
8590    }
8591    let blocks = bank
8592        .k_blocks
8593        .ok_or("TP row bank is not packed in native K-block order")?;
8594    if block >= blocks {
8595        return Err(format!("TP row block {block} outside 0..{blocks}").into());
8596    }
8597    validate_activations(activations, 1, FP8_BLOCK)?;
8598    let block_code_stride = bank.out_features * FP8_BLOCK;
8599    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8600    if bank.in_features != blocks * FP8_BLOCK
8601        || bank.code_stride != blocks * block_code_stride
8602        || bank.scale_stride != blocks * block_scale_stride
8603    {
8604        return Err("TP row bank block-major geometry is inconsistent".into());
8605    }
8606
8607    let expert_code_start = local_expert * bank.code_stride;
8608    let expert_scale_start = local_expert * bank.scale_stride;
8609    let weight = bank.codes.slice(
8610        expert_code_start + block * block_code_stride
8611            ..expert_code_start + (block + 1) * block_code_stride,
8612    );
8613    let scales = bank.scales.slice(
8614        expert_scale_start + block * block_scale_stride
8615            ..expert_scale_start + (block + 1) * block_scale_stride,
8616    );
8617    let activations = engine.htod(activations)?;
8618    let input = activations.slice(0..activations.len());
8619    let output = engine.qmatvec_mmq_fp8_blk_view(
8620        &weight,
8621        &scales,
8622        &input,
8623        1,
8624        FP8_BLOCK,
8625        bank.out_features,
8626    )?;
8627    engine.dtoh(&output)
8628}
8629
8630fn run_resident_bank_expert_device(
8631    engine: &Engine,
8632    bank: &ResidentE4m3ExpertBankRank,
8633    local_expert: usize,
8634    activations: &CudaSlice<f32>,
8635    tokens: usize,
8636) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8637    let _main = engine.gpu.enter_main()?;
8638    if bank.k_blocks.is_some() {
8639        return Err("block-major TP row bank requires canonical block execution".into());
8640    }
8641    let local_count = bank.expert_range.end - bank.expert_range.start;
8642    if local_expert >= local_count {
8643        return Err(format!(
8644            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8645            bank.expert_range
8646        )
8647        .into());
8648    }
8649    let expected = tokens
8650        .checked_mul(bank.in_features)
8651        .ok_or("native TP activation size overflow")?;
8652    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
8653        return Err(format!(
8654            "native TP activation len/device {}/{} != expected {expected}/{}",
8655            activations.len(),
8656            activations.ordinal(),
8657            engine.ctx().ordinal()
8658        )
8659        .into());
8660    }
8661    let weight = bank
8662        .codes
8663        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8664    let scales = bank
8665        .scales
8666        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8667    let input = activations.slice(0..activations.len());
8668    engine.qmatvec_mmq_fp8_blk_view(
8669        &weight,
8670        &scales,
8671        &input,
8672        tokens,
8673        bank.in_features,
8674        bank.out_features,
8675    )
8676}
8677
8678fn run_resident_bank_expert_block_device(
8679    engine: &Engine,
8680    bank: &ResidentE4m3ExpertBankRank,
8681    local_expert: usize,
8682    block: usize,
8683    activations: &cudarc::driver::CudaView<'_, f32>,
8684) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8685    let _main = engine.gpu.enter_main()?;
8686    let local_count = bank.expert_range.end - bank.expert_range.start;
8687    if local_expert >= local_count {
8688        return Err(format!(
8689            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8690            bank.expert_range
8691        )
8692        .into());
8693    }
8694    let blocks = bank
8695        .k_blocks
8696        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
8697    if block >= blocks {
8698        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
8699    }
8700    let activation_device = activations.stream().context().ordinal();
8701    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
8702        return Err(format!(
8703            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
8704            activations.len(),
8705            activation_device,
8706            engine.ctx().ordinal()
8707        )
8708        .into());
8709    }
8710    let block_code_stride = bank.out_features * FP8_BLOCK;
8711    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8712    if bank.in_features != blocks * FP8_BLOCK
8713        || bank.code_stride != blocks * block_code_stride
8714        || bank.scale_stride != blocks * block_scale_stride
8715    {
8716        return Err("native TP row bank block-major geometry is inconsistent".into());
8717    }
8718    let expert_code_start = local_expert * bank.code_stride;
8719    let expert_scale_start = local_expert * bank.scale_stride;
8720    let weight = bank.codes.slice(
8721        expert_code_start + block * block_code_stride
8722            ..expert_code_start + (block + 1) * block_code_stride,
8723    );
8724    let scales = bank.scales.slice(
8725        expert_scale_start + block * block_scale_stride
8726            ..expert_scale_start + (block + 1) * block_scale_stride,
8727    );
8728    engine.qmatvec_mmq_fp8_blk_view(
8729        &weight,
8730        &scales,
8731        activations,
8732        1,
8733        FP8_BLOCK,
8734        bank.out_features,
8735    )
8736}
8737
8738fn configure_native_p2p(
8739    ranks: &[Engine],
8740    devices: &[usize],
8741) -> Result<(), Box<dyn std::error::Error>> {
8742    if ranks.len() != devices.len() || ranks.len() < 2 {
8743        return Err("native TP P2P setup requires matching multi-rank devices".into());
8744    }
8745    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
8746        if engine.ctx().ordinal() != device {
8747            return Err(format!(
8748                "native TP rank {rank} context device {} != requested device {device}",
8749                engine.ctx().ordinal()
8750            )
8751            .into());
8752        }
8753    }
8754
8755    for src in 0..ranks.len() {
8756        for dst in 0..ranks.len() {
8757            if src == dst {
8758                continue;
8759            }
8760            let mut can_access = 0;
8761            unsafe {
8762                cudarc::driver::sys::cuDeviceCanAccessPeer(
8763                    &mut can_access,
8764                    ranks[src].ctx().cu_device(),
8765                    ranks[dst].ctx().cu_device(),
8766                )
8767                .result()?;
8768            }
8769            if can_access == 0 {
8770                return Err(format!(
8771                    "native TP requires P2P, but dev{} cannot access dev{}",
8772                    devices[src], devices[dst]
8773                )
8774                .into());
8775            }
8776            ranks[src].ctx().bind_to_thread()?;
8777            let rc =
8778                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
8779            use cudarc::driver::sys::cudaError_enum as E;
8780            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
8781                return Err(format!(
8782                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
8783                    devices[src], devices[dst]
8784                )
8785                .into());
8786            }
8787        }
8788    }
8789
8790    for &owner in devices {
8791        for &accessor in devices {
8792            if owner == accessor {
8793                continue;
8794            }
8795            let device = cudarc::driver::result::device::get(owner as i32)?;
8796            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
8797            unsafe {
8798                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
8799            }
8800            let desc = cudarc::driver::sys::CUmemAccessDesc {
8801                location: cudarc::driver::sys::CUmemLocation {
8802                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
8803                    id: accessor as i32,
8804                },
8805                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
8806            };
8807            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
8808            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
8809                return Err(format!(
8810                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
8811                     {rc:?}"
8812                )
8813                .into());
8814            }
8815        }
8816    }
8817
8818    for src in 0..ranks.len() {
8819        for dst in 0..ranks.len() {
8820            if src == dst {
8821                continue;
8822            }
8823            for &words in NATIVE_P2P_PROBE_WORDS {
8824                let expected = (0..words)
8825                    .map(|index| {
8826                        (index as u32)
8827                            .wrapping_mul(0x9e37_79b9)
8828                            .wrapping_add(((src as u32) << 16) | dst as u32)
8829                    })
8830                    .collect::<Vec<_>>();
8831                let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
8832                let source = ranks[src].htod_u32_v(&expected)?;
8833                let mut destination = ranks[dst].htod_u32_v(&poison)?;
8834                ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
8835                let actual = ranks[dst].dtoh_u32(&destination)?;
8836                if actual != expected {
8837                    let mismatches = actual
8838                        .iter()
8839                        .zip(&expected)
8840                        .filter(|(actual, expected)| actual != expected)
8841                        .count();
8842                    return Err(format!(
8843                        "native TP peer probe dev{}->dev{} failed at {} bytes: \
8844                         {mismatches}/{} words differ",
8845                        devices[src],
8846                        devices[dst],
8847                        words * std::mem::size_of::<u32>(),
8848                        expected.len()
8849                    )
8850                    .into());
8851                }
8852            }
8853        }
8854    }
8855    ranks[0].ctx().bind_to_thread()?;
8856    eprintln!(
8857        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
8858         directions={} byte_ladder={:?} mismatches=0",
8859        ranks.len() * (ranks.len() - 1),
8860        NATIVE_P2P_PROBE_WORDS
8861            .iter()
8862            .map(|words| words * std::mem::size_of::<u32>())
8863            .collect::<Vec<_>>(),
8864    );
8865    Ok(())
8866}
8867
8868fn validate_activations(
8869    activations: &[f32],
8870    tokens: usize,
8871    in_features: usize,
8872) -> Result<(), String> {
8873    let expected = tokens
8874        .checked_mul(in_features)
8875        .ok_or_else(|| "activation size overflow".to_string())?;
8876    if activations.len() != expected {
8877        return Err(format!(
8878            "activation count {} != {tokens}x{in_features} ({expected})",
8879            activations.len()
8880        ));
8881    }
8882    if !activations.iter().all(|value| value.is_finite()) {
8883        return Err("activations contain a non-finite value".to_string());
8884    }
8885    Ok(())
8886}
8887
8888fn column_shard(
8889    matrix: E4m3BlockMatrix<'_>,
8890    tp: usize,
8891    rank: usize,
8892) -> Result<E4m3BlockMatrix<'_>, String> {
8893    let local_out = matrix.out_features / tp;
8894    let row_start = rank * local_out;
8895    let code_start = row_start * matrix.in_features;
8896    let code_end = code_start + local_out * matrix.in_features;
8897    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8898    let local_scale_rows = local_out / FP8_BLOCK;
8899    let scale_start = rank * local_scale_rows * scale_cols;
8900    let scale_end = scale_start + local_scale_rows * scale_cols;
8901    Ok(E4m3BlockMatrix {
8902        codes: &matrix.codes[code_start..code_end],
8903        scales: &matrix.scales[scale_start..scale_end],
8904        out_features: local_out,
8905        in_features: matrix.in_features,
8906    })
8907}
8908
8909fn row_shard(
8910    matrix: E4m3BlockMatrix<'_>,
8911    tp: usize,
8912    rank: usize,
8913) -> Result<(Vec<u8>, Vec<f32>), String> {
8914    let local_in = matrix.in_features / tp;
8915    let col_start = rank * local_in;
8916    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
8917    for row in 0..matrix.out_features {
8918        let start = row * matrix.in_features + col_start;
8919        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
8920    }
8921
8922    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
8923    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8924    let local_scale_cols = local_in / FP8_BLOCK;
8925    let scale_col_start = rank * local_scale_cols;
8926    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
8927    for row in 0..scale_rows {
8928        let start = row * scale_cols + scale_col_start;
8929        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
8930    }
8931    Ok((codes, scales))
8932}
8933
8934fn activation_shard(
8935    activations: &[f32],
8936    tokens: usize,
8937    in_features: usize,
8938    tp: usize,
8939    rank: usize,
8940) -> Vec<f32> {
8941    let local_in = in_features / tp;
8942    let col_start = rank * local_in;
8943    let mut shard = Vec::with_capacity(tokens * local_in);
8944    for token in 0..tokens {
8945        let start = token * in_features + col_start;
8946        shard.extend_from_slice(&activations[start..start + local_in]);
8947    }
8948    shard
8949}
8950
8951// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
8952//
8953// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
8954// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
8955// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
8956// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
8957// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
8958// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
8959//
8960// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
8961// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
8962// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
8963// TP1-vs-TP2 bit gate. Every entry point below follows this order.
8964//
8965// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
8966// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
8967// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
8968
8969/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
8970#[derive(Clone, Copy)]
8971pub struct Nvfp4BlockMatrix<'a> {
8972    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
8973    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
8974    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
8975    pub out_features: usize,
8976    pub in_features: usize,
8977}
8978
8979impl Nvfp4BlockMatrix<'_> {
8980    pub fn validate(&self) -> Result<(), String> {
8981        if self.in_features == 0 || self.out_features == 0 {
8982            return Err("NVFP4 matrix has a zero dimension".to_string());
8983        }
8984        if self.in_features % 64 != 0 {
8985            return Err(format!(
8986                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
8987                self.in_features
8988            ));
8989        }
8990        if self.codes.len() != self.out_features * self.in_features / 2 {
8991            return Err(format!(
8992                "NVFP4 code bytes {} != {}x{}/2",
8993                self.codes.len(),
8994                self.out_features,
8995                self.in_features
8996            ));
8997        }
8998        if self.scales.len() != self.out_features * self.in_features / 16 {
8999            return Err(format!(
9000                "NVFP4 scale bytes {} != {}x{}/16",
9001                self.scales.len(),
9002                self.out_features,
9003                self.in_features
9004            ));
9005        }
9006        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
9007            return Err(format!(
9008                "NVFP4 macro scale {} is not finite-positive",
9009                self.macro_scale
9010            ));
9011        }
9012        Ok(())
9013    }
9014}
9015
9016/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
9017#[derive(Clone, Copy)]
9018pub struct Nvfp4ExpertBank<'a> {
9019    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
9020    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
9021    pub macros: &'a [f32], // [expert_count] weight_scale_2
9022    pub expert_count: usize,
9023    pub out_features: usize,
9024    pub in_features: usize,
9025}
9026
9027impl Nvfp4ExpertBank<'_> {
9028    pub fn validate(&self) -> Result<(), String> {
9029        if self.expert_count == 0 {
9030            return Err("NVFP4 expert bank is empty".to_string());
9031        }
9032        if self.macros.len() != self.expert_count {
9033            return Err(format!(
9034                "NVFP4 bank macros {} != expert count {}",
9035                self.macros.len(),
9036                self.expert_count
9037            ));
9038        }
9039        self.expert(0).map(|_| ())
9040    }
9041
9042    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
9043        if expert >= self.expert_count {
9044            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
9045        }
9046        let code_stride = self.out_features * self.in_features / 2;
9047        let scale_stride = self.out_features * self.in_features / 16;
9048        if self.codes.len() != self.expert_count * code_stride
9049            || self.scales.len() != self.expert_count * scale_stride
9050        {
9051            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
9052        }
9053        let matrix = Nvfp4BlockMatrix {
9054            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
9055            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
9056            macro_scale: self.macros[expert],
9057            out_features: self.out_features,
9058            in_features: self.in_features,
9059        };
9060        matrix.validate()?;
9061        Ok(matrix)
9062    }
9063}
9064
9065/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
9066pub struct ResidentNvfp4Rank {
9067    blocks: crate::CudaSlice<u8>,
9068    macro_scale: f32,
9069    out_features: usize,
9070    in_features: usize,
9071    row_bytes: usize,
9072}
9073
9074pub struct ResidentNvfp4ColumnParallel {
9075    ranks: Vec<ResidentNvfp4Rank>,
9076    pub out_features: usize,
9077    pub in_features: usize,
9078}
9079
9080pub struct ResidentNvfp4RowParallel {
9081    ranks: Vec<ResidentNvfp4Rank>,
9082    pub out_features: usize,
9083    pub in_features: usize,
9084}
9085
9086pub struct ResidentTpNvfp4Expert {
9087    gate: ResidentNvfp4ColumnParallel,
9088    up: ResidentNvfp4ColumnParallel,
9089    down: ResidentNvfp4RowParallel,
9090    pub input_width: usize,
9091    pub expert_width: usize,
9092}
9093
9094/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
9095/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
9096/// later perf rung, mirroring the FP8 bank's history).
9097pub struct ResidentNvfp4ColumnBankRank {
9098    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
9099    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
9100    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
9101    bank: crate::CudaSlice<u8>,
9102    expert_bytes: usize,
9103    local_out: usize,
9104    in_features: usize,
9105    row_bytes: usize,
9106}
9107
9108impl ResidentNvfp4ColumnBankRank {
9109    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
9110        self.bank
9111            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
9112    }
9113}
9114
9115/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
9116/// as exactly this many input-column windows summed in shard order, at every world size: a
9117/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
9118/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
9119/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
9120pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
9121
9122pub struct ResidentNvfp4RowBankRank {
9123    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
9124    bank: crate::CudaSlice<u8>,
9125    expert_bytes: usize,
9126    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
9127    out_features: usize,
9128    local_in: usize,
9129    row_bytes: usize,
9130}
9131
9132impl ResidentNvfp4RowBankRank {
9133    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
9134        self.bank
9135            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
9136    }
9137}
9138
9139impl ResidentNvfp4TensorParallel {
9140    pub(crate) fn device_workspace_handle(
9141        &self,
9142    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
9143        &self.device_workspace
9144    }
9145}
9146
9147pub struct ResidentNvfp4TensorParallel {
9148    gate: Vec<ResidentNvfp4ColumnBankRank>,
9149    up: Vec<ResidentNvfp4ColumnBankRank>,
9150    down: Vec<ResidentNvfp4RowBankRank>,
9151    macros_gate: Vec<f32>,
9152    macros_up: Vec<f32>,
9153    macros_down: Vec<f32>,
9154    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
9155    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
9156    /// into the route-weight axpy scalar.
9157    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
9158    macros_up_dev: Vec<crate::CudaSlice<f32>>,
9159    macros_down_dev: Vec<crate::CudaSlice<f32>>,
9160    pub expert_count: usize,
9161    pub input_width: usize,
9162    pub expert_width: usize,
9163    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
9164    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
9165    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
9166    /// Grouped-prime per-rank slot-major pointer tables (gate/up/down x n_expert), built once.
9167    /// The banks are resident and never move, so rebuilding + re-uploading 3*n_expert u64s per
9168    /// rank per LAYER was pure per-call host churn on the prime path.
9169    prime_tables: std::sync::Mutex<Vec<crate::CudaSlice<u64>>>,
9170    /// MEMRA_STEP_NVFP4_EP2: the rank banks above hold WHOLE experts (owner = id & 1,
9171    /// slot = id >> 1) at full width instead of TP shards. Consumers must branch on this;
9172    /// shard-semantics paths refuse loudly.
9173    pub(crate) ep2: bool,
9174}
9175
9176/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
9177/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
9178/// (token, layer) call so the decode loop performs zero output allocations.
9179/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
9180/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
9181/// conservatively) and the persistent e-context input staging its copies read.
9182struct RoutesGraph {
9183    exec: cudarc::driver::sys::CUgraphExec,
9184    parent: cudarc::driver::sys::CUgraph,
9185    _children: Vec<cudarc::driver::CudaGraph>,
9186}
9187// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
9188// context-agnostic process handles.
9189unsafe impl Send for RoutesGraph {}
9190
9191impl Drop for RoutesGraph {
9192    fn drop(&mut self) {
9193        unsafe {
9194            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
9195            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
9196        }
9197    }
9198}
9199
9200impl Nvfp4DeviceRoutesWorkspace {
9201    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
9202        self.in_stage_e.as_ref()
9203    }
9204    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
9205        self.in_stage_e.as_mut()
9206    }
9207    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
9208        self.out_stage_e.as_mut()
9209    }
9210    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
9211    pub(crate) fn arm_stages(
9212        &mut self,
9213        e: &Engine,
9214        width: usize,
9215        n_sel: usize,
9216    ) -> Result<(), Box<dyn std::error::Error>> {
9217        let _main = e.gpu.enter_main()?;
9218        if self.in_stage_e.is_none() {
9219            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
9220            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
9221        }
9222        if self.dev_route_e.is_none() {
9223            self.dev_route_e = Some((
9224                e.htod_i32(&vec![0i32; n_sel])?,
9225                e.htod(&vec![0.0f32; n_sel])?,
9226            ));
9227        }
9228        Ok(())
9229    }
9230
9231    /// Split-borrow: the routes input (shared) + output (mut) stages together.
9232    pub(crate) fn in_and_out_stages_mut(
9233        &mut self,
9234    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
9235        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
9236            (Some(input), Some(output)) => Some((input, output)),
9237            _ => None,
9238        }
9239    }
9240    pub(crate) fn dev_route_e_mut(
9241        &mut self,
9242    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
9243        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
9244    }
9245}
9246
9247pub struct Nvfp4DeviceRoutesWorkspace {
9248    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
9249    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
9250    gate_out: Vec<crate::CudaSlice<f32>>,
9251    up_out: Vec<crate::CudaSlice<f32>>,
9252    act_q: Vec<crate::CudaSlice<i8>>,
9253    act_d: Vec<crate::CudaSlice<f32>>,
9254    sel: Vec<crate::CudaSlice<i32>>,
9255    partial: Vec<crate::CudaSlice<f32>>,
9256    accumulator: Vec<crate::CudaSlice<f32>>,
9257    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
9258    combine_w: Vec<crate::CudaSlice<f32>>,
9259    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
9260    /// in-kernel via sel + macros_down_dev).
9261    route_w: Vec<crate::CudaSlice<f32>>,
9262    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
9263    /// per-call allocation).
9264    in_q: Vec<crate::CudaSlice<i8>>,
9265    in_d: Vec<crate::CudaSlice<f32>>,
9266    /// e-context staging for the device router outputs (persistent — rank streams peer-read
9267    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
9268    /// never-free discipline).
9269    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
9270    /// Prestage door state: input pull + quantize already issued for this layer's call
9271    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
9272    prestaged: bool,
9273    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
9274    /// the routed run skips rank1's sel pull. Reset per call.
9275    rank1_routed: bool,
9276    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
9277    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
9278    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
9279    fence_flags_raw: u64,
9280    fence_ticket: u32,
9281    /// Prestage input fence, recorded on e after the input's producer.
9282    ev_input: Option<(CudaEvent, usize)>,
9283    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
9284    /// captured copies read/write), and the per-layer stitched parent.
9285    in_stage_e: Option<crate::CudaSlice<f32>>,
9286    out_stage_e: Option<crate::CudaSlice<f32>>,
9287    routes_graph: Option<RoutesGraph>,
9288    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
9289    raw_dev_route_e: Option<(u64, u64)>,
9290    raw_combine: Option<(u64, u64, u64, u64)>,
9291    raw_input: Vec<u64>,
9292    raw_sel: Vec<u64>,
9293    raw_route_w: Vec<u64>,
9294    remote: crate::CudaSlice<f32>,
9295    combined: crate::CudaSlice<f32>,
9296    n_sel: usize,
9297    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
9298    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
9299    /// BoundarySlot discipline, same as the v2 attention workspace.
9300    input: Vec<crate::CudaSlice<f32>>,
9301    ev_rank: Vec<CudaEvent>,
9302    ev_done: Option<CudaEvent>,
9303    ev_entry: Option<(CudaEvent, usize)>,
9304}
9305
9306/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
9307struct ResidentNvfp4EpRank {
9308    gate: Vec<crate::CudaSlice<u8>>,
9309    up: Vec<crate::CudaSlice<u8>>,
9310    down: Vec<crate::CudaSlice<u8>>,
9311    #[allow(dead_code)]
9312    expert_range: Range<usize>,
9313}
9314
9315pub struct ResidentNvfp4ExpertParallel {
9316    ranks: Vec<ResidentNvfp4EpRank>,
9317    macros_gate: Vec<f32>,
9318    macros_up: Vec<f32>,
9319    macros_down: Vec<f32>,
9320    pub expert_count: usize,
9321    pub input_width: usize,
9322    pub expert_width: usize,
9323    gate_row_bytes: usize,
9324    down_row_bytes: usize,
9325}
9326
9327fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
9328    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
9329        matrix.codes,
9330        matrix.scales,
9331        matrix.out_features,
9332        matrix.in_features,
9333    )
9334}
9335
9336fn nvfp4_row_bytes(in_features: usize) -> usize {
9337    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
9338}
9339
9340/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
9341/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
9342/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
9343/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
9344/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
9345pub(crate) fn fuse_rope_append_on() -> bool {
9346    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9347    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
9348}
9349
9350pub(crate) fn no_local_shadow_on() -> bool {
9351    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9352    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
9353}
9354
9355/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
9356/// into the slot-major row layout the EP2 kernels read: per row, slot g's 16 qs bytes at
9357/// g*16, then the two UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count
9358/// unchanged. This layout USED to be an env door (`MEMRA_NVFP4_BANK_V2`, removed 2026-08-29
9359/// after its ON arm changed generated text in serving, see
9360/// research/step37-bankv2-removal-20260829); it survives ONLY as the fixed layout of the
9361/// EP2 whole-expert banks, whose `*_ep` kernels read it unconditionally.
9362fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
9363    let row_bytes = nvfp4_row_bytes(in_features);
9364    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
9365    let n_slots = in_features / 32;
9366    let mut out = Vec::with_capacity(v1.len());
9367    for row in 0..out_features {
9368        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
9369        for g in 0..n_slots {
9370            let (sblk, h) = (g / 2, g % 2);
9371            let b = &r[sblk * 36..sblk * 36 + 36];
9372            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
9373        }
9374        for g in 0..n_slots {
9375            let (sblk, h) = (g / 2, g % 2);
9376            let b = &r[sblk * 36..sblk * 36 + 36];
9377            out.push(b[2 * h]);
9378            out.push(b[2 * h + 1]);
9379        }
9380    }
9381    out
9382}
9383
9384/// Repack one expert shard for the contiguous banks. `slot_major` is true ONLY for the EP2
9385/// whole-expert banks, whose `*_ep` kernels read the slot-major permutation; the TP
9386/// column/row shard banks stay in the block_nvfp4 v1 layout every other kernel reads.
9387fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>, slot_major: bool) -> Vec<u8> {
9388    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
9389    let v1 = nvfp4_repack_matrix(matrix);
9390    if slot_major {
9391        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
9392    } else {
9393        v1
9394    }
9395}
9396
9397/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
9398/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
9399fn nvfp4_column_shard<'a>(
9400    matrix: Nvfp4BlockMatrix<'a>,
9401    tp: usize,
9402    rank: usize,
9403) -> Result<Nvfp4BlockMatrix<'a>, String> {
9404    if matrix.out_features % tp != 0 {
9405        return Err(format!(
9406            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
9407            matrix.out_features
9408        ));
9409    }
9410    let local_out = matrix.out_features / tp;
9411    let code_row = matrix.in_features / 2;
9412    let scale_row = matrix.in_features / 16;
9413    Ok(Nvfp4BlockMatrix {
9414        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
9415        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
9416        macro_scale: matrix.macro_scale,
9417        out_features: local_out,
9418        in_features: matrix.in_features,
9419    })
9420}
9421
9422/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
9423/// row contributes one contiguous byte window, gathered across rows.
9424fn nvfp4_row_shard(
9425    matrix: Nvfp4BlockMatrix<'_>,
9426    tp: usize,
9427    rank: usize,
9428) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
9429    if matrix.in_features % tp != 0 {
9430        return Err(format!(
9431            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
9432            matrix.in_features
9433        ));
9434    }
9435    let local_in = matrix.in_features / tp;
9436    if local_in % 64 != 0 {
9437        return Err(format!(
9438            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
9439        ));
9440    }
9441    let code_row = matrix.in_features / 2;
9442    let scale_row = matrix.in_features / 16;
9443    let local_code = local_in / 2;
9444    let local_scale = local_in / 16;
9445    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
9446    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
9447    for row in 0..matrix.out_features {
9448        let code_start = row * code_row + rank * local_code;
9449        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
9450        let scale_start = row * scale_row + rank * local_scale;
9451        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
9452    }
9453    Ok((codes, scales, local_in))
9454}
9455
9456/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
9457/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
9458/// point (see the section header).
9459fn run_rank_nvfp4(
9460    engine: &Engine,
9461    matrix: Nvfp4BlockMatrix<'_>,
9462    activations: &[f32],
9463    tokens: usize,
9464) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9465    matrix.validate()?;
9466    validate_activations(activations, tokens, matrix.in_features)?;
9467    let _main = engine.gpu.enter_main()?;
9468    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
9469    let activations = engine.htod(activations)?;
9470    let output = engine.qmatvec_nvfp4_fast(
9471        &blocks.slice(0..blocks.len()),
9472        &activations,
9473        tokens,
9474        matrix.in_features,
9475        matrix.out_features,
9476        nvfp4_row_bytes(matrix.in_features),
9477    )?;
9478    engine.dtoh(&output)
9479}
9480
9481fn upload_rank_nvfp4(
9482    engine: &Engine,
9483    matrix: Nvfp4BlockMatrix<'_>,
9484) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
9485    matrix.validate()?;
9486    let _main = engine.gpu.enter_main()?;
9487    Ok(ResidentNvfp4Rank {
9488        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
9489        macro_scale: matrix.macro_scale,
9490        out_features: matrix.out_features,
9491        in_features: matrix.in_features,
9492        row_bytes: nvfp4_row_bytes(matrix.in_features),
9493    })
9494}
9495
9496fn run_resident_rank_nvfp4(
9497    engine: &Engine,
9498    rank: &ResidentNvfp4Rank,
9499    activations: &[f32],
9500    tokens: usize,
9501) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9502    validate_activations(activations, tokens, rank.in_features)?;
9503    let _main = engine.gpu.enter_main()?;
9504    let activations = engine.htod(activations)?;
9505    let output = engine.qmatvec_nvfp4_fast(
9506        &rank.blocks.slice(0..rank.blocks.len()),
9507        &activations,
9508        tokens,
9509        rank.in_features,
9510        rank.out_features,
9511        rank.row_bytes,
9512    )?;
9513    engine.dtoh(&output)
9514}
9515
9516fn apply_macro(values: &mut [f32], macro_scale: f32) {
9517    for value in values.iter_mut() {
9518        *value *= macro_scale;
9519    }
9520}
9521
9522impl TpE4m3HostBounce {
9523    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
9524    pub fn full_nvfp4(
9525        &self,
9526        matrix: Nvfp4BlockMatrix<'_>,
9527        activations: &[f32],
9528        tokens: usize,
9529    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9530        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
9531        apply_macro(&mut output, matrix.macro_scale);
9532        Ok(output)
9533    }
9534
9535    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
9536    /// order, macro applied ONCE post-gather.
9537    pub fn column_parallel_nvfp4(
9538        &self,
9539        matrix: Nvfp4BlockMatrix<'_>,
9540        activations: &[f32],
9541        tokens: usize,
9542    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
9543        matrix.validate()?;
9544        validate_activations(activations, tokens, matrix.in_features)?;
9545        let tp = self.ranks.len();
9546        let local_out = matrix.out_features / tp;
9547        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9548        let mut rank_outputs = Vec::with_capacity(tp);
9549        for (rank_index, rank) in self.ranks.iter().enumerate() {
9550            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
9551            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
9552            let row_start = rank_index * local_out;
9553            for token in 0..tokens {
9554                gathered[token * matrix.out_features + row_start
9555                    ..token * matrix.out_features + row_start + local_out]
9556                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9557            }
9558            rank_outputs.push(output);
9559        }
9560        apply_macro(&mut gathered, matrix.macro_scale);
9561        Ok(ColumnParallelResult {
9562            gathered,
9563            rank_outputs,
9564        })
9565    }
9566
9567    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
9568    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
9569    pub fn row_parallel_nvfp4(
9570        &self,
9571        matrix: Nvfp4BlockMatrix<'_>,
9572        activations: &[f32],
9573        tokens: usize,
9574    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
9575        matrix.validate()?;
9576        validate_activations(activations, tokens, matrix.in_features)?;
9577        let tp = self.ranks.len();
9578        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9579        let mut rank_partials = Vec::with_capacity(tp);
9580        for (rank_index, rank) in self.ranks.iter().enumerate() {
9581            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
9582            let local_activations =
9583                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9584            let shard = Nvfp4BlockMatrix {
9585                codes: &codes,
9586                scales: &scales,
9587                macro_scale: matrix.macro_scale,
9588                out_features: matrix.out_features,
9589                in_features: local_in,
9590            };
9591            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
9592            for (sum, value) in reduced.iter_mut().zip(&partial) {
9593                *sum += *value;
9594            }
9595            rank_partials.push(partial);
9596        }
9597        apply_macro(&mut reduced, matrix.macro_scale);
9598        Ok(RowParallelResult {
9599            reduced,
9600            rank_partials,
9601        })
9602    }
9603
9604    pub fn upload_expert_nvfp4(
9605        &self,
9606        gate: Nvfp4BlockMatrix<'_>,
9607        up: Nvfp4BlockMatrix<'_>,
9608        down: Nvfp4BlockMatrix<'_>,
9609    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
9610        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9611            return Err("NVFP4 TP expert gate/up dimensions differ".into());
9612        }
9613        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9614            return Err(format!(
9615                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
9616                down.out_features, down.in_features, gate.out_features, gate.in_features
9617            )
9618            .into());
9619        }
9620        let tp = self.ranks.len();
9621        let mut gate_ranks = Vec::with_capacity(tp);
9622        let mut up_ranks = Vec::with_capacity(tp);
9623        let mut down_ranks = Vec::with_capacity(tp);
9624        for (rank_index, engine) in self.ranks.iter().enumerate() {
9625            gate_ranks.push(upload_rank_nvfp4(
9626                engine,
9627                nvfp4_column_shard(gate, tp, rank_index)?,
9628            )?);
9629            up_ranks.push(upload_rank_nvfp4(
9630                engine,
9631                nvfp4_column_shard(up, tp, rank_index)?,
9632            )?);
9633            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
9634            down_ranks.push(upload_rank_nvfp4(
9635                engine,
9636                Nvfp4BlockMatrix {
9637                    codes: &codes,
9638                    scales: &scales,
9639                    macro_scale: down.macro_scale,
9640                    out_features: down.out_features,
9641                    in_features: local_in,
9642                },
9643            )?);
9644        }
9645        Ok(ResidentTpNvfp4Expert {
9646            gate: ResidentNvfp4ColumnParallel {
9647                ranks: gate_ranks,
9648                out_features: gate.out_features,
9649                in_features: gate.in_features,
9650            },
9651            up: ResidentNvfp4ColumnParallel {
9652                ranks: up_ranks,
9653                out_features: up.out_features,
9654                in_features: up.in_features,
9655            },
9656            down: ResidentNvfp4RowParallel {
9657                ranks: down_ranks,
9658                out_features: down.out_features,
9659                in_features: down.in_features,
9660            },
9661            input_width: gate.in_features,
9662            expert_width: gate.out_features,
9663        })
9664    }
9665
9666    fn column_parallel_resident_nvfp4(
9667        &self,
9668        matrix: &ResidentNvfp4ColumnParallel,
9669        activations: &[f32],
9670        tokens: usize,
9671    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9672        validate_activations(activations, tokens, matrix.in_features)?;
9673        let local_out = matrix.out_features / self.ranks.len();
9674        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9675        let mut macro_scale = None;
9676        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9677            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
9678            let row_start = rank_index * local_out;
9679            for token in 0..tokens {
9680                gathered[token * matrix.out_features + row_start
9681                    ..token * matrix.out_features + row_start + local_out]
9682                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9683            }
9684            macro_scale = Some(shard.macro_scale);
9685        }
9686        apply_macro(
9687            &mut gathered,
9688            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
9689        );
9690        Ok(gathered)
9691    }
9692
9693    fn row_parallel_resident_nvfp4(
9694        &self,
9695        matrix: &ResidentNvfp4RowParallel,
9696        activations: &[f32],
9697        tokens: usize,
9698    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9699        validate_activations(activations, tokens, matrix.in_features)?;
9700        let tp = self.ranks.len();
9701        let local_in = matrix.in_features / tp;
9702        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9703        let mut macro_scale = None;
9704        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9705            if shard.in_features != local_in {
9706                return Err(format!(
9707                    "NVFP4 resident row shard in_features {} != expected {local_in}",
9708                    shard.in_features
9709                )
9710                .into());
9711            }
9712            let local_activations =
9713                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9714            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
9715            for (sum, value) in reduced.iter_mut().zip(&partial) {
9716                *sum += *value;
9717            }
9718            macro_scale = Some(shard.macro_scale);
9719        }
9720        apply_macro(
9721            &mut reduced,
9722            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
9723        );
9724        Ok(reduced)
9725    }
9726
9727    pub fn run_expert_nvfp4(
9728        &self,
9729        expert: &ResidentTpNvfp4Expert,
9730        input: &[f32],
9731        tokens: usize,
9732    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9733        validate_activations(input, tokens, expert.input_width)?;
9734        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
9735        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
9736        let activated: Vec<f32> = gate
9737            .iter()
9738            .zip(&up)
9739            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
9740            .collect();
9741        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
9742        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
9743    }
9744
9745    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
9746    pub fn upload_tensor_parallel_nvfp4(
9747        &self,
9748        gate: Nvfp4ExpertBank<'_>,
9749        up: Nvfp4ExpertBank<'_>,
9750        down: Nvfp4ExpertBank<'_>,
9751    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
9752        gate.validate()?;
9753        up.validate()?;
9754        down.validate()?;
9755        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9756            return Err("NVFP4 TP gate/up/down expert counts differ".into());
9757        }
9758        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9759            return Err("NVFP4 TP gate/up dimensions differ".into());
9760        }
9761        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9762            return Err(format!(
9763                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
9764                down.out_features, down.in_features, gate.out_features, gate.in_features
9765            )
9766            .into());
9767        }
9768        let tp = self.ranks.len();
9769        if gate.out_features % tp != 0 {
9770            return Err(format!(
9771                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
9772                gate.out_features
9773            )
9774            .into());
9775        }
9776        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
9777            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
9778        {
9779            return Err(format!(
9780                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
9781                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
9782                down.in_features
9783            )
9784            .into());
9785        }
9786        if tp > NVFP4_CANONICAL_ROW_SHARDS {
9787            return Err(format!(
9788                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
9789                 ({NVFP4_CANONICAL_ROW_SHARDS})"
9790            )
9791            .into());
9792        }
9793
9794        let ep2 = step_nvfp4_ep2_on() && tp == 2;
9795        let mut gate_ranks = Vec::with_capacity(tp);
9796        let mut up_ranks = Vec::with_capacity(tp);
9797        let mut macros_gate_dev = Vec::with_capacity(tp);
9798        let mut macros_up_dev = Vec::with_capacity(tp);
9799        let mut macros_down_dev = Vec::with_capacity(tp);
9800        for (rank_index, engine) in self.ranks.iter().enumerate() {
9801            let _main = engine.gpu.enter_main()?;
9802            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
9803            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
9804            // are unchanged (same repack).
9805            // EP2: this rank holds the FULL matrices of the experts it owns (id & 1 ==
9806            // rank_index), stacked at slot id >> 1 — same total bytes as the shard bank.
9807            let mut gate_host: Vec<u8> = Vec::new();
9808            let mut up_host: Vec<u8> = Vec::new();
9809            let mut owned = 0usize;
9810            for expert in 0..gate.expert_count {
9811                if ep2 {
9812                    if expert % 2 != rank_index {
9813                        continue;
9814                    }
9815                    owned += 1;
9816                    gate_host
9817                        .extend_from_slice(&nvfp4_repack_bank_matrix(gate.expert(expert)?, true));
9818                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up.expert(expert)?, true));
9819                } else {
9820                    let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
9821                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard, false));
9822                    let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
9823                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard, false));
9824                }
9825            }
9826            let bank_experts = if ep2 { owned } else { gate.expert_count };
9827            let gate_expert_bytes = gate_host.len() / bank_experts.max(1);
9828            let up_expert_bytes = up_host.len() / bank_experts.max(1);
9829            let local_out = if ep2 {
9830                gate.out_features
9831            } else {
9832                gate.out_features / tp
9833            };
9834            gate_ranks.push(ResidentNvfp4ColumnBankRank {
9835                bank: engine.htod_bytes(&gate_host)?,
9836                expert_bytes: gate_expert_bytes,
9837                local_out,
9838                in_features: gate.in_features,
9839                row_bytes: nvfp4_row_bytes(gate.in_features),
9840            });
9841            up_ranks.push(ResidentNvfp4ColumnBankRank {
9842                bank: engine.htod_bytes(&up_host)?,
9843                expert_bytes: up_expert_bytes,
9844                local_out,
9845                in_features: up.in_features,
9846                row_bytes: nvfp4_row_bytes(up.in_features),
9847            });
9848            macros_gate_dev.push(engine.htod(gate.macros)?);
9849            macros_up_dev.push(engine.htod(up.macros)?);
9850            macros_down_dev.push(engine.htod(down.macros)?);
9851        }
9852        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
9853        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
9854        // execution and reduction order stay identical.
9855        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
9856        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
9857            let device_rank = shard_index % tp;
9858            let engine = &self.ranks[device_rank];
9859            let _main = engine.gpu.enter_main()?;
9860            let mut down_host: Vec<u8> = Vec::new();
9861            let mut owned = 0usize;
9862            for expert in 0..down.expert_count {
9863                let down_matrix = down.expert(expert)?;
9864                if ep2 {
9865                    // EP2: shard_index doubles as the owner rank; full-width down matrices
9866                    // of the owned experts, stacked at slot id >> 1.
9867                    if expert % 2 != device_rank {
9868                        continue;
9869                    }
9870                    owned += 1;
9871                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(down_matrix, true));
9872                } else {
9873                    let (codes, scales, local_in) =
9874                        nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
9875                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(
9876                        Nvfp4BlockMatrix {
9877                            codes: &codes,
9878                            scales: &scales,
9879                            macro_scale: down_matrix.macro_scale,
9880                            out_features: down_matrix.out_features,
9881                            in_features: local_in,
9882                        },
9883                        false,
9884                    ));
9885                }
9886            }
9887            let bank_experts = if ep2 { owned } else { down.expert_count };
9888            let down_expert_bytes = down_host.len() / bank_experts.max(1);
9889            let local_in = if ep2 {
9890                down.in_features
9891            } else {
9892                down.in_features / NVFP4_CANONICAL_ROW_SHARDS
9893            };
9894            down_ranks.push(ResidentNvfp4RowBankRank {
9895                bank: engine.htod_bytes(&down_host)?,
9896                expert_bytes: down_expert_bytes,
9897                device_rank,
9898                out_features: down.out_features,
9899                local_in,
9900                row_bytes: nvfp4_row_bytes(local_in),
9901            });
9902        }
9903        Ok(ResidentNvfp4TensorParallel {
9904            gate: gate_ranks,
9905            up: up_ranks,
9906            down: down_ranks,
9907            macros_gate: gate.macros.to_vec(),
9908            macros_up: up.macros.to_vec(),
9909            macros_down: down.macros.to_vec(),
9910            macros_gate_dev,
9911            macros_up_dev,
9912            macros_down_dev,
9913            expert_count: gate.expert_count,
9914            input_width: gate.in_features,
9915            expert_width: gate.out_features,
9916            device_workspace: std::sync::Mutex::new(None),
9917            prime_tables: std::sync::Mutex::new(Vec::new()),
9918            ep2,
9919        })
9920    }
9921
9922    /// EP2 host-canonical: the whole expert executes on its owning rank at full width
9923    /// (owner = expert & 1, bank slot = expert >> 1). Per-row program == the column-bank
9924    /// path's kernel, so gate/up are bit-equal to the TP layout.
9925    fn run_full_bank_expert_nvfp4(
9926        &self,
9927        ranks: &[ResidentNvfp4ColumnBankRank],
9928        macros: &[f32],
9929        expert: usize,
9930        input: &[f32],
9931    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9932        let owner = expert & 1;
9933        let slot = expert >> 1;
9934        let bank = ranks
9935            .get(owner)
9936            .ok_or("NVFP4 EP2 column bank missing owner rank")?;
9937        let engine = &self.ranks[owner];
9938        let _main = engine.gpu.enter_main()?;
9939        let activations = engine.htod(input)?;
9940        // EP2 banks are ALWAYS slot-major (see nvfp4_repack_bank_matrix), so the oracle
9941        // must be the slot-major reader.
9942        let output = engine.qmatvec_nvfp4_fast_v2(
9943            &bank.expert(slot),
9944            &activations,
9945            1,
9946            bank.in_features,
9947            bank.local_out,
9948            bank.row_bytes,
9949        )?;
9950        let mut out = engine.dtoh(&output)?;
9951        apply_macro(&mut out, macros[expert]);
9952        Ok(out)
9953    }
9954
9955    /// EP2 host-canonical down: one full-width dot on the owner (NUMERIC-CLASS vs the
9956    /// canonical 2-shard sum — the parenthesization this door declares).
9957    fn run_full_down_expert_nvfp4(
9958        &self,
9959        shards: &[ResidentNvfp4RowBankRank],
9960        macros: &[f32],
9961        expert: usize,
9962        input: &[f32],
9963    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9964        let owner = expert & 1;
9965        let slot = expert >> 1;
9966        let shard = shards
9967            .get(owner)
9968            .ok_or("NVFP4 EP2 down bank missing owner rank")?;
9969        let engine = &self.ranks[owner];
9970        let _main = engine.gpu.enter_main()?;
9971        let activations = engine.htod(input)?;
9972        // EP2 down banks are ALWAYS slot-major (see nvfp4_repack_bank_matrix).
9973        let output = engine.qmatvec_nvfp4_fast_v2(
9974            &shard.expert(slot),
9975            &activations,
9976            1,
9977            shard.local_in,
9978            shard.out_features,
9979            shard.row_bytes,
9980        )?;
9981        let mut out = engine.dtoh(&output)?;
9982        apply_macro(&mut out, macros[expert]);
9983        Ok(out)
9984    }
9985
9986    fn run_column_bank_expert_nvfp4(
9987        &self,
9988        ranks: &[ResidentNvfp4ColumnBankRank],
9989        macros: &[f32],
9990        expert: usize,
9991        input: &[f32],
9992    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9993        let local_out = ranks
9994            .first()
9995            .ok_or("NVFP4 TP column bank has no ranks")?
9996            .local_out;
9997        let mut gathered = vec![0.0f32; local_out * ranks.len()];
9998        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
9999            let _main = engine.gpu.enter_main()?;
10000            let activations = engine.htod(input)?;
10001            let output = engine.qmatvec_nvfp4_fast(
10002                &bank.expert(expert),
10003                &activations,
10004                1,
10005                bank.in_features,
10006                bank.local_out,
10007                bank.row_bytes,
10008            )?;
10009            let output = engine.dtoh(&output)?;
10010            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
10011        }
10012        apply_macro(&mut gathered, macros[expert]);
10013        Ok(gathered)
10014    }
10015
10016    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
10017    /// executes on its owning rank engine), so the reduction parenthesization is identical at
10018    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
10019    fn run_row_bank_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 out_features = shards
10027            .first()
10028            .ok_or("NVFP4 TP row bank has no canonical shards")?
10029            .out_features;
10030        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
10031        let mut reduced = vec![0.0f32; out_features];
10032        for (shard_index, shard) in shards.iter().enumerate() {
10033            let engine = self
10034                .ranks
10035                .get(shard.device_rank)
10036                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
10037            let _main = engine.gpu.enter_main()?;
10038            let local_activations =
10039                activation_shard(input, 1, in_features, shards.len(), shard_index);
10040            let activations = engine.htod(&local_activations)?;
10041            let output = engine.qmatvec_nvfp4_fast(
10042                &shard.expert(expert),
10043                &activations,
10044                1,
10045                shard.local_in,
10046                shard.out_features,
10047                shard.row_bytes,
10048            )?;
10049            let partial = engine.dtoh(&output)?;
10050            for (sum, value) in reduced.iter_mut().zip(&partial) {
10051                *sum += *value;
10052            }
10053        }
10054        apply_macro(&mut reduced, macros[expert]);
10055        Ok(reduced)
10056    }
10057
10058    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
10059    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
10060    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
10061    pub fn upload_expert_parallel_nvfp4(
10062        &self,
10063        gate: Nvfp4ExpertBank<'_>,
10064        up: Nvfp4ExpertBank<'_>,
10065        down: Nvfp4ExpertBank<'_>,
10066    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
10067        gate.validate()?;
10068        up.validate()?;
10069        down.validate()?;
10070        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
10071            return Err("NVFP4 EP gate/up/down expert counts differ".into());
10072        }
10073        if gate.in_features != up.in_features || gate.out_features != up.out_features {
10074            return Err("NVFP4 EP gate/up dimensions differ".into());
10075        }
10076        if down.in_features != gate.out_features || down.out_features != gate.in_features {
10077            return Err(format!(
10078                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
10079                down.out_features, down.in_features, gate.out_features, gate.in_features
10080            )
10081            .into());
10082        }
10083        let world = self.ranks.len();
10084        if gate.expert_count % world != 0 {
10085            return Err(format!(
10086                "NVFP4 EP expert count {} is not divisible by {world} ranks",
10087                gate.expert_count
10088            )
10089            .into());
10090        }
10091        let experts_per_rank = gate.expert_count / world;
10092        let mut ranks = Vec::with_capacity(world);
10093        for (rank_index, engine) in self.ranks.iter().enumerate() {
10094            let _main = engine.gpu.enter_main()?;
10095            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
10096            let mut gate_experts = Vec::with_capacity(experts_per_rank);
10097            let mut up_experts = Vec::with_capacity(experts_per_rank);
10098            let mut down_experts = Vec::with_capacity(experts_per_rank);
10099            for expert in expert_range.clone() {
10100                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
10101                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
10102                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
10103            }
10104            ranks.push(ResidentNvfp4EpRank {
10105                gate: gate_experts,
10106                up: up_experts,
10107                down: down_experts,
10108                expert_range,
10109            });
10110        }
10111        Ok(ResidentNvfp4ExpertParallel {
10112            ranks,
10113            macros_gate: gate.macros.to_vec(),
10114            macros_up: up.macros.to_vec(),
10115            macros_down: down.macros.to_vec(),
10116            expert_count: gate.expert_count,
10117            input_width: gate.in_features,
10118            expert_width: gate.out_features,
10119            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
10120            down_row_bytes: nvfp4_row_bytes(down.in_features),
10121        })
10122    }
10123
10124    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
10125    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
10126    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
10127    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
10128    /// contract. Exactness-first; no throughput claim.
10129    #[allow(clippy::too_many_arguments)]
10130    pub fn run_routed_experts_nvfp4(
10131        &self,
10132        experts: &ResidentNvfp4ExpertParallel,
10133        input: &[f32],
10134        tokens: usize,
10135        selected: &[usize],
10136        route_weights: &[f32],
10137        experts_per_token: usize,
10138        activation_limit: Option<f32>,
10139    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10140        validate_activations(input, tokens, experts.input_width)?;
10141        let pairs = tokens
10142            .checked_mul(experts_per_token)
10143            .ok_or("NVFP4 EP route count overflow")?;
10144        if selected.len() != pairs || route_weights.len() != pairs {
10145            return Err(format!(
10146                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
10147                 {experts_per_token} ({pairs})",
10148                selected.len(),
10149                route_weights.len(),
10150            )
10151            .into());
10152        }
10153        if !route_weights.iter().all(|weight| weight.is_finite()) {
10154            return Err("NVFP4 EP route weights contain a non-finite value".into());
10155        }
10156        let experts_per_rank = experts.expert_count / experts.ranks.len();
10157        let mut output = vec![0.0f32; tokens * experts.input_width];
10158        for token in 0..tokens {
10159            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
10160            for slot in 0..experts_per_token {
10161                let pair = token * experts_per_token + slot;
10162                let expert = selected[pair];
10163                if expert >= experts.expert_count {
10164                    return Err(format!(
10165                        "NVFP4 EP selected expert {expert} outside 0..{}",
10166                        experts.expert_count
10167                    )
10168                    .into());
10169                }
10170                let owner = expert / experts_per_rank;
10171                let local = expert - owner * experts_per_rank;
10172                let rank = &experts.ranks[owner];
10173                let engine = &self.ranks[owner];
10174                let _main = engine.gpu.enter_main()?;
10175                let device_input = engine.htod(input_row)?;
10176                let gate_out = engine.qmatvec_nvfp4_fast(
10177                    &rank.gate[local].slice(0..rank.gate[local].len()),
10178                    &device_input,
10179                    1,
10180                    experts.input_width,
10181                    experts.expert_width,
10182                    experts.gate_row_bytes,
10183                )?;
10184                let up_out = engine.qmatvec_nvfp4_fast(
10185                    &rank.up[local].slice(0..rank.up[local].len()),
10186                    &device_input,
10187                    1,
10188                    experts.input_width,
10189                    experts.expert_width,
10190                    experts.gate_row_bytes,
10191                )?;
10192                let mut gate_host = engine.dtoh(&gate_out)?;
10193                let mut up_host = engine.dtoh(&up_out)?;
10194                apply_macro(&mut gate_host, experts.macros_gate[expert]);
10195                apply_macro(&mut up_host, experts.macros_up[expert]);
10196                let activated: Vec<f32> = gate_host
10197                    .iter()
10198                    .zip(&up_host)
10199                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
10200                    .collect();
10201                let device_activated = engine.htod(&activated)?;
10202                let down_out = engine.qmatvec_nvfp4_fast(
10203                    &rank.down[local].slice(0..rank.down[local].len()),
10204                    &device_activated,
10205                    1,
10206                    experts.expert_width,
10207                    experts.input_width,
10208                    experts.down_row_bytes,
10209                )?;
10210                let mut down_host = engine.dtoh(&down_out)?;
10211                apply_macro(&mut down_host, experts.macros_down[expert]);
10212                let weight = route_weights[pair];
10213                for (sum, value) in output
10214                    [token * experts.input_width..(token + 1) * experts.input_width]
10215                    .iter_mut()
10216                    .zip(down_host)
10217                {
10218                    *sum += weight * value;
10219                }
10220            }
10221        }
10222        Ok(output)
10223    }
10224
10225    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
10226    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
10227    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
10228    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
10229    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
10230    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
10231    ///
10232    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
10233    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
10234    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
10235    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
10236    /// host-canonical oracle, and with repeat determinism against itself.
10237    /// Clamped layers refuse (they stay on the EP program).
10238    pub fn run_tensor_parallel_routes_nvfp4_device(
10239        &self,
10240        experts: &ResidentNvfp4TensorParallel,
10241        input: &[f32],
10242        selected: &[usize],
10243        route_weights: &[f32],
10244        experts_per_token: usize,
10245        activation_limit: Option<f32>,
10246    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
10247        validate_activations(input, 1, experts.input_width)?;
10248        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10249            return Err(format!(
10250                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
10251                selected.len(),
10252                route_weights.len(),
10253            )
10254            .into());
10255        }
10256        if !route_weights.iter().all(|weight| weight.is_finite()) {
10257            return Err("NVFP4 device route weights contain a non-finite value".into());
10258        }
10259        let world = self.ranks.len();
10260        if world != NVFP4_CANONICAL_ROW_SHARDS {
10261            return Err(format!(
10262                "NVFP4 device routes require world == canonical shard grid \
10263                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10264            )
10265            .into());
10266        }
10267        let local_out = if experts.ep2 {
10268            experts.expert_width
10269        } else {
10270            experts.expert_width / world
10271        };
10272
10273        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
10274        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
10275        // everything else without Nsight.
10276        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10277        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10278        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10279        let started = timing.then(std::time::Instant::now);
10280
10281        let n_sel = experts_per_token;
10282        let mut workspace_guard = experts
10283            .device_workspace
10284            .lock()
10285            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10286        if workspace_guard.is_none() {
10287            let mut gate_out = Vec::with_capacity(world);
10288            let mut up_out = Vec::with_capacity(world);
10289            let mut act_q = Vec::with_capacity(world);
10290            let mut act_d = Vec::with_capacity(world);
10291            let mut sel = Vec::with_capacity(world);
10292            let mut partial = Vec::with_capacity(world);
10293            let mut accumulator = Vec::with_capacity(world);
10294            let mut combine_w = Vec::with_capacity(world);
10295            let mut route_w = Vec::with_capacity(world);
10296            let mut in_q = Vec::with_capacity(world);
10297            let mut in_d = Vec::with_capacity(world);
10298            let mut input = Vec::with_capacity(world);
10299            let mut ev_rank = Vec::with_capacity(world);
10300            let moe_direct = moe_direct_on();
10301            for (rank, engine) in self.ranks.iter().enumerate() {
10302                let _main = engine.gpu.enter_main()?;
10303                gate_out.push(engine.uninit(n_sel * local_out)?);
10304                up_out.push(engine.uninit(n_sel * local_out)?);
10305                act_q.push(engine.uninit_i8(n_sel * local_out)?);
10306                act_d.push(engine.uninit(n_sel * local_out / 32)?);
10307                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
10308                partial.push(engine.uninit(n_sel * experts.input_width)?);
10309                // Direct join: peer accumulators live on ROOT (single P2P store pass).
10310                if moe_direct && rank != 0 {
10311                    let root = &self.ranks[0];
10312                    let _root_main = root.gpu.enter_main()?;
10313                    accumulator.push(root.zeros(experts.input_width)?);
10314                } else {
10315                    accumulator.push(engine.zeros(experts.input_width)?);
10316                }
10317                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
10318                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
10319                in_q.push(engine.uninit_i8(experts.input_width)?);
10320                in_d.push(engine.uninit(experts.input_width / 32)?);
10321                input.push(engine.uninit(experts.input_width)?);
10322                ev_rank.push(engine.ctx().new_event(None)?);
10323            }
10324            let root = &self.ranks[0];
10325            let _main = root.gpu.enter_main()?;
10326            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
10327                prestaged: false,
10328                rank1_routed: false,
10329                ev_input: None,
10330                fence_flags_raw: 0,
10331                fence_ticket: 0,
10332                gate_out,
10333                up_out,
10334                act_q,
10335                act_d,
10336                sel,
10337                partial,
10338                accumulator,
10339                combine_w,
10340                route_w,
10341                in_q,
10342                in_d,
10343                dev_route_e: None,
10344                in_stage_e: None,
10345                out_stage_e: None,
10346                routes_graph: None,
10347                raw_dev_route_e: None,
10348                raw_combine: None,
10349                raw_input: Vec::new(),
10350                raw_sel: Vec::new(),
10351                raw_route_w: Vec::new(),
10352                remote: root.uninit(experts.input_width)?,
10353                combined: root.uninit(experts.input_width)?,
10354                n_sel,
10355                input,
10356                ev_rank,
10357                ev_done: Some(root.ctx().new_event(None)?),
10358                ev_entry: None,
10359            });
10360        }
10361        let workspace = workspace_guard
10362            .as_mut()
10363            .expect("NVFP4 device routes workspace initialized above");
10364        // EP2 uses this call only as the workspace-arming warmup (the prejoin path drives
10365        // decode); its host-routed sweep semantics do not apply to whole-expert banks.
10366        if experts.ep2 {
10367            return Ok(vec![0.0f32; experts.input_width]);
10368        }
10369        if workspace.n_sel != n_sel {
10370            return Err(format!(
10371                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10372                workspace.n_sel
10373            )
10374            .into());
10375        }
10376        for &expert in selected {
10377            if expert >= experts.expert_count {
10378                return Err(format!(
10379                    "NVFP4 device selected expert {expert} outside 0..{}",
10380                    experts.expert_count
10381                )
10382                .into());
10383            }
10384        }
10385        let sel_i32 = selected
10386            .iter()
10387            .map(|&expert| expert as i32)
10388            .collect::<Vec<_>>();
10389
10390        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
10391        // down) covers every selected expert via the selection array and the contiguous bank —
10392        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
10393        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
10394        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
10395        // accumulation order — the program's values are unchanged.
10396        for (rank_index, engine) in self.ranks.iter().enumerate() {
10397            let _main = engine.gpu.enter_main()?;
10398            let device_input = engine.htod(input)?;
10399            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
10400            engine.quantize_q8_1_into(
10401                &device_input,
10402                1,
10403                experts.input_width,
10404                &mut in_q[rank_index],
10405                &mut in_d[rank_index],
10406            )?;
10407            // device_input frees on this rank's stream after the quantize — same-stream order.
10408        }
10409        self.nvfp4_routes_batched_sweeps(
10410            experts,
10411            workspace,
10412            selected,
10413            route_weights,
10414            &sel_i32,
10415            local_out,
10416            n_sel,
10417            activation_limit,
10418            false,
10419        )?;
10420
10421        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
10422        // reduce in canonical shard order, read back once.
10423        let root = &self.ranks[0];
10424        for engine in &self.ranks[1..] {
10425            let _main = engine.gpu.enter_main()?;
10426            engine.stream().synchronize()?;
10427        }
10428        let _main = root.gpu.enter_main()?;
10429        root.stream()
10430            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10431        root.add(
10432            &workspace.accumulator[0],
10433            &workspace.remote,
10434            &mut workspace.combined,
10435            experts.input_width,
10436        )?;
10437        let output = root.dtoh(&workspace.combined)?;
10438        if let Some(started) = started {
10439            use std::sync::atomic::Ordering;
10440            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10441                + started.elapsed().as_nanos() as u64;
10442            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10443            if calls % 430 == 0 {
10444                eprintln!(
10445                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10446                    ns as f64 / 1.0e6,
10447                    ns as f64 / calls as f64 / 1.0e3,
10448                );
10449            }
10450        }
10451        Ok(output)
10452    }
10453
10454    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
10455    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
10456    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
10457    /// owning rank's stream; callers own input acquisition and the combine.
10458    #[allow(clippy::too_many_arguments)]
10459    fn nvfp4_routes_batched_sweeps(
10460        &self,
10461        experts: &ResidentNvfp4TensorParallel,
10462        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10463        selected: &[usize],
10464        route_weights: &[f32],
10465        sel_i32: &[i32],
10466        local_out: usize,
10467        n_sel: usize,
10468        activation_limit: Option<f32>,
10469        device_routed: bool,
10470    ) -> Result<(), Box<dyn std::error::Error>> {
10471        for rank_index in 0..self.ranks.len() {
10472            self.nvfp4_routes_batched_sweeps_rank(
10473                experts,
10474                workspace,
10475                selected,
10476                route_weights,
10477                sel_i32,
10478                local_out,
10479                n_sel,
10480                activation_limit,
10481                device_routed,
10482                rank_index,
10483            )?;
10484        }
10485        Ok(())
10486    }
10487
10488    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
10489    /// the graph door can capture each rank's segment on its own stream.
10490    #[allow(clippy::too_many_arguments)]
10491    fn nvfp4_routes_batched_sweeps_rank(
10492        &self,
10493        experts: &ResidentNvfp4TensorParallel,
10494        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10495        selected: &[usize],
10496        route_weights: &[f32],
10497        sel_i32: &[i32],
10498        local_out: usize,
10499        n_sel: usize,
10500        activation_limit: Option<f32>,
10501        device_routed: bool,
10502        rank_index: usize,
10503    ) -> Result<(), Box<dyn std::error::Error>> {
10504        {
10505            let engine = &self.ranks[rank_index];
10506            let _main = engine.gpu.enter_main()?;
10507            // EP2: whole-expert full-width sweep, owner-guarded; down+combine fused writes
10508            // this rank's slot-ordered partial straight into its accumulator (the join is
10509            // unchanged). Device-routed only — the host-routed arm and the graph door refuse
10510            // at the caller.
10511            if experts.ep2 {
10512                if !device_routed {
10513                    return Err("NVFP4 EP2 banks support the device-routed decode arm only".into());
10514                }
10515                let gate_bank = &experts.gate[rank_index];
10516                let up_bank = &experts.up[rank_index];
10517                if gate_bank.local_out != experts.expert_width
10518                    || gate_bank.expert_bytes != up_bank.expert_bytes
10519                {
10520                    return Err("NVFP4 EP2 bank geometry drifted".into());
10521                }
10522                {
10523                    let Nvfp4DeviceRoutesWorkspace {
10524                        sel,
10525                        gate_out,
10526                        up_out,
10527                        in_q,
10528                        in_d,
10529                        ..
10530                    } = &mut *workspace;
10531                    engine.qmatvec_nvfp4_sel_gu_ep_into(
10532                        &gate_bank.bank,
10533                        &up_bank.bank,
10534                        &sel[rank_index],
10535                        &in_q[rank_index],
10536                        &in_d[rank_index],
10537                        &mut gate_out[rank_index],
10538                        &mut up_out[rank_index],
10539                        n_sel,
10540                        gate_bank.in_features,
10541                        gate_bank.local_out,
10542                        gate_bank.row_bytes,
10543                        gate_bank.expert_bytes,
10544                        rank_index,
10545                    )?;
10546                }
10547                {
10548                    let Nvfp4DeviceRoutesWorkspace {
10549                        gate_out,
10550                        up_out,
10551                        sel,
10552                        act_q,
10553                        act_d,
10554                        ..
10555                    } = &mut *workspace;
10556                    engine.silu_mul_scaled_q8_1_sel_ep_into(
10557                        &gate_out[rank_index],
10558                        &up_out[rank_index],
10559                        &experts.macros_gate_dev[rank_index],
10560                        &experts.macros_up_dev[rank_index],
10561                        &sel[rank_index],
10562                        activation_limit,
10563                        &mut act_q[rank_index],
10564                        &mut act_d[rank_index],
10565                        local_out,
10566                        n_sel,
10567                        rank_index,
10568                    )?;
10569                }
10570                let shard = &experts.down[rank_index];
10571                if shard.device_rank != rank_index || shard.local_in != local_out {
10572                    return Err("NVFP4 EP2 down bank placement drifted".into());
10573                }
10574                {
10575                    let Nvfp4DeviceRoutesWorkspace {
10576                        sel,
10577                        act_q,
10578                        act_d,
10579                        route_w,
10580                        accumulator,
10581                        ..
10582                    } = &mut *workspace;
10583                    engine.qmatvec_nvfp4_sel_down8_ep_into(
10584                        &shard.bank,
10585                        &sel[rank_index],
10586                        &act_q[rank_index],
10587                        &act_d[rank_index],
10588                        &route_w[rank_index],
10589                        &experts.macros_down_dev[rank_index],
10590                        &mut accumulator[rank_index],
10591                        n_sel,
10592                        shard.local_in,
10593                        shard.out_features,
10594                        shard.row_bytes,
10595                        shard.expert_bytes,
10596                        local_out,
10597                        local_out / 32,
10598                        rank_index,
10599                    )?;
10600                }
10601                return Ok(());
10602            }
10603            if !device_routed {
10604                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
10605                // Folded combine weights (route_weight x down macro) — one 40-byte upload
10606                // replaces the accumulator reset + n_sel sequential axpy launches below.
10607                let folded = (0..n_sel)
10608                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
10609                    .collect::<Vec<_>>();
10610                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
10611                engine.stream().memcpy_htod(&folded, &mut view)?;
10612            }
10613            let gate_bank = &experts.gate[rank_index];
10614            let up_bank = &experts.up[rank_index];
10615            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
10616            engine.qmatvec_nvfp4_sel_into(
10617                &gate_bank.bank,
10618                &workspace.sel[rank_index],
10619                aq,
10620                ad,
10621                &mut workspace.gate_out[rank_index],
10622                n_sel,
10623                gate_bank.in_features,
10624                gate_bank.local_out,
10625                gate_bank.row_bytes,
10626                gate_bank.expert_bytes,
10627                0,
10628                0,
10629            )?;
10630            engine.qmatvec_nvfp4_sel_into(
10631                &up_bank.bank,
10632                &workspace.sel[rank_index],
10633                aq,
10634                ad,
10635                &mut workspace.up_out[rank_index],
10636                n_sel,
10637                up_bank.in_features,
10638                up_bank.local_out,
10639                up_bank.row_bytes,
10640                up_bank.expert_bytes,
10641                0,
10642                0,
10643            )?;
10644            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
10645            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
10646            // input-column window (the geometry gift; see the method doc).
10647            {
10648                let Nvfp4DeviceRoutesWorkspace {
10649                    gate_out,
10650                    up_out,
10651                    sel,
10652                    act_q,
10653                    act_d,
10654                    ..
10655                } = &mut *workspace;
10656                engine.silu_mul_scaled_q8_1_sel_into(
10657                    &gate_out[rank_index],
10658                    &up_out[rank_index],
10659                    &experts.macros_gate_dev[rank_index],
10660                    &experts.macros_up_dev[rank_index],
10661                    &sel[rank_index],
10662                    activation_limit,
10663                    &mut act_q[rank_index],
10664                    &mut act_d[rank_index],
10665                    local_out,
10666                    n_sel,
10667                )?;
10668            }
10669            let shard = &experts.down[rank_index];
10670            if shard.device_rank != rank_index || shard.local_in != local_out {
10671                return Err(
10672                    "NVFP4 device routes: down canonical shard placement drifted from \
10673                     the gate/up column split"
10674                        .into(),
10675                );
10676            }
10677            {
10678                let Nvfp4DeviceRoutesWorkspace {
10679                    sel,
10680                    act_q,
10681                    act_d,
10682                    partial,
10683                    ..
10684                } = &mut *workspace;
10685                engine.qmatvec_nvfp4_sel_into(
10686                    &shard.bank,
10687                    &sel[rank_index],
10688                    &act_q[rank_index],
10689                    &act_d[rank_index],
10690                    &mut partial[rank_index],
10691                    n_sel,
10692                    shard.local_in,
10693                    shard.out_features,
10694                    shard.row_bytes,
10695                    shard.expert_bytes,
10696                    local_out,
10697                    local_out / 32,
10698                )?;
10699            }
10700            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
10701            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
10702            // fold the down macro in-kernel from the device selection.
10703            {
10704                let Nvfp4DeviceRoutesWorkspace {
10705                    partial,
10706                    combine_w,
10707                    route_w,
10708                    sel,
10709                    accumulator,
10710                    ..
10711                } = &mut *workspace;
10712                if device_routed {
10713                    engine.axpy_rows_seq_md_into(
10714                        &partial[rank_index],
10715                        &route_w[rank_index],
10716                        &experts.macros_down_dev[rank_index],
10717                        &sel[rank_index],
10718                        &mut accumulator[rank_index],
10719                        experts.input_width,
10720                        n_sel,
10721                    )?;
10722                } else {
10723                    engine.axpy_rows_seq_into(
10724                        &partial[rank_index],
10725                        &combine_w[rank_index],
10726                        &mut accumulator[rank_index],
10727                        experts.input_width,
10728                        n_sel,
10729                    )?;
10730                }
10731            }
10732        }
10733        Ok(())
10734    }
10735
10736    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
10737    /// a device row on the model engine `e` and the combined output returns as a fresh
10738    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
10739    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
10740    /// the input's producer; each rank waits it before its peer read; the root reduce waits
10741    /// every rank's done event; `e` waits the root's done event before copying out. The
10742    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
10743    pub fn run_tensor_parallel_routes_nvfp4_device_io(
10744        &self,
10745        experts: &ResidentNvfp4TensorParallel,
10746        e: &Engine,
10747        input_dev: &crate::CudaSlice<f32>,
10748        selected: &[usize],
10749        route_weights: &[f32],
10750        experts_per_token: usize,
10751        activation_limit: Option<f32>,
10752    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10753        if input_dev.len() != experts.input_width {
10754            return Err(format!(
10755                "NVFP4 device-io routes input {} != width {}",
10756                input_dev.len(),
10757                experts.input_width
10758            )
10759            .into());
10760        }
10761        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10762            return Err(format!(
10763                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
10764                selected.len(),
10765                route_weights.len(),
10766            )
10767            .into());
10768        }
10769        if !route_weights.iter().all(|weight| weight.is_finite()) {
10770            return Err("NVFP4 device route weights contain a non-finite value".into());
10771        }
10772        let world = self.ranks.len();
10773        if world != NVFP4_CANONICAL_ROW_SHARDS {
10774            return Err(format!(
10775                "NVFP4 device routes require world == canonical shard grid \
10776                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10777            )
10778            .into());
10779        }
10780        let local_out = experts.expert_width / world;
10781        let n_sel = experts_per_token;
10782
10783        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10784        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10785        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10786        let started = timing.then(std::time::Instant::now);
10787
10788        let mut workspace_guard = experts
10789            .device_workspace
10790            .lock()
10791            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10792        if workspace_guard.is_none() {
10793            drop(workspace_guard);
10794            // Build through the host-IO ensure path exactly once: run it with a zero input.
10795            // Cheaper than duplicating the init; the first real call overwrites everything.
10796            let zero = vec![0.0f32; experts.input_width];
10797            let zero_sel = vec![0usize; n_sel];
10798            let zero_w = vec![0.0f32; n_sel];
10799            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10800                experts,
10801                &zero,
10802                &zero_sel,
10803                &zero_w,
10804                n_sel,
10805                activation_limit,
10806            )?;
10807            workspace_guard = experts
10808                .device_workspace
10809                .lock()
10810                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10811        }
10812        let workspace = workspace_guard
10813            .as_mut()
10814            .expect("NVFP4 device routes workspace initialized above");
10815        if workspace.n_sel != n_sel {
10816            return Err(format!(
10817                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10818                workspace.n_sel
10819            )
10820            .into());
10821        }
10822        for &expert in selected {
10823            if expert >= experts.expert_count {
10824                return Err(format!(
10825                    "NVFP4 device selected expert {expert} outside 0..{}",
10826                    experts.expert_count
10827                )
10828                .into());
10829            }
10830        }
10831        let sel_i32 = selected
10832            .iter()
10833            .map(|&expert| expert as i32)
10834            .collect::<Vec<_>>();
10835
10836        // Entry fence: e's stream position covers the input's producer AND every consumer of
10837        // the previous layer's output (queued on e's stream before this call), guarding the
10838        // workspace reuse exactly like the v2 attention driver.
10839        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10840            if *device != e.ctx().ordinal() {
10841                return Err("NVFP4 device-io routes engine changed".into());
10842            }
10843        } else {
10844            let _main = e.gpu.enter_main()?;
10845            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10846        }
10847        {
10848            let _main = e.gpu.enter_main()?;
10849            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10850            ev_entry.record(&e.stream())?;
10851        }
10852        for (rank_index, engine) in self.ranks.iter().enumerate() {
10853            let _main = engine.gpu.enter_main()?;
10854            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10855            engine.stream().wait(ev_entry)?;
10856            {
10857                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10858                engine
10859                    .stream()
10860                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10861            }
10862            {
10863                let Nvfp4DeviceRoutesWorkspace {
10864                    input, in_q, in_d, ..
10865                } = &mut *workspace;
10866                engine.quantize_q8_1_into(
10867                    &input[rank_index],
10868                    1,
10869                    experts.input_width,
10870                    &mut in_q[rank_index],
10871                    &mut in_d[rank_index],
10872                )?;
10873            }
10874        }
10875        self.nvfp4_routes_batched_sweeps(
10876            experts,
10877            workspace,
10878            selected,
10879            route_weights,
10880            &sel_i32,
10881            local_out,
10882            n_sel,
10883            activation_limit,
10884            false,
10885        )?;
10886
10887        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
10888        // the root stream in canonical shard order, and e copies the combined row out behind
10889        // the root's done event.
10890        // rank0 == root: its own stream order already covers its sweep; only the PEER
10891        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
10892        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
10893            let _main = engine.gpu.enter_main()?;
10894            workspace.ev_rank[rank_index].record(&engine.stream())?;
10895        }
10896        if moe_direct_on() && self.ranks.len() == 2 {
10897            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
10898            // rank0's is root-stream-ordered. One root event + rank1's own event order
10899            // the model engine's single add — same operand order as root's add
10900            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
10901            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
10902            // hazard class does not apply).
10903            {
10904                let root = &self.ranks[0];
10905                let _main = root.gpu.enter_main()?;
10906                workspace
10907                    .ev_done
10908                    .as_ref()
10909                    .expect("device routes done event")
10910                    .record(&root.stream())?;
10911            }
10912            let _main = e.gpu.enter_main()?;
10913            e.stream().wait(
10914                workspace
10915                    .ev_done
10916                    .as_ref()
10917                    .expect("device routes done event"),
10918            )?;
10919            for ev in workspace.ev_rank.iter().skip(1) {
10920                e.stream().wait(ev)?;
10921            }
10922            let mut output = e.uninit(experts.input_width)?;
10923            e.add(
10924                &workspace.accumulator[0],
10925                &workspace.accumulator[1],
10926                &mut output,
10927                experts.input_width,
10928            )?;
10929            let output = output;
10930            if let Some(started) = started {
10931                use std::sync::atomic::Ordering;
10932                let ns = TIMING_NS
10933                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10934                    + started.elapsed().as_nanos() as u64;
10935                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10936                if calls % 430 == 0 {
10937                    eprintln!(
10938                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10939                        ns as f64 / 1.0e6,
10940                        ns as f64 / calls as f64 / 1.0e3,
10941                    );
10942                }
10943            }
10944            return Ok(output);
10945        }
10946        {
10947            let root = &self.ranks[0];
10948            let _main = root.gpu.enter_main()?;
10949            for ev in workspace.ev_rank.iter().skip(1) {
10950                root.stream().wait(ev)?;
10951            }
10952            root.stream()
10953                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10954            {
10955                let Nvfp4DeviceRoutesWorkspace {
10956                    accumulator,
10957                    remote,
10958                    combined,
10959                    ..
10960                } = &mut *workspace;
10961                root.add(&accumulator[0], remote, combined, experts.input_width)?;
10962            }
10963            workspace
10964                .ev_done
10965                .as_ref()
10966                .expect("device routes done event")
10967                .record(&root.stream())?;
10968        }
10969        let output = {
10970            let _main = e.gpu.enter_main()?;
10971            e.stream().wait(
10972                workspace
10973                    .ev_done
10974                    .as_ref()
10975                    .expect("device routes done event"),
10976            )?;
10977            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
10978            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
10979            let mut output = e.uninit(experts.input_width)?;
10980            e.stream().memcpy_dtod(
10981                &workspace.combined.slice(0..experts.input_width),
10982                &mut output.slice_mut(0..experts.input_width),
10983            )?;
10984            output
10985        };
10986        if let Some(started) = started {
10987            use std::sync::atomic::Ordering;
10988            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10989                + started.elapsed().as_nanos() as u64;
10990            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10991            if calls % 430 == 0 {
10992                eprintln!(
10993                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10994                    ns as f64 / 1.0e6,
10995                    ns as f64 / calls as f64 / 1.0e3,
10996                );
10997            }
10998        }
10999        Ok(output)
11000    }
11001
11002    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
11003    /// route weights arrive as the device router's e-context outputs — the per-layer host
11004    /// logits readback disappears. The fresh router outputs are staged into persistent
11005    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
11006    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
11007    #[allow(clippy::too_many_arguments)]
11008    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
11009    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
11010    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
11011    /// the routed run then does its own staging as before.
11012    pub fn nvfp4_routes_prestage(
11013        &self,
11014        experts: &ResidentNvfp4TensorParallel,
11015        e: &Engine,
11016        input_dev: &crate::CudaSlice<f32>,
11017    ) -> Result<bool, Box<dyn std::error::Error>> {
11018        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
11019    }
11020
11021    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
11022    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
11023    /// deterministic kernels on identical input bits produce identical sel/w, so the
11024    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
11025    /// routed run then skips rank1's sel pull.
11026    pub fn nvfp4_routes_prestage_with(
11027        &self,
11028        experts: &ResidentNvfp4TensorParallel,
11029        e: &Engine,
11030        input_dev: &crate::CudaSlice<f32>,
11031        rank1_router: impl FnOnce(
11032            &Engine,
11033            &crate::CudaSlice<f32>,
11034            &mut crate::CudaSlice<i32>,
11035            &mut crate::CudaSlice<f32>,
11036        ) -> Result<bool, Box<dyn std::error::Error>>,
11037    ) -> Result<bool, Box<dyn std::error::Error>> {
11038        if !routes_prestage_on() || step_tp_graph_enabled()? {
11039            return Ok(false);
11040        }
11041        if input_dev.len() != experts.input_width {
11042            return Err("NVFP4 prestage input width mismatch".into());
11043        }
11044        let mut workspace_guard = experts
11045            .device_workspace
11046            .lock()
11047            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11048        let Some(workspace) = workspace_guard.as_mut() else {
11049            return Ok(false);
11050        };
11051        if workspace.ev_input.is_none() {
11052            let _main = e.gpu.enter_main()?;
11053            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
11054        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
11055            return Err("NVFP4 prestage engine changed".into());
11056        }
11057        {
11058            let _main = e.gpu.enter_main()?;
11059            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
11060            ev.record(&e.stream())?;
11061        }
11062        for (rank_index, engine) in self.ranks.iter().enumerate() {
11063            let _main = engine.gpu.enter_main()?;
11064            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
11065            engine.stream().wait(ev)?;
11066            {
11067                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
11068                engine
11069                    .stream()
11070                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
11071            }
11072            {
11073                let Nvfp4DeviceRoutesWorkspace {
11074                    input, in_q, in_d, ..
11075                } = &mut *workspace;
11076                engine.quantize_q8_1_into(
11077                    &input[rank_index],
11078                    1,
11079                    experts.input_width,
11080                    &mut in_q[rank_index],
11081                    &mut in_d[rank_index],
11082                )?;
11083            }
11084        }
11085        if self.ranks.len() == 2 {
11086            let rank1 = &self.ranks[1];
11087            let _r1 = rank1.gpu.enter_main()?;
11088            let Nvfp4DeviceRoutesWorkspace {
11089                input,
11090                sel,
11091                route_w,
11092                ..
11093            } = &mut *workspace;
11094            let (in1, rest_sel) = (&input[1], &mut sel[1]);
11095            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
11096                workspace.rank1_routed = true;
11097            }
11098        }
11099        workspace.prestaged = true;
11100        Ok(true)
11101    }
11102
11103    /// STEP TP2 GEMM PRIME (`MEMRA_STEP_GEMM_PRIME`, 2026-08-27, TTFT lane): one grouped
11104    /// f16 GEMM per projection over the RESIDENT NVFP4 banks for a prime chunk of `t` tokens.
11105    ///
11106    /// WHY: the t-row walk primes a 4,092-token prompt in 19.8 s at its widest (GEMV-bound) and
11107    /// the generic batch prime's decode-class MoE takes 240 s; the CUTLASS sizing rows put
11108    /// GEMM-class expert math at 170-270 TFLOP/s on this silicon, i.e. a sub-second cold prime.
11109    /// This reuses the grouped f16 lane end to end (`moe_f16g_act` -> `moe_f16_grouped`
11110    /// direct-from-NVFP4 -> silu pairs -> grouped down) once per RANK against that rank's bank
11111    /// half: gate/up are column-halves (silu runs on matching halves), down is the canonical
11112    /// row-shard pair producing partials joined in the pinned shard order, and the final
11113    /// weighted scatter runs a fixed slot-0..n_used-1 sum per token - no atomics anywhere.
11114    /// Per-expert NVFP4 macro scales land where they must: gate/up BEFORE silu (nonlinear),
11115    /// down folded into the scatter weight.
11116    ///
11117    /// NUMERIC CLASS: the f16-mirror grouped-prefill class other families already serve -
11118    /// admission is the prefill-KV acceptance gate plus the ship-shape tape, not byte identity.
11119    #[allow(clippy::too_many_arguments)]
11120    /// MEMRA_MOE_DETERM_STAGE=1: checksum a stage's device buffer so two back-to-back calls of the
11121    /// grouped routine can be compared STAGE BY STAGE. The routine's OUTPUT is nondeterministic above
11122    /// ~400 tokens on the direct lane (1.9e-7 / 99% of elements at t=4096) while its GEMM kernels are
11123    /// bit-exact in isolation, so the divergence enters somewhere between. The first stage whose
11124    /// checksum differs across the two calls is where.
11125    ///
11126    /// Sum-of-bits, not sum-of-floats: float addition would itself reorder and could mask exactly the
11127    /// class of difference being hunted.
11128    fn determ_stage_bytes(v: &[u8]) -> u64 {
11129        v.iter().fold(0u64, |a, b| {
11130            a.wrapping_mul(1_000_003).wrapping_add(*b as u64)
11131        })
11132    }
11133
11134    /// Checksum an i32 index/offset buffer. The CSR, the active-expert ids and the group
11135    /// offsets are inputs the gate kernel dereferences just as much as the activations are;
11136    /// leaving them unchecksummed is what let "identical inputs, different output" stand on a
11137    /// SUBSET of the inputs for six rounds of this investigation.
11138    fn determ_stage_i32(v: &[i32]) -> u64 {
11139        v.iter().fold(0u64, |a, b| {
11140            a.wrapping_mul(1_000_003).wrapping_add(*b as u32 as u64)
11141        })
11142    }
11143
11144    fn determ_stage_sum(v: &[f32]) -> u64 {
11145        v.iter().fold(0u64, |a, x| {
11146            a.wrapping_mul(1_000_003).wrapping_add(x.to_bits() as u64)
11147        })
11148    }
11149
11150    pub fn run_tensor_parallel_routes_nvfp4_prime_grouped(
11151        &self,
11152        experts: &ResidentNvfp4TensorParallel,
11153        e: &Engine,
11154        z_t: &crate::CudaSlice<f32>,
11155        t: usize,
11156        sel: &[i32],
11157        w: &[f32],
11158        n_used: usize,
11159        activation_limit: Option<f32>,
11160    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11161        let world = self.ranks.len();
11162        if world != NVFP4_CANONICAL_ROW_SHARDS {
11163            return Err("NVFP4 grouped prime requires the canonical 2-shard grid".into());
11164        }
11165        // The dequant must read the layout the bank was BUILT in (feeding slot-major bytes
11166        // to the v1 kernel was a garbage-output bug this line exists for). The layout is a
11167        // property of the bank now, never of the environment: EP2 banks are always
11168        // slot-major, TP shard banks always block_nvfp4 v1 (nvfp4_repack_bank_matrix).
11169        let bank_qt = if experts.ep2 {
11170            crate::QT_NVFP4_V2
11171        } else {
11172            crate::QT_NVFP4
11173        };
11174        let width = experts.input_width;
11175        let n_expert = experts.expert_count;
11176        let n_pairs = t * n_used;
11177        if sel.len() < n_pairs || w.len() < n_pairs || z_t.len() < t * width {
11178            return Err("NVFP4 grouped prime geometry".into());
11179        }
11180        // MEMRA_PRIME_PROF=1 sub-split of the grouped prime (2026-08-28). The [moe-prof] mark
11181        // around this whole call reads 90% of the MoE bucket, but the call is not just GEMMs:
11182        // it host-builds the CSR, allocates ~6 large device buffers per rank per layer (z_r is
11183        // 67 MB, act is 84 MB at t=4096), and does 5 H2D copies per rank. Tile form, occupancy,
11184        // padding, B double-buffering and register pressure have ALL come back null, which is
11185        // the signature of time that is not in the kernel. So measure HOST wall with no syncs
11186        // for the build and the issue, and let the join wait absorb the GPU time: host-bound and
11187        // GPU-bound then read differently instead of summing into one opaque number.
11188        let gprof = std::env::var("MEMRA_PRIME_PROF").as_deref() == Ok("1") && t >= 16;
11189        let g_t0 = std::time::Instant::now();
11190        // CSR: expert-major pair lists. Host-built - prime is chunk-granular, and the router
11191        // selections arrive host-side from the sigmoid router oracle.
11192        let mut buckets: Vec<Vec<i32>> = vec![Vec::new(); n_expert];
11193        for (p, &s_id) in sel.iter().take(n_pairs).enumerate() {
11194            let s_id = s_id as usize;
11195            if s_id >= n_expert {
11196                return Err(format!("grouped prime selection {s_id} >= {n_expert}").into());
11197            }
11198            buckets[s_id].push(p as i32);
11199        }
11200        let mut ex_ids: Vec<i32> = Vec::new();
11201        let mut ex_off: Vec<i32> = vec![0];
11202        let mut ex_pairs: Vec<i32> = Vec::new();
11203        for (e_id, b) in buckets.iter().enumerate() {
11204            if !b.is_empty() {
11205                ex_ids.push(e_id as i32);
11206                ex_pairs.extend_from_slice(b);
11207                ex_off.push(ex_pairs.len() as i32);
11208            }
11209        }
11210        let n_active = ex_ids.len();
11211        if n_active == 0 {
11212            return Ok(e.zeros(t * width)?);
11213        }
11214        if n_active > 512 {
11215            return Err("grouped prime n_active > 512 (direct lane cap)".into());
11216        }
11217        let csr_tok: Vec<i32> = ex_pairs.iter().map(|&p| p / n_used as i32).collect();
11218        // pair-id -> CSR row: lets the fused tail read the partials in place, so the prime skips
11219        // a whole [n_pairs, width] permute (532 MB read + write per rank per layer at 4k).
11220        let mut inv = vec![0i32; n_pairs];
11221        for (row, &pair) in ex_pairs.iter().enumerate() {
11222            inv[pair as usize] = row as i32;
11223        }
11224        // Per-CSR-row gate/up macro scales (before silu); down macro folds into the scatter w.
11225        let mg: Vec<f32> = ex_pairs
11226            .iter()
11227            .map(|&p| experts.macros_gate[sel[p as usize] as usize])
11228            .collect();
11229        let mu: Vec<f32> = ex_pairs
11230            .iter()
11231            .map(|&p| experts.macros_up[sel[p as usize] as usize])
11232            .collect();
11233        let wd: Vec<f32> = (0..n_pairs)
11234            .map(|p| w[p] * experts.macros_down[sel[p] as usize])
11235            .collect();
11236        // Pointer tables: built on first use and kept on the bank. Resident banks never move,
11237        // so the old per-rank-per-LAYER rebuild+upload of 3*n_expert u64s was pure prime-path
11238        // host churn (45 layers x 2 ranks x 864 entries per prime).
11239        {
11240            let mut tabs = experts
11241                .prime_tables
11242                .lock()
11243                .map_err(|_| "grouped prime table cache is poisoned")?;
11244            if tabs.len() != world {
11245                tabs.clear();
11246                for rank in 0..world {
11247                    let engine = &self.ranks[rank];
11248                    let _main = engine.gpu.enter_main()?;
11249                    let (gb, ub, db) =
11250                        (&experts.gate[rank], &experts.up[rank], &experts.down[rank]);
11251                    let mut tab = vec![0u64; 3 * n_expert];
11252                    {
11253                        use cudarc::driver::DevicePtr;
11254                        let stream = engine.stream();
11255                        let (pg, _g0) = gb.bank.device_ptr(&stream);
11256                        let (pu, _g1) = ub.bank.device_ptr(&stream);
11257                        let (pd, _g2) = db.bank.device_ptr(&stream);
11258                        for ex in 0..n_expert {
11259                            tab[ex] = pg as u64 + (ex * gb.expert_bytes) as u64;
11260                            tab[n_expert + ex] = pu as u64 + (ex * ub.expert_bytes) as u64;
11261                            tab[2 * n_expert + ex] = pd as u64 + (ex * db.expert_bytes) as u64;
11262                        }
11263                    }
11264                    tabs.push(engine.htod_u64(&tab)?);
11265                }
11266            }
11267        }
11268        let g_csr = g_t0.elapsed().as_secs_f64() * 1e3;
11269        let g_t1 = std::time::Instant::now();
11270        // WHAT ARE THESE RANKS, ACTUALLY (2026-08-28)? The grouped MoE measures join ~ span_sum
11271        // (strictly serialized) at t=4096 while the same kernel hits 40 TFLOP/s standalone, and
11272        // one intervention based on cudarc's peer-copy event was refuted. Before proposing an
11273        // eleventh mechanism, verify the premise the whole question rests on: that the two ranks
11274        // are on DISTINCT devices, contexts and streams. If they share any of those, the
11275        // serialization needs no further explanation. One line per process.
11276        {
11277            static SAID: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
11278            if gprof && !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
11279                for rank in 0..world {
11280                    let e_r = &self.ranks[rank];
11281                    let _m = e_r.gpu.enter_main();
11282                    eprintln!(
11283                        "[rank-id] rank={rank} ordinal={} ctx={:?} stream={:?} root_ordinal={} \
11284                         root_stream={:?}",
11285                        e_r.ctx().ordinal(),
11286                        std::sync::Arc::as_ptr(&e_r.ctx()),
11287                        e_r.stream().cu_stream(),
11288                        e.ctx().ordinal(),
11289                        e.stream().cu_stream(),
11290                    );
11291                }
11292            }
11293        }
11294
11295        let mut partials: Vec<crate::CudaSlice<f32>> = Vec::with_capacity(world);
11296        let mut ev_rank: Vec<CudaEvent> = Vec::with_capacity(world);
11297        let mut ev_head: Vec<CudaEvent> = Vec::with_capacity(world);
11298        let mut ev_tail_prof: Vec<CudaEvent> = Vec::with_capacity(world);
11299        for rank in 0..world {
11300            let engine = &self.ranks[rank];
11301            let _main = engine.gpu.enter_main()?;
11302            if gprof {
11303                // CU_EVENT_DEFAULT, not None: cudarc's new_event(None) creates the event with
11304                // CU_EVENT_DISABLE_TIMING, and cuEventElapsedTime then returns INVALID_HANDLE.
11305                // That is what failed every span query for two build cycles — the ordering
11306                // events below correctly keep the default, since they are never timed.
11307                let h = engine
11308                    .ctx()
11309                    .new_event(Some(cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT))?;
11310                h.record(&engine.stream())?;
11311                ev_head.push(h);
11312            }
11313            // The grouped-MoE FFI's raw launches follow the RUNTIME API's current device, not
11314            // the pushed driver context — bind it per rank or rank-1 calls die InvalidValue.
11315            engine.bind_runtime_device(engine.ctx().ordinal() as i32)?;
11316            let gb = &experts.gate[rank];
11317            let ub = &experts.up[rank];
11318            let db = &experts.down[rank];
11319            if db.device_rank != rank {
11320                return Err("grouped prime: down shard placement drifted".into());
11321            }
11322            let local_ff = gb.local_out;
11323            if ub.local_out != local_ff || db.local_in != local_ff || db.out_features != width {
11324                return Err("grouped prime: bank width mismatch".into());
11325            }
11326            // All of the rank's host-side staging lands before its first kernel, so the
11327            // launch chain below issues without host copies interleaved.
11328            let csr_tok_d = engine.htod_i32(&csr_tok)?;
11329            let exi_d = engine.htod_i32(&ex_ids)?;
11330            let exoff_d = engine.htod_i32(&ex_off)?;
11331            let mg_d = engine.htod(&mg)?;
11332            let mu_d = engine.htod(&mu)?;
11333            // Per-rank pointer table into the bank shards, slot-major like DevExps::ptr_row.
11334            let tabs_guard = experts
11335                .prime_tables
11336                .lock()
11337                .map_err(|_| "grouped prime table cache is poisoned")?;
11338            let tab_d = &tabs_guard[rank];
11339            let mut z_r = engine.uninit(t * width)?;
11340            {
11341                let mut dst = z_r.slice_mut(0..t * width);
11342                engine
11343                    .stream()
11344                    .memcpy_dtod(&z_t.slice(0..t * width), &mut dst)?;
11345            }
11346            let dstage = std::env::var("MEMRA_MOE_DETERM_STAGE").as_deref() == Ok("1") && t >= 16;
11347            let (z16, zs) = engine.moe_f16g_act(&z_r, Some(&csr_tok_d), width, n_pairs)?;
11348            if dstage {
11349                // z16 is the GEMM's actual DATA input and is a byte buffer; checksumming only
11350                // z_r and zs left "identical inputs" unestablished and produced a localization
11351                // that outran the measurement. Checksum it as bytes.
11352                let zr = engine.dtoh(&z_r)?;
11353                let zsv = engine.dtoh(&zs)?;
11354                let z16v = engine.dtoh_u8(&z16)?;
11355                eprintln!(
11356                    "[determ-stage] rank={rank} t={t} z_r={:016x} zs={:016x} z16={:016x}",
11357                    Self::determ_stage_sum(&zr),
11358                    Self::determ_stage_sum(&zsv),
11359                    Self::determ_stage_bytes(&z16v)
11360                );
11361            }
11362            if dstage {
11363                // INPUT CLOSURE. Everything the gate kernel dereferences, plus the launch
11364                // geometry that decides how it is summed, checksummed in ONE place. A kernel
11365                // proven bit-deterministic on live data, with no atomics, can only diverge if
11366                // (A) some byte it reads differs, (B) the launch differs, or (C) it reads
11367                // outside its declared inputs. This closes A and B; C is what compute-sanitizer
11368                // is for. Partial input sets are how the divergence kept retreating into the
11369                // part that was never measured.
11370                engine.stream().synchronize()?;
11371                let csr_v = engine.dtoh_i32(&csr_tok_d)?;
11372                let exi_v = engine.dtoh_i32(&exi_d)?;
11373                let exo_v = engine.dtoh_i32(&exoff_d)?;
11374                let mg_v = engine.dtoh(&mg_d)?;
11375                let mu_v = engine.dtoh(&mu_d)?;
11376                let tab_v = engine.dtoh_u64(tab_d)?;
11377                eprintln!(
11378                    "[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={}",
11379                    Self::determ_stage_i32(&csr_v),
11380                    Self::determ_stage_i32(&exi_v),
11381                    Self::determ_stage_i32(&exo_v),
11382                    Self::determ_stage_i32(&ex_off),
11383                    Self::determ_stage_sum(&mg_v),
11384                    Self::determ_stage_sum(&mu_v),
11385                    tab_v
11386                        .iter()
11387                        .fold(0u64, |a, b| a.wrapping_mul(1_000_003).wrapping_add(*b)),
11388                    gb.row_bytes
11389                );
11390                // The resident weight bank is the GEMM's OTHER operand and was never checked.
11391                // Opt-in because it is a ~424 MB dtoh per rank per layer.
11392                if std::env::var("MEMRA_MOE_DETERM_BANK").as_deref() == Ok("1") {
11393                    let bank_v = engine.dtoh_u8(&gb.bank)?;
11394                    eprintln!(
11395                        "[determ-closure] rank={rank} t={t} gate_bank={:016x} bytes={}",
11396                        Self::determ_stage_bytes(&bank_v),
11397                        bank_v.len()
11398                    );
11399                }
11400            }
11401            let mut g = engine.moe_f16_grouped(
11402                tab_d,
11403                0,
11404                n_expert,
11405                &exi_d,
11406                &ex_off,
11407                &exoff_d,
11408                &z16,
11409                &zs,
11410                width,
11411                local_ff,
11412                n_active,
11413                n_pairs,
11414                bank_qt,
11415                gb.row_bytes,
11416            )?;
11417            engine.scale_rows(&mut g, &mg_d, local_ff, n_pairs)?;
11418            let mut u = engine.moe_f16_grouped(
11419                tab_d,
11420                1,
11421                n_expert,
11422                &exi_d,
11423                &ex_off,
11424                &exoff_d,
11425                &z16,
11426                &zs,
11427                width,
11428                local_ff,
11429                n_active,
11430                n_pairs,
11431                bank_qt,
11432                ub.row_bytes,
11433            )?;
11434            engine.scale_rows(&mut u, &mu_d, local_ff, n_pairs)?;
11435            // step35 routed SwiGLU clamp (per-layer; live only on layers 43/44 for this
11436            // family): min(silu(g), lim) * clamp(u, +-lim). Dropping it was the second
11437            // correctness bug of the first engaged run.
11438            let act = match activation_limit.filter(|l| *l > 1e-6) {
11439                Some(lim) => {
11440                    let mut a = engine.uninit(n_pairs * local_ff)?;
11441                    engine.swiglu_clamped_mul_scaled(
11442                        &g,
11443                        &u,
11444                        1.0,
11445                        1.0,
11446                        lim,
11447                        &mut a,
11448                        n_pairs * local_ff,
11449                    )?;
11450                    a
11451                }
11452                None => engine.moe_pairs_silu_mul(&g, &u, n_pairs * local_ff)?,
11453            };
11454            if dstage {
11455                let gv = engine.dtoh(&g)?;
11456                let uv = engine.dtoh(&u)?;
11457                let av = engine.dtoh(&act)?;
11458                // A SUM tells you THAT gate differs; it does not tell you HOW. ULP-dense diffs
11459                // (nearly every element, ~1e-8) are an ordering/precision class; a handful of
11460                // huge ones are a corruption class. They need different hunts, so measure the
11461                // shape here instead of inferring it later.
11462                let key = (rank, t);
11463                let mut prev_map = DETERM_PREV
11464                    .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()))
11465                    .lock()
11466                    .map_err(|_| "determ prev map poisoned")?;
11467                let shape = match prev_map.get(&key) {
11468                    Some(prev) if prev.len() == gv.len() => {
11469                        let mut md = 0.0f32;
11470                        let mut n_diff = 0usize;
11471                        let mut n_big = 0usize;
11472                        for (a, b) in prev.iter().zip(gv.iter()) {
11473                            let d = (a - b).abs();
11474                            if d > 0.0 {
11475                                n_diff += 1;
11476                            }
11477                            if d > 1e-3 {
11478                                n_big += 1;
11479                            }
11480                            if d > md {
11481                                md = d;
11482                            }
11483                        }
11484                        format!(
11485                            " | vs_prev maxdiff={md:.3e} differing={n_diff}/{} big(>1e-3)={n_big}",
11486                            gv.len()
11487                        )
11488                    }
11489                    _ => String::new(),
11490                };
11491                prev_map.insert(key, gv.clone());
11492                drop(prev_map);
11493                eprintln!(
11494                    "[determ-stage] rank={rank} t={t} gate={:016x} up={:016x} silu={:016x}{shape}",
11495                    Self::determ_stage_sum(&gv),
11496                    Self::determ_stage_sum(&uv),
11497                    Self::determ_stage_sum(&av)
11498                );
11499            }
11500            let (a16, a_s) = engine.moe_f16g_act(&act, None, local_ff, n_pairs)?;
11501            let d_csr = engine.moe_f16_grouped(
11502                tab_d,
11503                2,
11504                n_expert,
11505                &exi_d,
11506                &ex_off,
11507                &exoff_d,
11508                &a16,
11509                &a_s,
11510                local_ff,
11511                width,
11512                n_active,
11513                n_pairs,
11514                bank_qt,
11515                db.row_bytes,
11516            )?;
11517
11518            // No host sync: both ranks' chains must be in flight before anything waits.
11519            // The rank's tail event orders the root's cross-device pulls below.
11520            if dstage {
11521                engine.stream().synchronize()?;
11522                let a16v = engine.dtoh_u8(&a16)?;
11523                let dv = engine.dtoh(&d_csr)?;
11524                eprintln!(
11525                    "[determ-stage] rank={rank} t={t} a16={:016x} down_partial={:016x}",
11526                    Self::determ_stage_bytes(&a16v),
11527                    Self::determ_stage_sum(&dv)
11528                );
11529            }
11530            let ev = engine.ctx().new_event(None)?;
11531            ev.record(&engine.stream())?;
11532            if gprof {
11533                // Per-rank GPU SPAN (2026-08-28). Keep the tail event; the elapsed time is read
11534                // AFTER the join sync below. Reading it here returns NOT_READY (the work has only
11535                // been queued) and cudarc's elapsed_ms synchronizes, which serialized the very
11536                // ranks this is meant to test: host issue jumped 1.9 ms -> 34-47 ms per call and
11537                // the join wall fell to match. A probe that changes the schedule measures its own
11538                // perturbation.
11539                // CudaEvent is not Clone, so record a second tail event on the same stream —
11540                // adjacent to `ev`, so it carries the same completion timestamp for timing.
11541                let tp = engine
11542                    .ctx()
11543                    .new_event(Some(cudarc::driver::sys::CUevent_flags::CU_EVENT_DEFAULT))?;
11544                tp.record(&engine.stream())?;
11545                ev_tail_prof.push(tp);
11546            }
11547            ev_rank.push(ev);
11548            partials.push(d_csr);
11549        }
11550        let _main = e.gpu.enter_main()?;
11551        e.bind_runtime_device(e.ctx().ordinal() as i32)?;
11552        // Host-only: every rank's chain is queued, nothing has been waited on yet.
11553        let g_issue = g_t1.elapsed().as_secs_f64() * 1e3;
11554        let g_t2 = std::time::Instant::now();
11555        for ev in &ev_rank {
11556            e.stream().wait(ev)?;
11557        }
11558        // Both partials land on the root (rank 1's crosses the link once), then ONE fused pass
11559        // does join + CSR permute + weight + scatter. Shard order stays pinned as (y0 + y1).
11560        let mut y0 = e.uninit(n_pairs * width)?;
11561        {
11562            let mut dst = y0.slice_mut(0..n_pairs * width);
11563            e.stream()
11564                .memcpy_dtod(&partials[0].slice(0..n_pairs * width), &mut dst)?;
11565        }
11566        let mut y1 = e.uninit(n_pairs * width)?;
11567        {
11568            let mut dst = y1.slice_mut(0..n_pairs * width);
11569            e.stream()
11570                .memcpy_dtod(&partials[1].slice(0..n_pairs * width), &mut dst)?;
11571        }
11572        let inv_d = e.htod_i32(&inv)?;
11573        let wd_d = e.htod(&wd)?;
11574        let mut out = e.uninit(t * width)?;
11575        e.moe_prime_join_scatter(&y0, &y1, &inv_d, &wd_d, &mut out, width, n_used, t)?;
11576        if gprof {
11577            let _ = e.stream().synchronize();
11578            let g_join = g_t2.elapsed().as_secs_f64() * 1e3;
11579            // Everything has completed, so both events of every pair are ready and elapsed_ms
11580            // cannot block. A negative entry means the query itself failed and the row must be
11581            // read as missing data, never as a zero-length span.
11582            // cuEventElapsedTime needs the events' OWN context current — computing it under the
11583            // root's pushed context returned an error for every pair, and the first version
11584            // swallowed that into -1.0 with no reason attached. Enter each rank's context, and
11585            // print the failure once so a dead probe can never again look like a zero-length span.
11586            let mut span_ms: Vec<f32> = Vec::with_capacity(world);
11587            for (rank, (h, tp)) in ev_head.iter().zip(ev_tail_prof.iter()).enumerate() {
11588                let guard = self.ranks[rank].gpu.enter_main();
11589                match guard.and_then(|_g| h.elapsed_ms(tp).map_err(|e| e.into())) {
11590                    Ok(v) => span_ms.push(v),
11591                    Err(err) => {
11592                        static SAID: std::sync::atomic::AtomicBool =
11593                            std::sync::atomic::AtomicBool::new(false);
11594                        if !SAID.swap(true, std::sync::atomic::Ordering::Relaxed) {
11595                            eprintln!("[grp-prof] span query failed on rank {rank}: {err}");
11596                        }
11597                        span_ms.push(-1.0);
11598                    }
11599                }
11600            }
11601            eprintln!(
11602                "[grp-prof] t={t} n_active={n_active} csr={g_csr:.1}ms issue={g_issue:.1}ms \
11603                 join={g_join:.1}ms spans={span_ms:?} span_sum={:.1}ms span_max={:.1}ms",
11604                span_ms.iter().sum::<f32>(),
11605                span_ms.iter().cloned().fold(0.0f32, f32::max)
11606            );
11607        }
11608        Ok(out)
11609    }
11610
11611    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
11612        &self,
11613        experts: &ResidentNvfp4TensorParallel,
11614        e: &Engine,
11615        input_dev: &crate::CudaSlice<f32>,
11616        sel_d: &crate::CudaSlice<i32>,
11617        w_d: &crate::CudaSlice<f32>,
11618        experts_per_token: usize,
11619        activation_limit: Option<f32>,
11620    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11621        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
11622            experts,
11623            e,
11624            input_dev,
11625            sel_d,
11626            w_d,
11627            experts_per_token,
11628            activation_limit,
11629            || Ok(()),
11630        )
11631    }
11632
11633    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
11634    /// runs on the host right before the join wait is enqueued on e's stream — work it
11635    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
11636    /// sweep, instead of after the join. Value-neutral by construction (the hook only
11637    /// reorders independent host issue).
11638    #[allow(clippy::too_many_arguments)]
11639    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
11640        &self,
11641        experts: &ResidentNvfp4TensorParallel,
11642        e: &Engine,
11643        input_dev: &crate::CudaSlice<f32>,
11644        sel_d: &crate::CudaSlice<i32>,
11645        w_d: &crate::CudaSlice<f32>,
11646        experts_per_token: usize,
11647        activation_limit: Option<f32>,
11648        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11649    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11650        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
11651            experts,
11652            e,
11653            input_dev,
11654            sel_d,
11655            w_d,
11656            experts_per_token,
11657            activation_limit,
11658            pre_join,
11659            None,
11660        )
11661    }
11662
11663    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
11664    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
11665    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
11666    /// its apply launch. Raw UVA pointers so no lock is held across the call.
11667    #[allow(clippy::too_many_arguments)]
11668    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
11669        &self,
11670        experts: &ResidentNvfp4TensorParallel,
11671        e: &Engine,
11672        input_dev: &crate::CudaSlice<f32>,
11673        sel_d: &crate::CudaSlice<i32>,
11674        w_d: &crate::CudaSlice<f32>,
11675        experts_per_token: usize,
11676        activation_limit: Option<f32>,
11677        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11678        post_add: Option<(u64, u64)>,
11679    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11680        if input_dev.len() != experts.input_width {
11681            return Err(format!(
11682                "NVFP4 device-routed input {} != width {}",
11683                input_dev.len(),
11684                experts.input_width
11685            )
11686            .into());
11687        }
11688        let n_sel = experts_per_token;
11689        if sel_d.len() < n_sel || w_d.len() < n_sel {
11690            return Err(format!(
11691                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
11692                sel_d.len(),
11693                w_d.len()
11694            )
11695            .into());
11696        }
11697        let world = self.ranks.len();
11698        if world != NVFP4_CANONICAL_ROW_SHARDS {
11699            return Err(format!(
11700                "NVFP4 device routes require world == canonical shard grid \
11701                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
11702            )
11703            .into());
11704        }
11705        let local_out = if experts.ep2 {
11706            experts.expert_width
11707        } else {
11708            experts.expert_width / world
11709        };
11710
11711        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11712        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11713        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
11714        let started = timing.then(std::time::Instant::now);
11715
11716        let mut workspace_guard = experts
11717            .device_workspace
11718            .lock()
11719            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11720        if workspace_guard.is_none() {
11721            drop(workspace_guard);
11722            let zero = vec![0.0f32; experts.input_width];
11723            let zero_sel = vec![0usize; n_sel];
11724            let zero_w = vec![0.0f32; n_sel];
11725            let _ = self.run_tensor_parallel_routes_nvfp4_device(
11726                experts,
11727                &zero,
11728                &zero_sel,
11729                &zero_w,
11730                n_sel,
11731                activation_limit,
11732            )?;
11733            workspace_guard = experts
11734                .device_workspace
11735                .lock()
11736                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11737        }
11738        let workspace = workspace_guard
11739            .as_mut()
11740            .expect("NVFP4 device routes workspace initialized above");
11741        if workspace.n_sel != n_sel {
11742            return Err(format!(
11743                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
11744                workspace.n_sel
11745            )
11746            .into());
11747        }
11748
11749        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
11750        // stitched multi-device parent launched on e's stream — no events, no per-token node
11751        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
11752        // the children replay exactly the same kernel/copy sequence.
11753        //
11754        // GRAPH-LAUNCH HEADROOM GUARD (see spec::GRAPH_LAUNCH_MIN_FREE): below the
11755        // driver-free floor on the launching device this call falls through to the
11756        // eager routes path below — the exact body the graph captures, stateless per
11757        // call — instead of feeding cuGraphLaunch an exhausted card
11758        // (lane/graph-launch-guard-sweep-20260831).
11759        if step_tp_graph_enabled()? && step_tp_graph_headroom_ok(e) {
11760            if experts.ep2 {
11761                return Err(
11762                    "MEMRA_STEP_TP_GRAPH=1 with MEMRA_STEP_NVFP4_EP2=1 has never been \
11763                     co-gated; unset one"
11764                        .into(),
11765                );
11766            }
11767            if workspace.dev_route_e.is_none() {
11768                let _main = e.gpu.enter_main()?;
11769                workspace.dev_route_e = Some((
11770                    e.htod_i32(&vec![0i32; n_sel])?,
11771                    e.htod(&vec![0.0f32; n_sel])?,
11772                ));
11773            }
11774            if workspace.in_stage_e.is_none() {
11775                let _main = e.gpu.enter_main()?;
11776                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
11777                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
11778            }
11779            if workspace.routes_graph.is_none() {
11780                let graph = self.nvfp4_routes_build_graph(
11781                    experts,
11782                    workspace,
11783                    local_out,
11784                    n_sel,
11785                    activation_limit,
11786                )?;
11787                workspace.routes_graph = Some(graph);
11788                eprintln!(
11789                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
11790                     children=3 updates=none performance_claim=false"
11791                );
11792            }
11793            let output = {
11794                let _main = e.gpu.enter_main()?;
11795                {
11796                    let (sel_e, w_e) = workspace
11797                        .dev_route_e
11798                        .as_mut()
11799                        .expect("device route staging set above");
11800                    {
11801                        let mut dst = sel_e.slice_mut(0..n_sel);
11802                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11803                    }
11804                    {
11805                        let mut dst = w_e.slice_mut(0..n_sel);
11806                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11807                    }
11808                }
11809                {
11810                    let in_stage = workspace
11811                        .in_stage_e
11812                        .as_mut()
11813                        .expect("graph staging set above");
11814                    let mut dst = in_stage.slice_mut(0..experts.input_width);
11815                    e.stream()
11816                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
11817                }
11818                unsafe {
11819                    let r = cudarc::driver::sys::cuGraphLaunch(
11820                        workspace
11821                            .routes_graph
11822                            .as_ref()
11823                            .expect("routes graph built above")
11824                            .exec,
11825                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
11826                    );
11827                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
11828                        return Err(format!("routes graph launch: {r:?}").into());
11829                    }
11830                }
11831                let mut output = e.uninit(experts.input_width)?;
11832                {
11833                    let out_stage = workspace
11834                        .out_stage_e
11835                        .as_ref()
11836                        .expect("graph staging set above");
11837                    e.stream().memcpy_dtod(
11838                        &out_stage.slice(0..experts.input_width),
11839                        &mut output.slice_mut(0..experts.input_width),
11840                    )?;
11841                }
11842                output
11843            };
11844            if let Some(started) = started {
11845                use std::sync::atomic::Ordering;
11846                let ns = TIMING_NS
11847                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11848                    + started.elapsed().as_nanos() as u64;
11849                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11850                if calls % 430 == 0 {
11851                    eprintln!(
11852                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11853                        ns as f64 / 1.0e6,
11854                        ns as f64 / calls as f64 / 1.0e3,
11855                    );
11856                }
11857            }
11858            return Ok(output);
11859        }
11860
11861        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
11862        // copied into the persistent e-context pair, then the event is recorded — the caller's
11863        // sel_d/w_d can free on e's stream with no cross-stream reader.
11864        if let Some((_, device)) = workspace.ev_entry.as_ref() {
11865            if *device != e.ctx().ordinal() {
11866                return Err("NVFP4 device-routed routes engine changed".into());
11867            }
11868        } else {
11869            let _main = e.gpu.enter_main()?;
11870            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
11871        }
11872        if workspace.dev_route_e.is_none() {
11873            let _main = e.gpu.enter_main()?;
11874            workspace.dev_route_e = Some((
11875                e.htod_i32(&vec![0i32; n_sel])?,
11876                e.htod(&vec![0.0f32; n_sel])?,
11877            ));
11878        }
11879        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
11880        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
11881        // selection rows), so when every consuming rank shares e's device the ranks can read
11882        // them directly and this hop disappears. The graph door keeps the staging (its
11883        // captured copies read the fixed addresses).
11884        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
11885        let e_device = e.ctx().ordinal();
11886        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
11887        let rank1_routed_peek = workspace.rank1_routed;
11888        let stage_needed = !mirror
11889            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
11890                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
11891            });
11892        {
11893            let _main = e.gpu.enter_main()?;
11894            if stage_needed {
11895                let (sel_e, w_e) = workspace
11896                    .dev_route_e
11897                    .as_mut()
11898                    .expect("device route staging set above");
11899                {
11900                    let mut dst = sel_e.slice_mut(0..n_sel);
11901                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11902                }
11903                {
11904                    let mut dst = w_e.slice_mut(0..n_sel);
11905                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11906                }
11907            }
11908            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11909            ev_entry.record(&e.stream())?;
11910        }
11911        // Prestage door: input pull + quantize were already issued on the rank streams
11912        // (before the router) — the rank stream order suffices, skip them here.
11913        let prestaged = std::mem::take(&mut workspace.prestaged);
11914        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
11915        for (rank_index, engine) in self.ranks.iter().enumerate() {
11916            let _main = engine.gpu.enter_main()?;
11917            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11918            engine.stream().wait(ev_entry)?;
11919            if !prestaged {
11920                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
11921                engine
11922                    .stream()
11923                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
11924            }
11925            if !(rank1_routed && rank_index == 1) {
11926                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
11927                // the caller's persistent rows when this rank shares e's device (UVA, ordered
11928                // by ev_entry), else the staged e-context pair.
11929                let same_dev = engine.ctx().ordinal() == e_device;
11930                if mirror {
11931                    // Split the workspace borrow so the source (the staged pair, when this
11932                    // rank is off-device) and the destination rows coexist.
11933                    let Nvfp4DeviceRoutesWorkspace {
11934                        sel,
11935                        route_w,
11936                        dev_route_e,
11937                        ..
11938                    } = &mut *workspace;
11939                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
11940                        if same_dev {
11941                            (sel_d, w_d)
11942                        } else {
11943                            let (sel_e, w_e) = dev_route_e
11944                                .as_ref()
11945                                .expect("device route staging set above");
11946                            (sel_e, w_e)
11947                        };
11948                    engine.moe_sel_w_mirror(
11949                        src_sel,
11950                        src_w,
11951                        &mut sel[rank_index],
11952                        &mut route_w[rank_index],
11953                        n_sel,
11954                    )?;
11955                } else {
11956                    let (sel_e, w_e) = workspace
11957                        .dev_route_e
11958                        .as_ref()
11959                        .expect("device route staging set above");
11960                    {
11961                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
11962                        engine
11963                            .stream()
11964                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
11965                    }
11966                    {
11967                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
11968                        engine
11969                            .stream()
11970                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
11971                    }
11972                }
11973            }
11974            if !prestaged {
11975                let Nvfp4DeviceRoutesWorkspace {
11976                    input, in_q, in_d, ..
11977                } = &mut *workspace;
11978                engine.quantize_q8_1_into(
11979                    &input[rank_index],
11980                    1,
11981                    experts.input_width,
11982                    &mut in_q[rank_index],
11983                    &mut in_d[rank_index],
11984                )?;
11985            }
11986        }
11987        self.nvfp4_routes_batched_sweeps(
11988            experts,
11989            workspace,
11990            &[],
11991            &[],
11992            &[],
11993            local_out,
11994            n_sel,
11995            activation_limit,
11996            true,
11997        )?;
11998
11999        // rank0 == root: its own stream order already covers its sweep; only the PEER
12000        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
12001        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
12002            let _main = engine.gpu.enter_main()?;
12003            workspace.ev_rank[rank_index].record(&engine.stream())?;
12004        }
12005        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
12006        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
12007        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
12008        let mut ticket = 0u32;
12009        if memops {
12010            use cudarc::driver::sys;
12011            if workspace.fence_flags_raw == 0 {
12012                let root = &self.ranks[0];
12013                let _main = root.gpu.enter_main()?;
12014                let mut ptr: sys::CUdeviceptr = 0;
12015                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
12016                if r != sys::CUresult::CUDA_SUCCESS {
12017                    return Err(format!("fence flag alloc: {r:?}").into());
12018                }
12019                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
12020                if r != sys::CUresult::CUDA_SUCCESS {
12021                    return Err(format!("fence flag memset: {r:?}").into());
12022                }
12023                workspace.fence_flags_raw = ptr as u64;
12024            }
12025            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
12026            ticket = workspace.fence_ticket;
12027            let base = workspace.fence_flags_raw;
12028            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
12029            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
12030            // root memory is legal — the direct join already relies on it. Under
12031            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
12032            // replacing the cross-device event wait below.
12033            if fence_rank1_on() {
12034                let peer = &self.ranks[1];
12035                let _pmain = peer.gpu.enter_main()?;
12036                peer.ring_flag_raw(base, ticket)?;
12037            }
12038            {
12039                let root = &self.ranks[0];
12040                let _main = root.gpu.enter_main()?;
12041                let r = unsafe {
12042                    sys::cuStreamWriteValue32_v2(
12043                        root.stream().cu_stream() as sys::CUstream,
12044                        (base + 4) as sys::CUdeviceptr,
12045                        ticket,
12046                        0,
12047                    )
12048                };
12049                if r != sys::CUresult::CUDA_SUCCESS {
12050                    return Err(format!("fence write root: {r:?}").into());
12051                }
12052            }
12053        }
12054        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
12055        // kernels queued here execute while the peer rank drains its sweep.
12056        pre_join()?;
12057
12058        if moe_direct_on() && self.ranks.len() == 2 {
12059            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
12060            // rank0's is root-stream-ordered. One root event + rank1's own event order
12061            // the model engine's single add — same operand order as root's add
12062            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
12063            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
12064            // hazard class does not apply).
12065            let _main = e.gpu.enter_main()?;
12066            if memops {
12067                use cudarc::driver::sys;
12068                let base = workspace.fence_flags_raw;
12069                let r = unsafe {
12070                    sys::cuStreamWaitValue32_v2(
12071                        e.stream().cu_stream() as sys::CUstream,
12072                        (base + 4) as sys::CUdeviceptr,
12073                        ticket,
12074                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
12075                    )
12076                };
12077                if r != sys::CUresult::CUDA_SUCCESS {
12078                    return Err(format!("fence wait: {r:?}").into());
12079                }
12080                if fence_rank1_on() {
12081                    // Same-device wait on the flag rank1 rang over P2P.
12082                    let r = unsafe {
12083                        sys::cuStreamWaitValue32_v2(
12084                            e.stream().cu_stream() as sys::CUstream,
12085                            base as sys::CUdeviceptr,
12086                            ticket,
12087                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
12088                        )
12089                    };
12090                    if r != sys::CUresult::CUDA_SUCCESS {
12091                        return Err(format!("fence wait rank1: {r:?}").into());
12092                    }
12093                } else {
12094                    for ev in workspace.ev_rank.iter().skip(1) {
12095                        e.stream().wait(ev)?;
12096                    }
12097                }
12098            } else {
12099                {
12100                    let root = &self.ranks[0];
12101                    let _rmain = root.gpu.enter_main()?;
12102                    workspace
12103                        .ev_done
12104                        .as_ref()
12105                        .expect("device routes done event")
12106                        .record(&root.stream())?;
12107                }
12108                e.stream().wait(
12109                    workspace
12110                        .ev_done
12111                        .as_ref()
12112                        .expect("device routes done event"),
12113                )?;
12114                for ev in workspace.ev_rank.iter().skip(1) {
12115                    e.stream().wait(ev)?;
12116                }
12117            }
12118            let mut output = e.uninit(experts.input_width)?;
12119            if let Some((sh_raw, scale_raw)) = post_add {
12120                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
12121                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
12122                e.add3_raw(
12123                    &workspace.accumulator[0],
12124                    &workspace.accumulator[1],
12125                    sh_raw,
12126                    scale_raw,
12127                    &mut output,
12128                    experts.input_width,
12129                )?;
12130            } else {
12131                e.add(
12132                    &workspace.accumulator[0],
12133                    &workspace.accumulator[1],
12134                    &mut output,
12135                    experts.input_width,
12136                )?;
12137            }
12138            let output = output;
12139            if let Some(started) = started {
12140                use std::sync::atomic::Ordering;
12141                let ns = TIMING_NS
12142                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
12143                    + started.elapsed().as_nanos() as u64;
12144                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12145                if calls % 430 == 0 {
12146                    eprintln!(
12147                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12148                        ns as f64 / 1.0e6,
12149                        ns as f64 / calls as f64 / 1.0e3,
12150                    );
12151                }
12152            }
12153            return Ok(output);
12154        }
12155        {
12156            let root = &self.ranks[0];
12157            let _main = root.gpu.enter_main()?;
12158            for ev in workspace.ev_rank.iter().skip(1) {
12159                root.stream().wait(ev)?;
12160            }
12161            root.stream()
12162                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
12163            {
12164                let Nvfp4DeviceRoutesWorkspace {
12165                    accumulator,
12166                    remote,
12167                    combined,
12168                    ..
12169                } = &mut *workspace;
12170                root.add(&accumulator[0], remote, combined, experts.input_width)?;
12171            }
12172            workspace
12173                .ev_done
12174                .as_ref()
12175                .expect("device routes done event")
12176                .record(&root.stream())?;
12177        }
12178        let output = {
12179            let _main = e.gpu.enter_main()?;
12180            e.stream().wait(
12181                workspace
12182                    .ev_done
12183                    .as_ref()
12184                    .expect("device routes done event"),
12185            )?;
12186            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
12187            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
12188            let mut output = e.uninit(experts.input_width)?;
12189            e.stream().memcpy_dtod(
12190                &workspace.combined.slice(0..experts.input_width),
12191                &mut output.slice_mut(0..experts.input_width),
12192            )?;
12193            output
12194        };
12195        if let Some(started) = started {
12196            use std::sync::atomic::Ordering;
12197            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
12198                + started.elapsed().as_nanos() as u64;
12199            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
12200            if calls % 430 == 0 {
12201                eprintln!(
12202                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
12203                    ns as f64 / 1.0e6,
12204                    ns as f64 / calls as f64 / 1.0e3,
12205                );
12206            }
12207        }
12208        Ok(output)
12209    }
12210
12211    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
12212    /// caller wraps it with rank-event waits + the done record; the token graph captures it
12213    /// verbatim (parent edges provide the ordering).
12214    pub(crate) fn decode_v2_finish_root_fused(
12215        &self,
12216        ws: &mut StepTpDecodeV2Ws,
12217    ) -> Result<(), Box<dyn std::error::Error>> {
12218        let root = &self.ranks[0];
12219        let _main = root.gpu.enter_main()?;
12220        if ws.raw_peer_partial != 0 {
12221            // Capture-safe raw seams (arming happened in the stage flow).
12222            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
12223        } else {
12224            root.stream()
12225                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
12226        }
12227        {
12228            let StepTpDecodeV2Ws {
12229                o_partials,
12230                peer_partial,
12231                reduce_a,
12232                o_out,
12233                ..
12234            } = &mut *ws;
12235            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
12236        }
12237        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
12238        if shadows {
12239            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
12240            // raw when armed.
12241            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
12242            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
12243            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
12244            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
12245        }
12246        if shadows && ws.raw_peer_partial != 0 {
12247            raw_copy_bytes(
12248                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
12249                ws.raw_k1,
12250                ws.local_kv_dim * 4,
12251                root,
12252            )?;
12253            raw_copy_bytes(
12254                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
12255                ws.raw_v1,
12256                ws.local_kv_dim * 4,
12257                root,
12258            )?;
12259        } else if shadows {
12260            let start = ws.local_kv_dim;
12261            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
12262            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
12263            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
12264            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
12265        }
12266        if ws.raw_mixed_stage_e != 0 {
12267            // Token-graph mirrors: the e-glue children read same-context copies of the
12268            // root-produced rows.
12269            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
12270            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
12271            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
12272            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
12273        }
12274        Ok(())
12275    }
12276
12277    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
12278    /// reduce_a's own pointer.
12279    pub(crate) fn decode_v2_arm_token_mirrors(
12280        &self,
12281        ws: &mut StepTpDecodeV2Ws,
12282        mixed_stage_e: u64,
12283        shadow_stage_e: (u64, u64),
12284    ) -> Result<(), Box<dyn std::error::Error>> {
12285        use cudarc::driver::DevicePtr;
12286        let root = &self.ranks[0];
12287        let _main = root.gpu.enter_main()?;
12288        let stream = root.stream();
12289        let (a, _g) = ws.reduce_a.device_ptr(&stream);
12290        ws.raw_reduce_a = a as u64;
12291        ws.raw_mixed_stage_e = mixed_stage_e;
12292        ws.raw_shadow_stage_e = shadow_stage_e;
12293        Ok(())
12294    }
12295
12296    /// Build one layer's stitched routes graph: per-rank children captured on their own
12297    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
12298    /// capture-illegal there), a root combine child, and a multi-device parent with
12299    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
12300    /// nodes touch is persistent workspace/staging.
12301    fn nvfp4_routes_build_graph(
12302        &self,
12303        experts: &ResidentNvfp4TensorParallel,
12304        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12305        local_out: usize,
12306        n_sel: usize,
12307        activation_limit: Option<f32>,
12308    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
12309        use cudarc::driver::DevicePtr;
12310        use cudarc::driver::sys;
12311        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12312            if r == sys::CUresult::CUDA_SUCCESS {
12313                Ok(())
12314            } else {
12315                Err(format!("{what}: {r:?}").into())
12316            }
12317        }
12318        let world = self.ranks.len();
12319        if world != 2 {
12320            return Err("routes graph door is built for the TP2 pair".into());
12321        }
12322        let width = experts.input_width;
12323
12324        // Raw pointers cached before capture (each read with its owner's stream).
12325        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
12326            let stream = engine.stream();
12327            let (ptr, _g) = buf.device_ptr(&stream);
12328            ptr as u64
12329        };
12330        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
12331            let stream = engine.stream();
12332            let (ptr, _g) = buf.device_ptr(&stream);
12333            ptr as u64
12334        };
12335        let (sel_e, w_e) = workspace
12336            .dev_route_e
12337            .as_ref()
12338            .expect("device route staging set before graph build");
12339        let root_engine = &self.ranks[0];
12340        let p_in_stage = ptr_f32(
12341            workspace.in_stage_e.as_ref().expect("graph staging"),
12342            root_engine,
12343        );
12344        let p_out_stage = ptr_f32(
12345            workspace.out_stage_e.as_ref().expect("graph staging"),
12346            root_engine,
12347        );
12348        let p_sel_e = ptr_i32(sel_e, root_engine);
12349        let p_w_e = ptr_f32(w_e, root_engine);
12350        let p_input: Vec<u64> = (0..world)
12351            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
12352            .collect();
12353        let p_sel: Vec<u64> = (0..world)
12354            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
12355            .collect();
12356        let p_route_w: Vec<u64> = (0..world)
12357            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
12358            .collect();
12359        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
12360        let p_remote = ptr_f32(&workspace.remote, root_engine);
12361        let p_combined = ptr_f32(&workspace.combined, root_engine);
12362
12363        let raw_copy = |dst: u64,
12364                        src: u64,
12365                        bytes: usize,
12366                        engine: &Engine|
12367         -> Result<(), Box<dyn std::error::Error>> {
12368            unsafe {
12369                cu_try(
12370                    sys::cuMemcpyAsync(
12371                        dst as sys::CUdeviceptr,
12372                        src as sys::CUdeviceptr,
12373                        bytes,
12374                        engine.stream().cu_stream() as sys::CUstream,
12375                    ),
12376                    "routes graph cuMemcpyAsync",
12377                )
12378            }
12379        };
12380
12381        let mut children = Vec::with_capacity(3);
12382        for rank in 0..world {
12383            let engine = &self.ranks[rank];
12384            let _main = engine.gpu.enter_main()?;
12385            let (child, _retained) = engine.capture_graph_retained(|_| {
12386                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
12387                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
12388                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
12389                {
12390                    let Nvfp4DeviceRoutesWorkspace {
12391                        input, in_q, in_d, ..
12392                    } = &mut *workspace;
12393                    engine.quantize_q8_1_into(
12394                        &input[rank],
12395                        1,
12396                        width,
12397                        &mut in_q[rank],
12398                        &mut in_d[rank],
12399                    )?;
12400                }
12401                self.nvfp4_routes_batched_sweeps_rank(
12402                    experts,
12403                    workspace,
12404                    &[],
12405                    &[],
12406                    &[],
12407                    local_out,
12408                    n_sel,
12409                    activation_limit,
12410                    true,
12411                    rank,
12412                )?;
12413                Ok(())
12414            })?;
12415            children.push(child);
12416        }
12417        {
12418            let root = &self.ranks[0];
12419            let _main = root.gpu.enter_main()?;
12420            let (child, _retained) = root.capture_graph_retained(|_| {
12421                raw_copy(p_remote, p_acc1, width * 4, root)?;
12422                {
12423                    let Nvfp4DeviceRoutesWorkspace {
12424                        accumulator,
12425                        remote,
12426                        combined,
12427                        ..
12428                    } = &mut *workspace;
12429                    root.add(&accumulator[0], remote, combined, width)?;
12430                }
12431                raw_copy(p_out_stage, p_combined, width * 4, root)?;
12432                Ok(())
12433            })?;
12434            children.push(child);
12435        }
12436
12437        let mut parent: sys::CUgraph = std::ptr::null_mut();
12438        unsafe {
12439            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
12440        }
12441        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
12442        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
12443        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
12444        unsafe {
12445            cu_try(
12446                sys::cuGraphAddChildGraphNode(
12447                    &mut n0,
12448                    parent,
12449                    std::ptr::null(),
12450                    0,
12451                    children[0].cu_graph(),
12452                ),
12453                "routes child r0",
12454            )?;
12455            cu_try(
12456                sys::cuGraphAddChildGraphNode(
12457                    &mut n1,
12458                    parent,
12459                    std::ptr::null(),
12460                    0,
12461                    children[1].cu_graph(),
12462                ),
12463                "routes child r1",
12464            )?;
12465            let deps = [n0, n1];
12466            cu_try(
12467                sys::cuGraphAddChildGraphNode(
12468                    &mut n2,
12469                    parent,
12470                    deps.as_ptr(),
12471                    2,
12472                    children[2].cu_graph(),
12473                ),
12474                "routes child root",
12475            )?;
12476        }
12477        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
12478        unsafe {
12479            cu_try(
12480                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
12481                "routes instantiate",
12482            )?;
12483        }
12484        Ok(RoutesGraph {
12485            exec,
12486            parent,
12487            _children: children,
12488        })
12489    }
12490
12491    /// One rank's routes section for the token graph (event-free): staged input copy (raw
12492    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
12493    /// Eager device_routed wraps it with the entry-event wait.
12494    #[allow(clippy::too_many_arguments)]
12495    pub(crate) fn routes_rank_section(
12496        &self,
12497        experts: &ResidentNvfp4TensorParallel,
12498        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12499        raw_input_src: u64,
12500        local_out: usize,
12501        n_sel: usize,
12502        activation_limit: Option<f32>,
12503        rank_index: usize,
12504    ) -> Result<(), Box<dyn std::error::Error>> {
12505        let engine = &self.ranks[rank_index];
12506        {
12507            let _main = engine.gpu.enter_main()?;
12508            // sel/route_w land via raw copies from the e staging (fixed addresses).
12509            let (sel_e_ptr, w_e_ptr) = workspace
12510                .raw_dev_route_e
12511                .ok_or("routes rank section requires armed staging pointers")?;
12512            raw_copy_bytes(
12513                workspace.raw_input[rank_index],
12514                raw_input_src,
12515                experts.input_width * 4,
12516                engine,
12517            )?;
12518            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
12519            raw_copy_bytes(
12520                workspace.raw_route_w[rank_index],
12521                w_e_ptr,
12522                n_sel * 4,
12523                engine,
12524            )?;
12525            {
12526                let Nvfp4DeviceRoutesWorkspace {
12527                    input, in_q, in_d, ..
12528                } = &mut *workspace;
12529                engine.quantize_q8_1_into(
12530                    &input[rank_index],
12531                    1,
12532                    experts.input_width,
12533                    &mut in_q[rank_index],
12534                    &mut in_d[rank_index],
12535                )?;
12536            }
12537        }
12538        self.nvfp4_routes_batched_sweeps_rank(
12539            experts,
12540            workspace,
12541            &[],
12542            &[],
12543            &[],
12544            local_out,
12545            n_sel,
12546            activation_limit,
12547            true,
12548            rank_index,
12549        )
12550    }
12551
12552    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
12553    /// add, combined row raw-copied into the fixed e-context out stage.
12554    pub(crate) fn routes_root_section(
12555        &self,
12556        experts: &ResidentNvfp4TensorParallel,
12557        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12558    ) -> Result<(), Box<dyn std::error::Error>> {
12559        let root = &self.ranks[0];
12560        let _main = root.gpu.enter_main()?;
12561        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
12562            .raw_combine
12563            .ok_or("routes root section requires armed combine pointers")?;
12564        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
12565        {
12566            let Nvfp4DeviceRoutesWorkspace {
12567                accumulator,
12568                remote,
12569                combined,
12570                ..
12571            } = &mut *workspace;
12572            root.add(&accumulator[0], remote, combined, experts.input_width)?;
12573        }
12574        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
12575        Ok(())
12576    }
12577
12578    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
12579    /// combine set. Requires dev_route_e + in/out stages already allocated.
12580    pub(crate) fn routes_arm_raw(
12581        &self,
12582        experts: &ResidentNvfp4TensorParallel,
12583        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12584    ) -> Result<(), Box<dyn std::error::Error>> {
12585        use cudarc::driver::DevicePtr;
12586        if workspace.raw_dev_route_e.is_some() {
12587            return Ok(());
12588        }
12589        let _ = experts;
12590        let (sel_e, w_e) = workspace
12591            .dev_route_e
12592            .as_ref()
12593            .ok_or("routes staging not armed")?;
12594        let root = &self.ranks[0];
12595        {
12596            let _main = root.gpu.enter_main()?;
12597            let stream = root.stream();
12598            let (a, _g) = sel_e.device_ptr(&stream);
12599            let (b, _g) = w_e.device_ptr(&stream);
12600            workspace.raw_dev_route_e = Some((a as u64, b as u64));
12601            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
12602            let (d, _g) = workspace.remote.device_ptr(&stream);
12603            let (f, _g) = workspace.combined.device_ptr(&stream);
12604            let out_stage = workspace
12605                .out_stage_e
12606                .as_ref()
12607                .ok_or("routes out stage not armed")?;
12608            let (g_, _g) = out_stage.device_ptr(&stream);
12609            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
12610        }
12611        for rank in 0..self.ranks.len() {
12612            let engine = &self.ranks[rank];
12613            let _main = engine.gpu.enter_main()?;
12614            let stream = engine.stream();
12615            let (a, _g) = workspace.input[rank].device_ptr(&stream);
12616            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
12617            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
12618            workspace.raw_input.push(a as u64);
12619            workspace.raw_sel.push(b as u64);
12620            workspace.raw_route_w.push(c as u64);
12621        }
12622        Ok(())
12623    }
12624
12625    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
12626    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
12627    /// throughput claim.
12628    pub fn run_tensor_parallel_routes_nvfp4(
12629        &self,
12630        experts: &ResidentNvfp4TensorParallel,
12631        input: &[f32],
12632        tokens: usize,
12633        selected: &[usize],
12634        route_weights: &[f32],
12635        experts_per_token: usize,
12636        activation_limit: Option<f32>,
12637    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12638        validate_activations(input, tokens, experts.input_width)?;
12639        let pairs = tokens
12640            .checked_mul(experts_per_token)
12641            .ok_or("NVFP4 TP route count overflow")?;
12642        if selected.len() != pairs || route_weights.len() != pairs {
12643            return Err(format!(
12644                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
12645                 {experts_per_token} ({pairs})",
12646                selected.len(),
12647                route_weights.len(),
12648            )
12649            .into());
12650        }
12651        if !route_weights.iter().all(|weight| weight.is_finite()) {
12652            return Err("NVFP4 TP route weights contain a non-finite value".into());
12653        }
12654
12655        let mut output = vec![0.0f32; tokens * experts.input_width];
12656        for token in 0..tokens {
12657            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
12658            for slot in 0..experts_per_token {
12659                let pair = token * experts_per_token + slot;
12660                let expert = selected[pair];
12661                if expert >= experts.expert_count {
12662                    return Err(format!(
12663                        "NVFP4 TP selected expert {expert} outside 0..{}",
12664                        experts.expert_count
12665                    )
12666                    .into());
12667                }
12668                // EP2 banks hold the WHOLE expert on rank (expert & 1) at slot (expert >> 1);
12669                // per-row dots are the same full-width program either way (a column shard
12670                // splits ROWS, not the dot), so gate/up are bit-equal across layouts. Only
12671                // down's parenthesization moves (full-width dot vs canonical 2-shard sum) —
12672                // the numeric-class this door declares.
12673                let gate = if experts.ep2 {
12674                    self.run_full_bank_expert_nvfp4(
12675                        &experts.gate,
12676                        &experts.macros_gate,
12677                        expert,
12678                        input_row,
12679                    )?
12680                } else {
12681                    self.run_column_bank_expert_nvfp4(
12682                        &experts.gate,
12683                        &experts.macros_gate,
12684                        expert,
12685                        input_row,
12686                    )?
12687                };
12688                let up = if experts.ep2 {
12689                    self.run_full_bank_expert_nvfp4(
12690                        &experts.up,
12691                        &experts.macros_up,
12692                        expert,
12693                        input_row,
12694                    )?
12695                } else {
12696                    self.run_column_bank_expert_nvfp4(
12697                        &experts.up,
12698                        &experts.macros_up,
12699                        expert,
12700                        input_row,
12701                    )?
12702                };
12703                let activated: Vec<f32> = gate
12704                    .iter()
12705                    .zip(&up)
12706                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
12707                    .collect();
12708                debug_assert_eq!(activated.len(), experts.expert_width);
12709                let down = if experts.ep2 {
12710                    self.run_full_down_expert_nvfp4(
12711                        &experts.down,
12712                        &experts.macros_down,
12713                        expert,
12714                        &activated,
12715                    )?
12716                } else {
12717                    self.run_row_bank_expert_nvfp4(
12718                        &experts.down,
12719                        &experts.macros_down,
12720                        expert,
12721                        &activated,
12722                    )?
12723                };
12724                let weight = route_weights[pair];
12725                for (sum, value) in output
12726                    [token * experts.input_width..(token + 1) * experts.input_width]
12727                    .iter_mut()
12728                    .zip(down)
12729                {
12730                    *sum += weight * value;
12731                }
12732            }
12733        }
12734        Ok(output)
12735    }
12736}
12737
12738#[cfg(test)]
12739mod bank_v2_layout_tests {
12740    use super::{nvfp4_matrix_v2_permute, nvfp4_row_bytes};
12741
12742    /// The slot-major permutation had NO test at all until 2026-08-29, while its (since
12743    /// removed) `MEMRA_NVFP4_BANK_V2` FLAGS row carried a bit-identity claim and the live
12744    /// serving env pinned it on. This pins the DOCUMENTED mapping so a reader can be checked
12745    /// against something: per row, slot g's 16 qs bytes land contiguously at `g*16`, and its
12746    /// two UE4M3 scale bytes at `nslots*16 + g*2`. Source layout is memra `block_nvfp4`:
12747    /// 36-byte superblocks of [4 scale bytes | 32 packed e2m1], two 32-value slots per
12748    /// superblock. Since the 2026-08-29 door removal the permutation's ONLY consumer is the
12749    /// EP2 whole-expert bank build (`nvfp4_repack_bank_matrix(_, true)`), whose `*_ep`
12750    /// kernels and `qmatvec_nvfp4_fast_v2` oracle read this exact mapping.
12751    #[test]
12752    fn the_v2_bank_row_is_the_documented_slot_major_permutation() {
12753        // two rows, in_features 128 => 2 superblocks/row, 4 slots/row, 72 bytes/row.
12754        let (out_f, in_f) = (2usize, 128usize);
12755        let row_bytes = nvfp4_row_bytes(in_f);
12756        assert_eq!(row_bytes, 72);
12757        let v1: Vec<u8> = (0..out_f * row_bytes).map(|i| (i % 251) as u8).collect();
12758        let v2 = nvfp4_matrix_v2_permute(&v1, out_f, in_f);
12759        assert_eq!(v2.len(), v1.len(), "a permutation cannot change the size");
12760        let n_slots = in_f / 32;
12761        for row in 0..out_f {
12762            let src = &v1[row * row_bytes..(row + 1) * row_bytes];
12763            let dst = &v2[row * row_bytes..(row + 1) * row_bytes];
12764            for g in 0..n_slots {
12765                let (sblk, h) = (g / 2, g % 2);
12766                let sb = &src[sblk * 36..sblk * 36 + 36];
12767                assert_eq!(
12768                    &dst[g * 16..g * 16 + 16],
12769                    &sb[4 + 16 * h..4 + 16 * h + 16],
12770                    "row {row} slot {g} codes"
12771                );
12772                assert_eq!(
12773                    &dst[n_slots * 16 + g * 2..n_slots * 16 + g * 2 + 2],
12774                    &sb[2 * h..2 * h + 2],
12775                    "row {row} slot {g} scales"
12776                );
12777            }
12778            // and it moves bytes only: same multiset per row, rows never cross.
12779            let (mut a, mut b) = (src.to_vec(), dst.to_vec());
12780            a.sort_unstable();
12781            b.sort_unstable();
12782            assert_eq!(a, b, "row {row} is not a byte permutation");
12783        }
12784    }
12785}
12786
12787#[cfg(test)]
12788mod tests {
12789
12790    /// THE DEFECT, ASSERTED SO IT CANNOT COME BACK. The retired memo key hashed only the K
12791    /// pointer, the base pointer, the layer and t, while the table it returned ALSO carried
12792    /// the V and LEN pointers. Two different allocation generations that happen to share a K
12793    /// address therefore collide, and the entry the map hands back sends a live launch at
12794    /// another allocation's V and len. This test does not assert the key is fine; it asserts
12795    /// the key is BLIND, which is why `rows_tab_restage_on` exists and defaults ON.
12796    #[test]
12797    fn the_retired_rows_tab_key_cannot_see_the_v_and_len_pointers_it_hands_back() {
12798        let (kp, bp) = (0xdead_0000u64, 0u64);
12799        let live = [[kp, 0x00b1_0000u64, 0x00c1_0000u64, bp]];
12800        let recycled = [[kp, 0x00b2_0000u64, 0x00c2_0000u64, bp]];
12801        assert_eq!(
12802            super::retired_rows_tab_key(kp, bp, 20, 2),
12803            super::retired_rows_tab_key(kp, bp, 20, 2),
12804            "same layer and t must hash the same, or the test proves nothing"
12805        );
12806        let a = super::rows_tab_host(&live, 0x9000, true, 1);
12807        let b = super::rows_tab_host(&recycled, 0x9000, true, 1);
12808        assert_ne!(a, b, "the two generations write DIFFERENT tables");
12809        // ... yet one key covers both, which is exactly the use-after-free.
12810        assert_eq!(
12811            super::retired_rows_tab_key(live[0][0], live[0][3], 20, 1),
12812            super::retired_rows_tab_key(recycled[0][0], recycled[0][3], 20, 1),
12813            "the retired key collides across allocation generations"
12814        );
12815    }
12816
12817    /// The restage must be VALUE-NEUTRAL: on a fresh lookup the memo and the restage produce
12818    /// identical bytes, which is what makes spec-on output byte-identical to spec-off.
12819    #[test]
12820    fn rows_tab_layout_is_the_same_bytes_the_memo_would_have_cached() {
12821        let parts = [
12822            [0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64],
12823            [0x00a1u64, 0x00b1u64, 0x00c1u64, 0x00d1u64],
12824        ];
12825        let same = super::rows_tab_host(&parts, 0x7000, true, 2);
12826        assert_eq!(
12827            same,
12828            vec![
12829                0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64, 0x7000,
12830                1, // row 0: back = t-1-r = 1
12831                0x00a1u64, 0x00b1u64, 0x00c1u64, 0x00d1u64, 0x7000, 0, // row 1: back = 0
12832            ],
12833            "same-session rows share one counter cell and step back t-1-r"
12834        );
12835        let cross = super::rows_tab_host(&parts, 0x7000, false, 2);
12836        assert_eq!(
12837            cross,
12838            vec![
12839                0x00a0u64, 0x00b0u64, 0x00c0u64, 0x00d0u64, 0x7000, 0, 0x00a1u64, 0x00b1u64,
12840                0x00c1u64, 0x00d1u64, 0x7004, 0,
12841            ],
12842            "cross-session rows get their own counter cell and no step back"
12843        );
12844    }
12845    use super::*;
12846
12847    #[test]
12848    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
12849        let limit = Some(7.0);
12850        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
12851        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
12852        assert!(
12853            step_expert_activation_host(-20.0, 9.0, limit).abs()
12854                < step_expert_activation_host(-20.0, 9.0, None).abs()
12855        );
12856        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
12857        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
12858        assert!(validate_step_expert_activation_limit(limit).is_ok());
12859    }
12860
12861    #[test]
12862    fn moe_residual_host_preserves_official_add_order() {
12863        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
12864        assert_eq!(output, [0.0]);
12865        assert_eq!(
12866            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
12867            "MoE residual lengths residual=1 routed=2 shared=1"
12868        );
12869    }
12870
12871    #[test]
12872    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
12873        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
12874        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
12875        assert_eq!(owners.len(), 4);
12876        for (rank, owner) in owners.iter().enumerate() {
12877            assert_eq!(owner.rank, rank);
12878            assert_eq!(owner.selected, vec![0, 36]);
12879            assert_eq!(owner.token_rows, vec![0, 0]);
12880            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
12881        }
12882    }
12883
12884    #[test]
12885    fn expert_owner_routes_validate_geometry_and_selected_experts() {
12886        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
12887        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
12888        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
12889        assert!(error.contains("outside 0..288"));
12890    }
12891
12892    #[test]
12893    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
12894        let selected = [
12895            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
12896        ];
12897        assert_eq!(
12898            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
12899            16
12900        );
12901        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
12902        assert_eq!(
12903            owners
12904                .iter()
12905                .map(|owner| owner.selected.len())
12906                .collect::<Vec<_>>(),
12907            vec![2, 4, 6, 4]
12908        );
12909        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
12910        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
12911        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
12912    }
12913
12914    #[test]
12915    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
12916        let owner0 = [0usize, 3];
12917        let owner1 = [1usize, 2];
12918        let owners = [owner0.as_slice(), owner1.as_slice()];
12919        assert_eq!(
12920            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
12921                .unwrap(),
12922            WeightedRouteCombineShape {
12923                pairs: 4,
12924                max_pairs: 12,
12925            }
12926        );
12927        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
12928        assert!(
12929            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
12930                .is_err()
12931        );
12932        assert!(
12933            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
12934                .is_err()
12935        );
12936        assert!(
12937            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
12938                .is_err()
12939        );
12940    }
12941
12942    #[test]
12943    fn native_p2p_door_is_strict_and_default_off() {
12944        assert!(!parse_step_tp_native_p2p(None).unwrap());
12945        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
12946        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
12947        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
12948        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
12949        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
12950    }
12951
12952    #[test]
12953    fn bulk_p2p_door_is_strict_and_default_off() {
12954        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
12955        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
12956        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
12957        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
12958        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
12959        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
12960    }
12961
12962    #[test]
12963    fn ep_device_arithmetic_door_is_strict_and_default_off() {
12964        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
12965        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
12966        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
12967        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
12968        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
12969        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
12970    }
12971
12972    #[test]
12973    fn f32_mirror_door_is_strict_and_default_off() {
12974        assert!(!parse_step_tp_f32_mirror(None).unwrap());
12975        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
12976        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
12977        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
12978        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
12979        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
12980    }
12981
12982    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
12983        let codes = (0..out_features * in_features)
12984            .map(|index| (index % 251) as u8)
12985            .collect();
12986        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
12987            .map(|index| index as f32 + 1.0)
12988            .collect();
12989        (codes, scales)
12990    }
12991
12992    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
12993        (0..out_features * in_features)
12994            .flat_map(|value| (value as u16).to_le_bytes())
12995            .collect()
12996    }
12997
12998    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
12999        bytes
13000            .chunks_exact(2)
13001            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
13002            .collect()
13003    }
13004
13005    #[test]
13006    fn bf16_matrix_rejects_wrong_byte_count() {
13007        let bytes = vec![0u8; 4 * 4 * 2 - 1];
13008        let matrix = Bf16Matrix {
13009            bytes: &bytes,
13010            out_features: 4,
13011            in_features: 4,
13012        };
13013        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
13014    }
13015
13016    #[test]
13017    fn replicated_device_rows_require_exact_rank_local_shapes() {
13018        assert_eq!(
13019            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
13020            12_288
13021        );
13022        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
13023        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
13024        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
13025        assert!(
13026            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
13027        );
13028        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
13029    }
13030
13031    #[test]
13032    fn replicated_device_row_refresh_requires_exact_root_source() {
13033        assert_eq!(
13034            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
13035            12_288
13036        );
13037        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
13038        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
13039        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
13040        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
13041        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
13042    }
13043
13044    #[test]
13045    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
13046        for tp in [1, 2, 4, 8] {
13047            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
13048            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
13049            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
13050            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
13051            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
13052        }
13053        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
13054        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
13055        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
13056        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
13057    }
13058
13059    #[test]
13060    fn cache_rows_split_by_token_then_rank() {
13061        let rows = (0u8..24).collect::<Vec<_>>();
13062        assert_eq!(
13063            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
13064            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
13065        );
13066        assert_eq!(
13067            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
13068            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
13069        );
13070        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
13071        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
13072    }
13073
13074    #[test]
13075    fn bf16_column_shard_preserves_contiguous_output_rows() {
13076        let bytes = bf16_matrix_bytes(4, 4);
13077        let matrix = Bf16Matrix {
13078            bytes: &bytes,
13079            out_features: 4,
13080            in_features: 4,
13081        };
13082        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
13083        assert_eq!(shard.out_features, 2);
13084        assert_eq!(shard.in_features, 4);
13085        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
13086    }
13087
13088    #[test]
13089    fn bf16_row_shard_preserves_each_input_column_window() {
13090        let bytes = bf16_matrix_bytes(3, 4);
13091        let matrix = Bf16Matrix {
13092            bytes: &bytes,
13093            out_features: 3,
13094            in_features: 4,
13095        };
13096        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
13097        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
13098    }
13099
13100    #[test]
13101    fn bf16_row_block_preserves_global_column_order() {
13102        let bytes = bf16_matrix_bytes(3, 8);
13103        let matrix = Bf16Matrix {
13104            bytes: &bytes,
13105            out_features: 3,
13106            in_features: 8,
13107        };
13108        let block = bf16_row_block(matrix, 2, 3).unwrap();
13109        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
13110    }
13111
13112    #[test]
13113    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
13114        let (codes, scales) = matrix(1280, 4096);
13115        let matrix = E4m3BlockMatrix {
13116            codes: &codes,
13117            scales: &scales,
13118            out_features: 1280,
13119            in_features: 4096,
13120        };
13121        let shard = column_shard(matrix, 2, 1).unwrap();
13122        assert_eq!(shard.out_features, 640);
13123        assert_eq!(shard.codes, &codes[640 * 4096..]);
13124        assert_eq!(shard.scales, &scales[5 * 32..]);
13125    }
13126
13127    #[test]
13128    fn row_shard_preserves_each_weight_and_scale_column_window() {
13129        let (codes, scales) = matrix(4096, 1280);
13130        let matrix = E4m3BlockMatrix {
13131            codes: &codes,
13132            scales: &scales,
13133            out_features: 4096,
13134            in_features: 1280,
13135        };
13136        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
13137        assert_eq!(shard_codes.len(), 4096 * 640);
13138        assert_eq!(&shard_codes[..640], &codes[640..1280]);
13139        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
13140        assert_eq!(shard_scales.len(), 32 * 5);
13141        assert_eq!(&shard_scales[..5], &scales[5..10]);
13142        assert_eq!(&shard_scales[5..10], &scales[15..20]);
13143    }
13144
13145    #[test]
13146    fn activation_shards_keep_token_rows_separate() {
13147        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
13148        assert_eq!(
13149            activation_shard(&activations, 2, 8, 2, 1),
13150            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
13151        );
13152    }
13153
13154    #[test]
13155    fn expert_bank_selects_expert_major_code_and_scale_planes() {
13156        let expert_count = 2;
13157        let out_features = 128;
13158        let in_features = 128;
13159        let code_stride = out_features * in_features;
13160        let codes: Vec<u8> = (0..expert_count * code_stride)
13161            .map(|index| (index % 251) as u8)
13162            .collect();
13163        let scales = vec![1.0f32, 2.0];
13164        let bank = E4m3ExpertBank {
13165            codes: &codes,
13166            scales: &scales,
13167            expert_count,
13168            out_features,
13169            in_features,
13170        };
13171        bank.validate().unwrap();
13172        let expert = bank.expert(1).unwrap();
13173        assert_eq!(expert.codes, &codes[code_stride..]);
13174        assert_eq!(expert.scales, &[2.0]);
13175    }
13176
13177    #[test]
13178    fn expert_bank_rejects_non_positive_scale() {
13179        let codes = vec![0u8; 128 * 128];
13180        let scales = vec![0.0f32];
13181        let bank = E4m3ExpertBank {
13182            codes: &codes,
13183            scales: &scales,
13184            expert_count: 1,
13185            out_features: 128,
13186            in_features: 128,
13187        };
13188        assert!(bank.validate().unwrap_err().contains("non-positive"));
13189    }
13190
13191    #[test]
13192    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
13193        let expert_count = 2;
13194        let out_features = 256;
13195        let in_features = 128;
13196        let code_stride = out_features * in_features;
13197        let scale_stride = 2;
13198        let codes = (0..expert_count * code_stride)
13199            .map(|index| (index % 251) as u8)
13200            .collect::<Vec<_>>();
13201        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
13202        let bank = E4m3ExpertBank {
13203            codes: &codes,
13204            scales: &scales,
13205            expert_count,
13206            out_features,
13207            in_features,
13208        };
13209
13210        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
13211        assert_eq!(rank.out_features, 128);
13212        assert_eq!(rank.in_features, 128);
13213        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
13214        assert_eq!(rank.scales, vec![11.0, 21.0]);
13215        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
13216        assert_eq!(
13217            &rank.codes[128 * 128..],
13218            &codes[code_stride + 128 * 128..2 * code_stride]
13219        );
13220        assert_eq!(scale_stride, scales.len() / expert_count);
13221    }
13222
13223    #[test]
13224    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
13225        let expert_count = 2;
13226        let out_features = 128;
13227        let in_features = 256;
13228        let code_stride = out_features * in_features;
13229        let codes = (0..expert_count * code_stride)
13230            .map(|index| (index % 251) as u8)
13231            .collect::<Vec<_>>();
13232        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
13233        let bank = E4m3ExpertBank {
13234            codes: &codes,
13235            scales: &scales,
13236            expert_count,
13237            out_features,
13238            in_features,
13239        };
13240
13241        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
13242        assert_eq!(rank.out_features, 128);
13243        assert_eq!(rank.in_features, 128);
13244        assert_eq!(rank.k_blocks, Some(1));
13245        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
13246        assert_eq!(rank.scales, vec![11.0, 21.0]);
13247        assert_eq!(&rank.codes[..128], &codes[128..256]);
13248        assert_eq!(
13249            &rank.codes[128 * 128..128 * 128 + 128],
13250            &codes[code_stride + 128..code_stride + 256]
13251        );
13252    }
13253
13254    #[test]
13255    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
13256        let expert_count = 2;
13257        let out_features = 256;
13258        let in_features = 512;
13259        let code_stride = out_features * in_features;
13260        let mut codes = vec![0u8; expert_count * code_stride];
13261        for expert in 0..expert_count {
13262            for row in 0..out_features {
13263                for block in 0..4 {
13264                    let value = (expert * 80 + block * 16 + row % 16) as u8;
13265                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
13266                    codes[start..start + FP8_BLOCK].fill(value);
13267                }
13268            }
13269        }
13270        let scales = vec![
13271            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,
13272            112.0, 113.0, 114.0,
13273        ];
13274        let bank = E4m3ExpertBank {
13275            codes: &codes,
13276            scales: &scales,
13277            expert_count,
13278            out_features,
13279            in_features,
13280        };
13281
13282        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
13283        assert_eq!(rank.out_features, out_features);
13284        assert_eq!(rank.in_features, 256);
13285        assert_eq!(rank.k_blocks, Some(2));
13286        assert_eq!(rank.code_stride, out_features * 256);
13287        assert_eq!(rank.scale_stride, 4);
13288        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
13289        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
13290
13291        let block_stride = out_features * FP8_BLOCK;
13292        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
13293        assert!(
13294            rank.codes[block_stride..block_stride + FP8_BLOCK]
13295                .iter()
13296                .all(|&code| code == 48)
13297        );
13298        assert!(
13299            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
13300                .iter()
13301                .all(|&code| code == 112)
13302        );
13303        assert!(
13304            rank.codes
13305                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
13306                .iter()
13307                .all(|&code| code == 128)
13308        );
13309    }
13310
13311    #[test]
13312    fn step_ep_layer_specs_are_literal_and_fail_closed() {
13313        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
13314        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
13315        assert_eq!(
13316            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
13317            vec![StepEpLayerSpec {
13318                layer: 24,
13319                devices: vec![1, 2],
13320            }]
13321        );
13322        assert_eq!(
13323            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
13324            vec![
13325                StepEpLayerSpec {
13326                    layer: 24,
13327                    devices: vec![1, 2],
13328                },
13329                StepEpLayerSpec {
13330                    layer: 25,
13331                    devices: vec![1, 2],
13332                },
13333                StepEpLayerSpec {
13334                    layer: 31,
13335                    devices: vec![0, 2],
13336                },
13337            ]
13338        );
13339        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
13340        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
13341        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
13342        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
13343        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
13344        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
13345        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
13346    }
13347
13348    #[test]
13349    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
13350        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
13351        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
13352        assert_eq!(
13353            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
13354            vec![
13355                StepTpLayerSpec {
13356                    layer: 24,
13357                    devices: vec![1, 2],
13358                },
13359                StepTpLayerSpec {
13360                    layer: 25,
13361                    devices: vec![1, 2],
13362                },
13363            ]
13364        );
13365        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
13366        assert!(error.contains("MEMRA_STEP_TP"));
13367        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
13368        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
13369
13370        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
13371        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
13372        assert_eq!(all.first().unwrap().layer, 0);
13373        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
13374        let devices = (0..8).collect::<Vec<_>>();
13375        assert!(all.iter().all(|spec| spec.devices == devices));
13376        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
13377    }
13378}
13379
13380// ===== Whole-token graph builder (increment B) ==================================================
13381//
13382// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
13383// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
13384// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
13385// device and records a child + its dependency edges. A token then assembles as ONE multi-device
13386// parent (children per section per layer), launched once per token — the launch-collapse the
13387// per-layer minis could not reach (routes-mini negative, 2026-08-21).
13388
13389/// One captured section: the child graph plus which parent node it became, and the CUDA
13390/// context it was captured under (exec memset updates need it).
13391struct TokenGraphChild {
13392    graph: cudarc::driver::CudaGraph,
13393    node: cudarc::driver::sys::CUgraphNode,
13394    ctx: cudarc::driver::sys::CUcontext,
13395}
13396
13397/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
13398/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
13399/// handles address the parent's CLONED child graphs (the M1-probed update path).
13400struct TokenGraphFaSite {
13401    ctx: cudarc::driver::sys::CUcontext,
13402    memset_o: cudarc::driver::sys::CUgraphNode,
13403    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
13404    fa: cudarc::driver::sys::CUgraphNode,
13405    combine: cudarc::driver::sys::CUgraphNode,
13406    window: usize,
13407    n_head: usize,
13408    n_head_kv: usize,
13409    head_dim: usize,
13410}
13411
13412pub struct TokenGraphBuilder {
13413    parent: cudarc::driver::sys::CUgraph,
13414    children: Vec<TokenGraphChild>,
13415    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
13416    /// several while a parallel group is open.
13417    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
13418    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
13419    /// non-group section (they never gate a parallel group merge — the SH1 shape).
13420    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
13421    /// Open parallel group: sections issued under the same group id fork from the SAME
13422    /// predecessor set and merge into the frontier together when the group closes.
13423    group: Option<(
13424        u32,
13425        Vec<cudarc::driver::sys::CUgraphNode>,
13426        Vec<cudarc::driver::sys::CUgraphNode>,
13427    )>,
13428}
13429
13430// SAFETY: single decode thread; graph handles are process handles.
13431unsafe impl Send for TokenGraphBuilder {}
13432
13433impl TokenGraphBuilder {
13434    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
13435        use cudarc::driver::sys;
13436        let mut parent: sys::CUgraph = std::ptr::null_mut();
13437        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
13438        if r != sys::CUresult::CUDA_SUCCESS {
13439            return Err(format!("token graph create: {r:?}").into());
13440        }
13441        Ok(Self {
13442            parent,
13443            children: Vec::new(),
13444            frontier: Vec::new(),
13445            pending_detached: Vec::new(),
13446            group: None,
13447        })
13448    }
13449
13450    fn push_child(
13451        &mut self,
13452        graph: cudarc::driver::CudaGraph,
13453        parallel_group: Option<u32>,
13454        detached: bool,
13455        absorb: bool,
13456        ctx: cudarc::driver::sys::CUcontext,
13457    ) -> Result<(), Box<dyn std::error::Error>> {
13458        use cudarc::driver::sys;
13459        // Resolve the dependency set: serial sections depend on the current frontier; a
13460        // parallel-group section depends on the frontier AS OF the group opening; a
13461        // DETACHED section forks like a group member but joins only the next serial section.
13462        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
13463            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
13464            (state, Some(group)) => {
13465                // opening a new group (closing any previous one first)
13466                if let Some((_, _, members)) = state.take() {
13467                    self.frontier = members;
13468                }
13469                let base = self.frontier.clone();
13470                *state = Some((group, base.clone(), Vec::new()));
13471                base
13472            }
13473            (state, None) if detached => match state.as_ref() {
13474                Some((_, base, _)) => base.clone(),
13475                None => self.frontier.clone(),
13476            },
13477            (state, None) => {
13478                if let Some((_, _, members)) = state.take() {
13479                    self.frontier = members;
13480                }
13481                let mut deps = self.frontier.clone();
13482                if absorb {
13483                    deps.append(&mut self.pending_detached);
13484                }
13485                deps
13486            }
13487        };
13488        let mut node: sys::CUgraphNode = std::ptr::null_mut();
13489        let r = unsafe {
13490            sys::cuGraphAddChildGraphNode(
13491                &mut node,
13492                self.parent,
13493                if deps.is_empty() {
13494                    std::ptr::null()
13495                } else {
13496                    deps.as_ptr()
13497                },
13498                deps.len(),
13499                graph.cu_graph(),
13500            )
13501        };
13502        if r != sys::CUresult::CUDA_SUCCESS {
13503            return Err(format!("token graph child: {r:?}").into());
13504        }
13505        match (&mut self.group, parallel_group, detached) {
13506            (_, None, true) => self.pending_detached.push(node),
13507            (Some((_, _, members)), Some(_), _) => members.push(node),
13508            _ => self.frontier = vec![node],
13509        }
13510        self.children.push(TokenGraphChild { graph, node, ctx });
13511        Ok(())
13512    }
13513
13514    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
13515        use cudarc::driver::sys;
13516        if let Some((_, _, members)) = self.group.take() {
13517            self.frontier = members;
13518        }
13519        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
13520        // the node handles the exec update path (M1) addresses.
13521        let mut fa_sites = Vec::new();
13522        for child in &self.children {
13523            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
13524                fa_sites.push(site);
13525            }
13526        }
13527        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
13528        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
13529        if r != sys::CUresult::CUDA_SUCCESS {
13530            return Err(format!("token graph instantiate: {r:?}").into());
13531        }
13532        Ok(TokenGraph {
13533            exec,
13534            parent: self.parent,
13535            _children: self.children,
13536            fa_sites,
13537        })
13538    }
13539}
13540
13541/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
13542/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
13543fn discover_fa_site(
13544    child_node: cudarc::driver::sys::CUgraphNode,
13545    ctx: cudarc::driver::sys::CUcontext,
13546) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
13547    use cudarc::driver::sys;
13548    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
13549        if r == sys::CUresult::CUDA_SUCCESS {
13550            Ok(())
13551        } else {
13552            Err(format!("{what}: {r:?}").into())
13553        }
13554    }
13555    let mut graph: sys::CUgraph = std::ptr::null_mut();
13556    unsafe {
13557        cu_try(
13558            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
13559            "fa-site child GetGraph",
13560        )?;
13561    }
13562    let mut count: usize = 0;
13563    unsafe {
13564        cu_try(
13565            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
13566            "fa-site GetNodes(count)",
13567        )?;
13568    }
13569    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
13570    unsafe {
13571        cu_try(
13572            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
13573            "fa-site GetNodes",
13574        )?;
13575    }
13576    nodes.truncate(count);
13577    let node_type =
13578        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
13579            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
13580            unsafe {
13581                cu_try(
13582                    sys::cuGraphNodeGetType(node, &mut ty),
13583                    "fa-site NodeGetType",
13584                )?;
13585            }
13586            Ok(ty)
13587        };
13588    let memsets: Vec<sys::CUgraphNode> = {
13589        let mut v = Vec::new();
13590        for &node in &nodes {
13591            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
13592                v.push(node);
13593            }
13594        }
13595        v
13596    };
13597    if memsets.len() != 3 {
13598        return Ok(None);
13599    }
13600    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
13601    let dependents =
13602        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
13603            let mut n: usize = 0;
13604            unsafe {
13605                cu_try(
13606                    sys::cuGraphNodeGetDependentNodes_v2(
13607                        node,
13608                        std::ptr::null_mut(),
13609                        std::ptr::null_mut(),
13610                        &mut n,
13611                    ),
13612                    "fa-site GetDependentNodes(count)",
13613                )?;
13614            }
13615            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
13616            unsafe {
13617                cu_try(
13618                    sys::cuGraphNodeGetDependentNodes_v2(
13619                        node,
13620                        v.as_mut_ptr(),
13621                        std::ptr::null_mut(),
13622                        &mut n,
13623                    ),
13624                    "fa-site GetDependentNodes",
13625                )?;
13626            }
13627            v.truncate(n);
13628            Ok(v)
13629        };
13630    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
13631    // ordered among themselves but interchangeable for width updates.
13632    let mut fa: Option<sys::CUgraphNode> = None;
13633    let mut last_memset: Option<sys::CUgraphNode> = None;
13634    for &ms in &memsets {
13635        for dep in dependents(ms)? {
13636            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
13637                fa = Some(dep);
13638                last_memset = Some(ms);
13639            }
13640        }
13641    }
13642    let (Some(fa), Some(_last)) = (fa, last_memset) else {
13643        return Ok(None);
13644    };
13645    let mut combine: Option<sys::CUgraphNode> = None;
13646    for dep in dependents(fa)? {
13647        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
13648            combine = Some(dep);
13649        }
13650    }
13651    let Some(combine) = combine else {
13652        return Ok(None);
13653    };
13654    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
13655    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
13656    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13657    unsafe {
13658        cu_try(
13659            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
13660            "fa-site KernelNodeGetParams",
13661        )?;
13662    }
13663    let arg_i32 =
13664        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
13665    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
13666    // Identify the o-partial memset (hd x wider than the m/l pair).
13667    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
13668        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
13669        unsafe {
13670            cu_try(
13671                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
13672                "fa-site MemsetNodeGetParams",
13673            )?;
13674        }
13675        Ok(mp.width)
13676    };
13677    let mut widest = memsets[0];
13678    for &ms in &memsets[1..] {
13679        if width_of(ms)? > width_of(widest)? {
13680            widest = ms;
13681        }
13682    }
13683    let memset_m: Vec<sys::CUgraphNode> =
13684        memsets.iter().copied().filter(|&m| m != widest).collect();
13685    Ok(Some(TokenGraphFaSite {
13686        ctx,
13687        memset_o: widest,
13688        memset_m: [memset_m[0], memset_m[1]],
13689        fa,
13690        combine,
13691        window: win as usize,
13692        n_head: nh as usize,
13693        n_head_kv: nhkv as usize,
13694        head_dim: hd as usize,
13695    }))
13696}
13697
13698pub struct TokenGraph {
13699    exec: cudarc::driver::sys::CUgraphExec,
13700    parent: cudarc::driver::sys::CUgraph,
13701    _children: Vec<TokenGraphChild>,
13702    fa_sites: Vec<TokenGraphFaSite>,
13703}
13704
13705unsafe impl Send for TokenGraph {}
13706
13707impl TokenGraph {
13708    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
13709    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
13710    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
13711    /// move together so the exec always matches what a fresh build at `bucket` would bake.
13712    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
13713        use cudarc::driver::sys;
13714        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
13715            if r == sys::CUresult::CUDA_SUCCESS {
13716                Ok(())
13717            } else {
13718                Err(format!("{what}: {r:?}").into())
13719            }
13720        }
13721        for site in &self.fa_sites {
13722            let layer_bucket = if site.window > 0 {
13723                bucket.min(site.window)
13724            } else {
13725                bucket
13726            };
13727            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
13728            let nsp = layer_bucket.div_ceil(sp).max(1);
13729            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
13730            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13731            unsafe {
13732                cu_try(
13733                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
13734                    "retarget fa GetParams",
13735                )?;
13736                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
13737                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
13738                params.gridDimY = nsp as u32;
13739                cu_try(
13740                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
13741                    "retarget fa SetParams",
13742                )?;
13743            }
13744            // combine: nsp (slot 6).
13745            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13746            unsafe {
13747                cu_try(
13748                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
13749                    "retarget combine GetParams",
13750                )?;
13751                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
13752                cu_try(
13753                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
13754                    "retarget combine SetParams",
13755                )?;
13756            }
13757            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
13758            let set_width =
13759                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
13760                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
13761                    unsafe {
13762                        cu_try(
13763                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
13764                            "retarget memset GetParams",
13765                        )?;
13766                    }
13767                    mp.width = width;
13768                    unsafe {
13769                        cu_try(
13770                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
13771                            "retarget memset SetParams",
13772                        )?;
13773                    }
13774                    Ok(())
13775                };
13776            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
13777            set_width(site.memset_m[0], site.n_head * nsp)?;
13778            set_width(site.memset_m[1], site.n_head * nsp)?;
13779        }
13780        Ok(())
13781    }
13782
13783    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
13784        use cudarc::driver::sys;
13785        let _main = e.gpu.enter_main()?;
13786        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
13787        if r != sys::CUresult::CUDA_SUCCESS {
13788            return Err(format!("token graph launch: {r:?}").into());
13789        }
13790        Ok(())
13791    }
13792}
13793
13794impl Drop for TokenGraph {
13795    fn drop(&mut self) {
13796        unsafe {
13797            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
13798            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
13799        }
13800    }
13801}
13802
13803std::thread_local! {
13804    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
13805        const { std::cell::RefCell::new(None) };
13806}
13807
13808/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
13809pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
13810    let builder = TokenGraphBuilder::new()?;
13811    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
13812    Ok(())
13813}
13814
13815/// Take the finished parent (ends build mode).
13816pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
13817    let builder = TOKEN_GRAPH_BUILDER
13818        .with(|cell| cell.borrow_mut().take())
13819        .ok_or("token graph build was not begun")?;
13820    builder.finish()
13821}
13822
13823/// True while the thread-local builder is armed.
13824pub fn token_graph_building() -> bool {
13825    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
13826}
13827
13828/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
13829/// stream capture on `engine`'s stream and records the child. Sections sharing a
13830/// `parallel_group` id fork from the same predecessor set and merge together. The closure
13831/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
13832pub fn graph_section<F>(
13833    engine: &Engine,
13834    parallel_group: Option<u32>,
13835    f: F,
13836) -> Result<(), Box<dyn std::error::Error>>
13837where
13838    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13839{
13840    graph_section_opts(engine, parallel_group, false, false, f)
13841}
13842
13843/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
13844pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
13845where
13846    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13847{
13848    graph_section_opts(engine, None, false, true, f)
13849}
13850
13851/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
13852/// group base) and is joined only by the next serial section — never gates a group merge.
13853pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
13854where
13855    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13856{
13857    graph_section_opts(engine, None, true, false, f)
13858}
13859
13860pub fn graph_section_opts<F>(
13861    engine: &Engine,
13862    parallel_group: Option<u32>,
13863    detached: bool,
13864    absorb: bool,
13865    f: F,
13866) -> Result<(), Box<dyn std::error::Error>>
13867where
13868    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13869{
13870    let building = token_graph_building();
13871    if !building {
13872        let mut f = f;
13873        return f();
13874    }
13875    let (child, ctx) = {
13876        let _main = engine.gpu.enter_main()?;
13877        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
13878        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
13879        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
13880            return Err(format!("graph section ctx query: {r:?}").into());
13881        }
13882        let mut f = f;
13883        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
13884        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
13885        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
13886        (child, ctx)
13887    };
13888    TOKEN_GRAPH_BUILDER.with(|cell| {
13889        cell.borrow_mut()
13890            .as_mut()
13891            .expect("builder checked above")
13892            .push_child(child, parallel_group, detached, absorb, ctx)
13893    })
13894}