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
14const FP8_BLOCK: usize = 128;
15const NATIVE_P2P_PROBE_WORDS: usize = 4096;
16const STEP_GROUPED_FP8_EXPERTS: usize = 288;
17const STEP_GROUPED_FP8_TOP_K: usize = 8;
18const STEP_GROUPED_FP8_WIDTH: usize = 1280;
19
20fn validate_step_expert_activation_limit(limit: Option<f32>) -> Result<(), String> {
21    if let Some(limit) = limit {
22        if !limit.is_finite() || limit <= 0.0 {
23            return Err(format!(
24                "Step routed-expert activation limit must be positive and finite, got {limit}"
25            ));
26        }
27    }
28    Ok(())
29}
30
31/// Host-canonical Step routed-expert SwiGLU operation.
32///
33/// Step's final routed layers clamp the linear arm symmetrically and the SiLU arm only above.
34/// Keeping this scalar order explicit also defines the device-host-exact CUDA gate.
35/// Raw stream-ordered device copy for capture-safe cross-context seams (cudarc's slice-use
36/// tracking creates capture-illegal dependencies there). Pointers must be pre-cached with
37/// their owners' streams; bytes flow identically to the tracked copy.
38/// MEMRA_OPROJ_DIRECT=1 (o-proj direct join, default OFF until gated): peer ranks write
39/// their fused O partial OVER P2P into a root-resident buffer (UVA kernel stores), and the
40/// model engine adds the two partials itself — the root stream leaves the join entirely
41/// (no peer pull copy, no root add, no second event hop, no final 16KB ownership copy).
42/// Reduction order and kernel programs are unchanged, so the row is BIT-IDENTICAL.
43/// MEMRA_MOE_DIRECT=1 (moe direct join, default OFF until gated): the o-proj direct-join
44/// recipe on the expert combine — peer ranks' accumulators live root-side (the axpy twin
45/// register-accumulates and stores ONCE, so the P2P cost is a single 16KB store pass), and
46/// the model engine adds the two shard rows itself. Operand order matches root's add:
47/// BIT-IDENTICAL.
48/// MEMRA_ROUTES_PRESTAGE=1 (default OFF until gated): stage the shared layer input to
49/// every rank and quantize it BEFORE the router runs — neither depends on the selection,
50/// so the rank streams' pull+quantize overlaps dev0's router gemv+topk instead of chaining
51/// behind it (the router->quantize and axpy->add gap edges). Same copies, same quantize
52/// kernel, same operands: BIT-IDENTICAL.
53pub(crate) fn routes_prestage_on() -> bool {
54    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
55    *ON.get_or_init(|| std::env::var("MEMRA_ROUTES_PRESTAGE").as_deref() == Ok("1"))
56}
57
58/// MEMRA_FENCE_MEMOPS=1 (default OFF until gated): the moe direct join's two event
59/// fences become cuStreamWriteValue32/cuStreamWaitValue32 doorbells — hardware stream
60/// memops with lower signal->wake latency than cross-device cuStreamWaitEvent. Ordering:
61/// PCIe posted writes from one device arrive in order, so rank1's accumulator stores are
62/// visible before its flag write lands; e's GEQ wait then covers them. Falls back to
63/// events when the device rejects stream memops. Scheduling-only: BIT-IDENTICAL values.
64/// MEMRA_LEN_MIRROR_LAZY=1 (default OFF until gated): skip redundant per-layer 4B len
65/// htods — the local device mirror is unread in TP decode, and under FUSE_ROPE_APPEND the
66/// fused append's atomicInc owns the rank counters. Every one of those tiny copies is a
67/// compute->copy engine turnaround in the middle of the layer stream.
68/// MEMRA_RANK0_MERGE=1 (default OFF until gated): same-device rank0 rides e's stream via
69/// the runtime redirect — see decode_step_h.
70/// MEMRA_OPROJ_TAIL=1 (default OFF until gated): the o-proj direct-join add is DEFERRED —
71/// the finish arm keeps its waits, stores the two partial pointers here, and the residual
72/// add_rms_norm consumer composes mixed = a0+a1 in-register (join_add_rms_norm, verbatim
73/// program: BIT-IDENTICAL). The returned `mixed` buffer is UNWRITTEN in this mode; its
74/// only live consumer is the residual_norm_ffn seam, which takes the handoff.
75pub(crate) fn oproj_tail_on() -> bool {
76    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
77    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_TAIL").as_deref() == Ok("1"))
78}
79thread_local! {
80    static OPROJ_TAIL_PENDING: std::cell::Cell<Option<(u64, u64)>> =
81        const { std::cell::Cell::new(None) };
82}
83thread_local! {
84    /// The deferral is legal ONLY under callers whose walk flows into
85    /// residual_norm_ffn (decode_step_h / decode_step_chain arm this) — the verify
86    /// prefill reaches the same finish and would consume unwritten `mixed` otherwise
87    /// (M2-MISMATCH receipt: prefill argmax corrupted while decode stayed exact).
88    static OPROJ_TAIL_ELIGIBLE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
89}
90/// RAII eligibility scope for the o-proj tail deferral.
91pub(crate) struct OprojTailScope(());
92pub(crate) fn oproj_tail_scope() -> OprojTailScope {
93    OPROJ_TAIL_ELIGIBLE.with(|c| c.set(true));
94    OprojTailScope(())
95}
96impl Drop for OprojTailScope {
97    fn drop(&mut self) {
98        OPROJ_TAIL_ELIGIBLE.with(|c| c.set(false));
99        // A leftover un-consumed handoff must never leak across calls.
100        OPROJ_TAIL_PENDING.with(|c| c.set(None));
101    }
102}
103thread_local! {
104    /// T-COLUMN verify select: the verify driver sets the column before each per-column
105    /// attention call; decode_v2_input_qkv takes it (once) and selects from the slabs.
106    static VERIFY_TCOL: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
107}
108pub(crate) fn set_verify_tcol(c: Option<usize>) {
109    VERIFY_TCOL.with(|x| x.set(c));
110}
111pub(crate) fn take_verify_tcol() -> Option<usize> {
112    VERIFY_TCOL.with(|x| x.take())
113}
114
115/// MEMRA_TCOL_OPROJ=1 (spec verify): defer each column's o_proj out of the per-column
116/// walk — the finish seam stashes the column's `gated` rows instead of running the
117/// per-column finish choreography (rank events, P2P join, engine handoff), and one
118/// weight-amortized b4_tcol per rank + one elementwise join produce every column's
119/// `mixed` afterwards. Bit-exact per column: the tcol kernel is the t=1 b4 program per
120/// column, and the slab join adds the same operand values elementwise.
121pub(crate) fn tcol_oproj_on() -> bool {
122    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
123    *ON.get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ").as_deref() == Ok("1"))
124}
125thread_local! {
126    /// The verify driver arms the column before each per-column attention call; the
127    /// finish seam takes it (once). Stashed=true reports the defer actually happened
128    /// (the seam falls back to the normal finish when the config is ineligible).
129    static TCOL_OPROJ_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
130    static TCOL_OPROJ_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
131}
132pub(crate) fn set_tcol_oproj_defer(c: Option<usize>) {
133    TCOL_OPROJ_DEFER.with(|x| x.set(c));
134}
135pub(crate) fn take_tcol_oproj_defer() -> Option<usize> {
136    TCOL_OPROJ_DEFER.with(|x| x.take())
137}
138pub(crate) fn set_tcol_oproj_stashed() {
139    TCOL_OPROJ_STASHED.with(|x| x.set(true));
140}
141pub(crate) fn take_tcol_oproj_stashed() -> bool {
142    TCOL_OPROJ_STASHED.with(|x| x.replace(false))
143}
144
145pub(crate) fn oproj_tail_eligible() -> bool {
146    OPROJ_TAIL_ELIGIBLE.with(|c| c.get())
147}
148pub(crate) fn take_oproj_tail() -> Option<(u64, u64)> {
149    OPROJ_TAIL_PENDING.with(|c| c.take())
150}
151pub(crate) fn set_oproj_tail(v: (u64, u64)) {
152    OPROJ_TAIL_PENDING.with(|c| c.set(Some(v)));
153}
154
155pub(crate) fn rank0_merge_on() -> bool {
156    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
157    *ON.get_or_init(|| std::env::var("MEMRA_RANK0_MERGE").as_deref() == Ok("1"))
158}
159
160pub(crate) fn len_mirror_lazy_on() -> bool {
161    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
162    *ON.get_or_init(|| std::env::var("MEMRA_LEN_MIRROR_LAZY").as_deref() == Ok("1"))
163}
164
165pub(crate) fn fence_memops_on() -> bool {
166    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
167    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_MEMOPS").as_deref() == Ok("1"))
168}
169
170pub(crate) fn moe_direct_on() -> bool {
171    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
172    *ON.get_or_init(|| std::env::var("MEMRA_MOE_DIRECT").as_deref() == Ok("1"))
173}
174
175pub(crate) fn oproj_direct_on() -> bool {
176    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
177    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
178}
179
180pub(crate) fn raw_copy_bytes(
181    dst: u64,
182    src: u64,
183    bytes: usize,
184    engine: &Engine,
185) -> Result<(), Box<dyn std::error::Error>> {
186    use cudarc::driver::sys;
187    let r = unsafe {
188        sys::cuMemcpyAsync(
189            dst as sys::CUdeviceptr,
190            src as sys::CUdeviceptr,
191            bytes,
192            engine.stream().cu_stream() as sys::CUstream,
193        )
194    };
195    if r == sys::CUresult::CUDA_SUCCESS {
196        Ok(())
197    } else {
198        Err(format!("raw_copy_bytes: {r:?}").into())
199    }
200}
201
202pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
203    let silu = gate / (1.0 + (-gate).exp());
204    match limit {
205        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
206        None => silu * up,
207    }
208}
209
210#[derive(Debug, Clone, PartialEq, Eq)]
211struct ExpertOwnerRoutes {
212    rank: usize,
213    selected: Vec<usize>,
214    token_rows: Vec<usize>,
215    global_pairs: Vec<usize>,
216}
217
218fn partition_expert_owner_routes(
219    expert_count: usize,
220    ranks: usize,
221    tokens: usize,
222    experts_per_token: usize,
223    selected: &[usize],
224) -> Result<Vec<ExpertOwnerRoutes>, String> {
225    if expert_count == 0
226        || ranks == 0
227        || tokens == 0
228        || experts_per_token == 0
229        || expert_count % ranks != 0
230    {
231        return Err(format!(
232            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
233             tokens={tokens} experts_per_token={experts_per_token}"
234        ));
235    }
236    let pairs = tokens
237        .checked_mul(experts_per_token)
238        .ok_or("expert-owner route count overflow")?;
239    if selected.len() != pairs {
240        return Err(format!(
241            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
242            selected.len()
243        ));
244    }
245    let per_rank = expert_count / ranks;
246    let mut owners = (0..ranks)
247        .map(|rank| ExpertOwnerRoutes {
248            rank,
249            selected: Vec::new(),
250            token_rows: Vec::new(),
251            global_pairs: Vec::new(),
252        })
253        .collect::<Vec<_>>();
254    for (pair, &expert) in selected.iter().enumerate() {
255        if expert >= expert_count {
256            return Err(format!(
257                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
258            ));
259        }
260        let rank = expert / per_rank;
261        owners[rank].selected.push(expert - rank * per_rank);
262        owners[rank].token_rows.push(pair / experts_per_token);
263        owners[rank].global_pairs.push(pair);
264    }
265    Ok(owners)
266}
267
268fn validate_step_grouped_owner_routes(
269    expert_count: usize,
270    tokens: usize,
271    selected: &[usize],
272) -> Result<usize, String> {
273    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
274        return Err(format!(
275            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
276             experts={expert_count} tokens={tokens}",
277            STEP_GROUPED_FP8_EXPERTS
278        ));
279    }
280    let pairs = tokens
281        .checked_mul(STEP_GROUPED_FP8_TOP_K)
282        .ok_or("official Step owner-grouped FP8 route count overflow")?;
283    if selected.len() != pairs {
284        return Err(format!(
285            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
286            selected.len(),
287            STEP_GROUPED_FP8_TOP_K,
288        ));
289    }
290    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
291        let mut unique = routes.to_vec();
292        unique.sort_unstable();
293        unique.dedup();
294        if unique.len() != STEP_GROUPED_FP8_TOP_K {
295            return Err(format!(
296                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
297                 {routes:?}"
298            ));
299        }
300    }
301    Ok(pairs)
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305struct WeightedRouteCombineShape {
306    pairs: usize,
307    max_pairs: usize,
308}
309
310fn validate_weighted_route_combine(
311    width: usize,
312    experts_per_token: usize,
313    max_tokens: usize,
314    tokens: usize,
315    owner_global_pairs: &[&[usize]],
316    route_weights: &[f32],
317) -> Result<WeightedRouteCombineShape, String> {
318    if width == 0
319        || experts_per_token == 0
320        || max_tokens == 0
321        || tokens == 0
322        || tokens > max_tokens
323        || width > i32::MAX as usize
324        || experts_per_token > i32::MAX as usize
325        || tokens > i32::MAX as usize
326    {
327        return Err(format!(
328            "invalid weighted route combine geometry width={width} experts_per_token=\
329             {experts_per_token} tokens={tokens}/{max_tokens}"
330        ));
331    }
332    let pairs = tokens
333        .checked_mul(experts_per_token)
334        .ok_or("weighted route combine pair count overflow")?;
335    let max_pairs = max_tokens
336        .checked_mul(experts_per_token)
337        .ok_or("weighted route combine capacity overflow")?;
338    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
339        return Err(format!(
340            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
341            route_weights.len()
342        ));
343    }
344    let mut seen = vec![false; pairs];
345    let mut observed = 0usize;
346    for pairs_for_owner in owner_global_pairs {
347        observed = observed
348            .checked_add(pairs_for_owner.len())
349            .ok_or("weighted route combine observed pair count overflow")?;
350        for &pair in *pairs_for_owner {
351            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
352                return Err(format!(
353                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
354                ));
355            }
356        }
357    }
358    if observed != pairs || seen.iter().any(|present| !present) {
359        return Err(format!(
360            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
361        ));
362    }
363    Ok(WeightedRouteCombineShape { pairs, max_pairs })
364}
365
366fn cache_rank_rows(
367    rows: &[u8],
368    tokens: usize,
369    local_token_bytes: usize,
370    ranks: usize,
371    rank: usize,
372) -> Result<Vec<u8>, String> {
373    if ranks == 0 || rank >= ranks {
374        return Err(format!(
375            "TP cache rank {rank} is outside a {ranks}-rank layout"
376        ));
377    }
378    let global_token_bytes = local_token_bytes
379        .checked_mul(ranks)
380        .ok_or("TP cache global token-byte overflow")?;
381    let expected = tokens
382        .checked_mul(global_token_bytes)
383        .ok_or("TP cache row-byte overflow")?;
384    if rows.len() != expected {
385        return Err(format!(
386            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
387            rows.len()
388        ));
389    }
390    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
391    for token in 0..tokens {
392        let start = token * global_token_bytes + rank * local_token_bytes;
393        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
394    }
395    Ok(shard)
396}
397
398fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
399    match value {
400        None | Some("") | Some("0") => Ok(false),
401        Some("1") => Ok(true),
402        Some(value) => Err(format!(
403            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
404        )),
405    }
406}
407
408pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
409    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
410}
411
412fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
413    match value {
414        None | Some("") | Some("0") => Ok(false),
415        Some("1") => Ok(true),
416        Some(value) => Err(format!(
417            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
418        )),
419    }
420}
421
422pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
423    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
424}
425
426fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
427    match value {
428        None | Some("") | Some("0") => Ok(false),
429        Some("1") => Ok(true),
430        Some(value) => Err(format!(
431            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
432        )),
433    }
434}
435
436fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
437    match value {
438        None | Some("") | Some("0") => Ok(false),
439        Some("1") => Ok(true),
440        Some(value) => Err(format!(
441            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
442        )),
443    }
444}
445
446/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
447/// host-canonical program remains the oracle until the device path carries its own gates.
448pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
449    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
450}
451
452pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
453    parse_step_ep_device_arithmetic(
454        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
455            .ok()
456            .as_deref(),
457    )
458}
459
460fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
461    match value {
462        None | Some("") | Some("0") => Ok(false),
463        Some("1") => Ok(true),
464        Some(value) => Err(format!(
465            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
466        )),
467    }
468}
469
470pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
471    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
472}
473
474fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
475    match value {
476        None | Some("") | Some("0") => Ok(false),
477        Some("1") => Ok(true),
478        Some(value) => Err(format!(
479            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
480        )),
481    }
482}
483
484/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
485/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
486/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
487/// side of the comparison).
488pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
489    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
490}
491
492fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
493    match value {
494        None | Some("") | Some("0") => Ok(false),
495        Some("1") => Ok(true),
496        Some(value) => Err(format!(
497            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
498        )),
499    }
500}
501
502fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
503    match value {
504        None | Some("") | Some("0") => Ok(false),
505        Some("1") => Ok(true),
506        Some(value) => Err(format!(
507            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
508        )),
509    }
510}
511
512/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
513/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
514/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
515pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
516    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
517}
518
519fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
520    match value {
521        None | Some("") | Some("0") => Ok(false),
522        Some("1") => Ok(true),
523        Some(value) => Err(format!(
524            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
525        )),
526    }
527}
528
529fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
530    match value {
531        None | Some("") | Some("0") => Ok(false),
532        Some("1") => Ok(true),
533        Some(value) => Err(format!(
534            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
535        )),
536    }
537}
538
539/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
540/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
541/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
542/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
543pub fn step_tp_dcw_enabled() -> Result<bool, String> {
544    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
545}
546
547/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
548/// expert program — per-layer multi-device parents built from per-rank children, launched on
549/// the model engine's stream; zero per-token node updates). Mechanism proven by
550/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
551pub fn step_tp_graph_enabled() -> Result<bool, String> {
552    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
553}
554
555/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
556/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
557/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
558pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
559    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
560}
561
562#[derive(Debug, Clone, PartialEq, Eq)]
563pub struct StepEpLayerSpec {
564    pub layer: usize,
565    pub devices: Vec<usize>,
566}
567
568pub type StepTpLayerSpec = StepEpLayerSpec;
569
570fn parse_step_layer_specs(
571    flag: &str,
572    value: Option<&str>,
573    allow_full_model: bool,
574) -> Result<Vec<StepEpLayerSpec>, String> {
575    let Some(value) = value else {
576        return Ok(Vec::new());
577    };
578    if value.is_empty() || value == "0" {
579        return Ok(Vec::new());
580    }
581
582    let mut specs = Vec::new();
583    for item in value.split(';') {
584        let (layers, devices) = item.split_once('@').ok_or_else(|| {
585            let layers = if allow_full_model {
586                "LAYER[-LAYER] or all"
587            } else {
588                "LAYER[-LAYER]"
589            };
590            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
591        })?;
592        let (first, last) = if layers == "all" {
593            if !allow_full_model {
594                return Err(format!(
595                    "{flag} does not support the full-model shorthand; assign routed layers \
596                     explicitly"
597                ));
598            }
599            (0, STEP37_TRUNK_LAYERS - 1)
600        } else {
601            match layers.split_once('-') {
602                Some((first, last)) => {
603                    let first = first
604                        .parse::<usize>()
605                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
606                    let last = last
607                        .parse::<usize>()
608                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
609                    if first > last {
610                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
611                    }
612                    if last - first + 1 > 128 {
613                        return Err(format!(
614                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
615                        ));
616                    }
617                    (first, last)
618                }
619                None => {
620                    let layer = layers
621                        .parse::<usize>()
622                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
623                    (layer, layer)
624                }
625            }
626        };
627        let devices = devices
628            .split(',')
629            .map(|device| {
630                device
631                    .parse::<usize>()
632                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
633            })
634            .collect::<Result<Vec<_>, _>>()?;
635        if !(2..=8).contains(&devices.len()) {
636            return Err(format!(
637                "{flag} requires 2..=8 devices, got {}",
638                devices.len()
639            ));
640        }
641        let mut unique = devices.clone();
642        unique.sort_unstable();
643        unique.dedup();
644        if unique.len() != devices.len() {
645            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
646        }
647        for layer in first..=last {
648            if specs
649                .iter()
650                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
651            {
652                return Err(format!("{flag} assigns layer {layer} more than once"));
653            }
654            specs.push(StepEpLayerSpec {
655                layer,
656                devices: devices.clone(),
657            });
658        }
659    }
660    Ok(specs)
661}
662
663pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
664    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
665}
666
667pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
668    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
669}
670
671pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
672    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
673}
674
675pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
676    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
677}
678
679#[derive(Clone, Copy)]
680pub struct E4m3BlockMatrix<'a> {
681    pub codes: &'a [u8],
682    pub scales: &'a [f32],
683    pub out_features: usize,
684    pub in_features: usize,
685}
686
687impl E4m3BlockMatrix<'_> {
688    fn validate(&self) -> Result<(), String> {
689        let code_count = self
690            .out_features
691            .checked_mul(self.in_features)
692            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
693        if self.codes.len() != code_count {
694            return Err(format!(
695                "E4M3 code count {} != {}x{} ({code_count})",
696                self.codes.len(),
697                self.out_features,
698                self.in_features,
699            ));
700        }
701        let scale_count =
702            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
703        if self.scales.len() != scale_count {
704            return Err(format!(
705                "E4M3 scale count {} != {scale_count} for {}x{}",
706                self.scales.len(),
707                self.out_features,
708                self.in_features,
709            ));
710        }
711        if !self
712            .scales
713            .iter()
714            .all(|scale| scale.is_finite() && *scale > 0.0)
715        {
716            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
717        }
718        Ok(())
719    }
720}
721
722#[derive(Clone, Copy)]
723pub struct E4m3ExpertBank<'a> {
724    pub codes: &'a [u8],
725    pub scales: &'a [f32],
726    pub expert_count: usize,
727    pub out_features: usize,
728    pub in_features: usize,
729}
730
731impl E4m3ExpertBank<'_> {
732    fn validate(&self) -> Result<(), String> {
733        if self.expert_count == 0 {
734            return Err("E4M3 expert bank is empty".to_string());
735        }
736        let code_stride = self
737            .out_features
738            .checked_mul(self.in_features)
739            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
740        let code_count = self
741            .expert_count
742            .checked_mul(code_stride)
743            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
744        if self.codes.len() != code_count {
745            return Err(format!(
746                "E4M3 expert code count {} != {}x{} ({code_count})",
747                self.codes.len(),
748                self.expert_count,
749                code_stride,
750            ));
751        }
752        let scale_stride =
753            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
754        let scale_count = self
755            .expert_count
756            .checked_mul(scale_stride)
757            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
758        if self.scales.len() != scale_count {
759            return Err(format!(
760                "E4M3 expert scale count {} != {}x{} ({scale_count})",
761                self.scales.len(),
762                self.expert_count,
763                scale_stride,
764            ));
765        }
766        if !self
767            .scales
768            .iter()
769            .all(|scale| scale.is_finite() && *scale > 0.0)
770        {
771            return Err(
772                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
773            );
774        }
775        Ok(())
776    }
777
778    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
779        if expert >= self.expert_count {
780            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
781        }
782        let code_stride = self.out_features * self.in_features;
783        let scale_stride =
784            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
785        Ok(E4m3BlockMatrix {
786            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
787            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
788            out_features: self.out_features,
789            in_features: self.in_features,
790        })
791    }
792}
793
794pub struct ColumnParallelResult {
795    pub gathered: Vec<f32>,
796    pub rank_outputs: Vec<Vec<f32>>,
797}
798
799pub struct RowParallelResult {
800    pub reduced: Vec<f32>,
801    pub rank_partials: Vec<Vec<f32>>,
802}
803
804#[derive(Clone, Copy)]
805pub struct Bf16Matrix<'a> {
806    pub bytes: &'a [u8],
807    pub out_features: usize,
808    pub in_features: usize,
809}
810
811impl Bf16Matrix<'_> {
812    pub fn validate(&self) -> Result<(), String> {
813        if self.out_features == 0 || self.in_features == 0 {
814            return Err("BF16 matrix dimensions must be nonzero".into());
815        }
816        let expected = self
817            .out_features
818            .checked_mul(self.in_features)
819            .and_then(|values| values.checked_mul(2))
820            .ok_or("BF16 matrix byte count overflow")?;
821        if self.bytes.len() != expected {
822            return Err(format!(
823                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
824                self.bytes.len(),
825                self.out_features,
826                self.in_features,
827            ));
828        }
829        Ok(())
830    }
831}
832
833struct ResidentE4m3Rank {
834    codes: CudaSlice<u8>,
835    scales: CudaSlice<f32>,
836    out_features: usize,
837    in_features: usize,
838}
839
840enum ResidentBf16Weight {
841    Bf16(CudaSlice<u8>),
842    F32(CudaSlice<f32>),
843}
844
845impl ResidentBf16Weight {
846    fn ordinal(&self) -> usize {
847        match self {
848            Self::Bf16(bytes) => bytes.ordinal(),
849            Self::F32(values) => values.ordinal(),
850        }
851    }
852}
853
854struct ResidentBf16Rank {
855    weight: ResidentBf16Weight,
856    out_features: usize,
857    in_features: usize,
858}
859
860pub struct ResidentColumnParallel {
861    ranks: Vec<ResidentE4m3Rank>,
862    out_features: usize,
863    in_features: usize,
864}
865
866pub struct ResidentRowParallel {
867    ranks: Vec<ResidentE4m3Rank>,
868    out_features: usize,
869    in_features: usize,
870}
871
872pub struct ResidentBf16ColumnParallel {
873    ranks: Vec<ResidentBf16Rank>,
874    out_features: usize,
875    in_features: usize,
876    canonical_chunk_rows: Option<usize>,
877}
878
879pub struct ResidentBf16RowParallel {
880    ranks: Vec<ResidentBf16Rank>,
881    out_features: usize,
882    in_features: usize,
883}
884
885pub struct ResidentStepBf16RowParallel {
886    ranks: Vec<Vec<ResidentBf16Rank>>,
887    out_features: usize,
888    in_features: usize,
889    canonical_chunk_cols: usize,
890}
891
892/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
893pub struct ResidentSigmoidTopKRouter {
894    weight: CudaSlice<f32>,
895    correction_bias: CudaSlice<f32>,
896    active: CudaSlice<u8>,
897    root_device: usize,
898    input_width: usize,
899    expert_count: usize,
900    experts_per_token: usize,
901    active_count: usize,
902    scaling_factor: f32,
903    route_norm: bool,
904}
905
906pub struct SigmoidTopKHostOutput {
907    pub logits: Vec<f32>,
908    pub selected: Vec<u32>,
909    pub weights: Vec<f32>,
910}
911
912/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
913pub struct ResidentReplicatedBf16SwiGlu {
914    gate: Vec<ResidentBf16Rank>,
915    up: Vec<ResidentBf16Rank>,
916    down: Vec<ResidentBf16Rank>,
917    input_width: usize,
918    intermediate_width: usize,
919}
920
921/// One token-major F32 batch replicated across a native-P2P rank group.
922///
923/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
924/// substrate between independently sharded operators; it carries no model or topology claim.
925pub struct ResidentReplicatedDeviceRows {
926    ranks: Vec<CudaSlice<f32>>,
927    tokens: usize,
928    width: usize,
929}
930
931impl ResidentReplicatedDeviceRows {
932    pub fn tokens(&self) -> usize {
933        self.tokens
934    }
935
936    pub fn width(&self) -> usize {
937        self.width
938    }
939
940    pub fn ranks(&self) -> usize {
941        self.ranks.len()
942    }
943}
944
945/// Canonical MoE output order: routed plus shared, then add the layer residual.
946pub fn moe_residual_host(
947    residual: &[f32],
948    routed: &[f32],
949    shared: &[f32],
950) -> Result<Vec<f32>, String> {
951    if residual.len() != routed.len() || residual.len() != shared.len() {
952        return Err(format!(
953            "MoE residual lengths residual={} routed={} shared={}",
954            residual.len(),
955            routed.len(),
956            shared.len()
957        ));
958    }
959    let ffn = routed
960        .iter()
961        .zip(shared)
962        .map(|(&routed, &shared)| routed + shared)
963        .collect::<Vec<_>>();
964    Ok(residual
965        .iter()
966        .zip(ffn)
967        .map(|(&residual, ffn)| residual + ffn)
968        .collect())
969}
970
971pub use memra_kv::{
972    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
973};
974
975/// Persistent TP2/TP4/TP8 routed-expert reference.
976///
977/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
978/// Activations and deterministic host-staged collectives remain per invocation. This is the
979/// correctness substrate for serving TP/EP, not product-throughput evidence.
980pub struct ResidentTpExpert {
981    gate: ResidentColumnParallel,
982    up: ResidentColumnParallel,
983    down: ResidentRowParallel,
984    input_width: usize,
985    expert_width: usize,
986}
987
988struct ResidentE4m3ExpertBankRank {
989    codes: CudaSlice<u8>,
990    scales: CudaSlice<f32>,
991    expert_range: Range<usize>,
992    out_features: usize,
993    in_features: usize,
994    code_stride: usize,
995    scale_stride: usize,
996    /// TP row banks are packed by native 128-wide K block so reduction can replay the
997    /// checkpoint's global block order exactly. Other banks remain row-major.
998    k_blocks: Option<usize>,
999}
1000
1001struct PackedE4m3ExpertBankRank {
1002    codes: Vec<u8>,
1003    scales: Vec<f32>,
1004    expert_range: Range<usize>,
1005    out_features: usize,
1006    in_features: usize,
1007    code_stride: usize,
1008    scale_stride: usize,
1009    k_blocks: Option<usize>,
1010}
1011
1012struct ResidentEpRank {
1013    gate: ResidentE4m3ExpertBankRank,
1014    up: ResidentE4m3ExpertBankRank,
1015    down: ResidentE4m3ExpertBankRank,
1016}
1017
1018/// Persistent expert-parallel reference.
1019///
1020/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1021/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1022/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1023/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1024pub struct ResidentExpertParallel {
1025    ranks: Vec<ResidentEpRank>,
1026    expert_count: usize,
1027    input_width: usize,
1028    expert_width: usize,
1029}
1030
1031/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1032///
1033/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1034/// outside this gate-only adapter.
1035pub struct StepGroupedFp8ProjectionOutput {
1036    pub gate: Vec<f32>,
1037    pub up: Vec<f32>,
1038    pub down: Vec<f32>,
1039}
1040
1041/// Prepared official Step grouped-FP8 projection gate.
1042///
1043/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1044/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1045pub struct PreparedStepGroupedFp8Gate {
1046    device: usize,
1047    gate: ResidentE4m3ExpertBankRank,
1048    up: ResidentE4m3ExpertBankRank,
1049    down: ResidentE4m3ExpertBankRank,
1050    input: CudaSlice<f32>,
1051    route_csr: DeviceExpertCsr,
1052    down_csr: DeviceExpertCsr,
1053    gate_workspace: Fp8GroupedWorkspace,
1054    up_workspace: Fp8GroupedWorkspace,
1055    down_workspace: Fp8GroupedWorkspace,
1056    activation: CudaSlice<f32>,
1057    activation_limit: Option<f32>,
1058    tokens: usize,
1059    pairs: usize,
1060}
1061
1062impl PreparedStepGroupedFp8Gate {
1063    pub fn tokens(&self) -> usize {
1064        self.tokens
1065    }
1066
1067    pub fn pairs(&self) -> usize {
1068        self.pairs
1069    }
1070}
1071
1072struct PreparedStepGroupedExpertOwner {
1073    rank: usize,
1074    global_pairs: Vec<usize>,
1075    route_csr: DeviceExpertCsr,
1076    down_csr: DeviceExpertCsr,
1077    gate_workspace: Fp8GroupedWorkspace,
1078    up_workspace: Fp8GroupedWorkspace,
1079    down_workspace: Fp8GroupedWorkspace,
1080    activation: CudaSlice<f32>,
1081}
1082
1083struct StepGroupedExpertOwnerSchedule {
1084    global_pairs: Vec<usize>,
1085    route_csr: ExpertCsr,
1086    down_csr: ExpertCsr,
1087}
1088
1089/// Prepared official Step expert-owner grouped-FP8 projection gate.
1090///
1091/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1092/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1093/// after every owner has completed its rank-local program.
1094pub struct PreparedStepGroupedExpertParallelGate {
1095    rank_inputs: Vec<CudaSlice<f32>>,
1096    owners: Vec<PreparedStepGroupedExpertOwner>,
1097    activation_limit: Option<f32>,
1098    tokens: usize,
1099    pairs: usize,
1100    max_tokens: usize,
1101    max_pairs: usize,
1102    input_width: usize,
1103    expert_width: usize,
1104    generation: u64,
1105    executed_generation: Option<u64>,
1106    ready: bool,
1107}
1108
1109impl PreparedStepGroupedExpertParallelGate {
1110    pub fn tokens(&self) -> usize {
1111        self.tokens
1112    }
1113
1114    pub fn pairs(&self) -> usize {
1115        self.pairs
1116    }
1117
1118    pub fn max_tokens(&self) -> usize {
1119        self.max_tokens
1120    }
1121
1122    pub fn input_width(&self) -> usize {
1123        self.input_width
1124    }
1125
1126    pub fn expert_width(&self) -> usize {
1127        self.expert_width
1128    }
1129
1130    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1131        validate_step_expert_activation_limit(limit)?;
1132        self.activation_limit = limit;
1133        self.executed_generation = None;
1134        Ok(())
1135    }
1136
1137    pub fn active_owners(&self) -> usize {
1138        self.owners
1139            .iter()
1140            .filter(|owner| !owner.global_pairs.is_empty())
1141            .count()
1142    }
1143
1144    pub fn owner_pair_counts(&self) -> Vec<usize> {
1145        self.owners
1146            .iter()
1147            .map(|owner| owner.global_pairs.len())
1148            .collect()
1149    }
1150
1151    pub fn generation(&self) -> u64 {
1152        self.generation
1153    }
1154}
1155
1156struct PreparedPeerWeightedRouteOwner {
1157    token_rows: CudaSlice<i32>,
1158    slots: CudaSlice<i32>,
1159    weights: CudaSlice<f32>,
1160    active_pairs: usize,
1161}
1162
1163/// Persistent root-side weighted combine for peer-owned canonical route rows.
1164///
1165/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1166/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1167/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1168pub struct PreparedPeerWeightedRouteCombine {
1169    root_device: usize,
1170    owners: Vec<PreparedPeerWeightedRouteOwner>,
1171    peer_staging: CudaSlice<f32>,
1172    slots: CudaSlice<f32>,
1173    weights: CudaSlice<f32>,
1174    output: CudaSlice<f32>,
1175    peer_devices: Vec<usize>,
1176    peer_outputs: Vec<CudaSlice<f32>>,
1177    width: usize,
1178    experts_per_token: usize,
1179    max_tokens: usize,
1180    max_pairs: usize,
1181    tokens: usize,
1182    pairs: usize,
1183    projection_generation: u64,
1184    output_generation: Option<u64>,
1185    broadcast_generation: Option<u64>,
1186    ready: bool,
1187}
1188
1189impl PreparedPeerWeightedRouteCombine {
1190    pub fn tokens(&self) -> usize {
1191        self.tokens
1192    }
1193
1194    pub fn pairs(&self) -> usize {
1195        self.pairs
1196    }
1197
1198    pub fn owner_pair_counts(&self) -> Vec<usize> {
1199        self.owners.iter().map(|owner| owner.active_pairs).collect()
1200    }
1201
1202    pub fn distributed_ranks(&self) -> usize {
1203        1 + self.peer_outputs.len()
1204    }
1205}
1206
1207struct ResidentTpExpertBank {
1208    gate: Vec<ResidentE4m3ExpertBankRank>,
1209    up: Vec<ResidentE4m3ExpertBankRank>,
1210    down: Vec<ResidentE4m3ExpertBankRank>,
1211    expert_count: usize,
1212    input_width: usize,
1213    expert_width: usize,
1214}
1215
1216/// Persistent tensor-parallel expert bank.
1217///
1218/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1219/// input-column shard of every down projection. Activations cross deterministic host-staged
1220/// collectives on hosts where native peer copies are unavailable or corrupt.
1221pub struct ResidentTensorParallel {
1222    bank: ResidentTpExpertBank,
1223}
1224
1225/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1226///
1227/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1228/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1229/// repeated performance evidence qualify it.
1230pub struct TpE4m3HostBounce {
1231    devices: Vec<usize>,
1232    ranks: Vec<Engine>,
1233    native_p2p: bool,
1234    ep_device_arithmetic: bool,
1235    bulk_p2p: bool,
1236    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1237    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1238    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1239}
1240
1241/// Persistent workspace of the v2 rank-local decode-attention driver.
1242///
1243/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1244/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1245/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1246/// before its consumers run in the same call; nothing carries state between tokens.
1247/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1248/// fused kernels read (F32 mirror or raw checkpoint bf16).
1249pub enum StepTpGateShards<'a> {
1250    F32(&'a [crate::CudaSlice<f32>]),
1251    Bf16(&'a [crate::CudaSlice<u8>]),
1252}
1253
1254pub struct StepTpDecodeV2Ws {
1255    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1256    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1257    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1258    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1259    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1260    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1261    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1262    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1263    pub(crate) tcol_cap: usize,
1264    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1265    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1266    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1267    tcol_gated: Vec<CudaSlice<f32>>,
1268    tcol_opart: Vec<CudaSlice<f32>>,
1269    tcol_opeer: Option<CudaSlice<f32>>,
1270    tcol_omix: Option<CudaSlice<f32>>,
1271    tcol_ocap: usize,
1272    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1273    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1274    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1275    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1276    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1277    pub(crate) q: Vec<CudaSlice<f32>>,
1278    pub(crate) k: Vec<CudaSlice<f32>>,
1279    pub(crate) pos: Vec<CudaSlice<i32>>,
1280    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1281    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1282    pub(crate) gate: Vec<CudaSlice<f32>>,
1283    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1284    pub(crate) gated: Vec<CudaSlice<f32>>,
1285    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1286    o_partials: Vec<Vec<CudaSlice<f32>>>,
1287    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1288    ev_rank: Vec<CudaEvent>,
1289    // root-context buffers
1290    peer_partial: CudaSlice<f32>,
1291    reduce_a: CudaSlice<f32>,
1292    reduce_b: CudaSlice<f32>,
1293    /// Never written; the canonical zero start of the v1 add chain.
1294    zeros: CudaSlice<f32>,
1295    pub(crate) k_shadow: CudaSlice<f32>,
1296    pub(crate) v_shadow: CudaSlice<f32>,
1297    ev_refresh: CudaEvent,
1298    ev_oproj: CudaEvent,
1299    // model-engine (e) context
1300    gate_e: CudaSlice<f32>,
1301    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1302    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1303    pub(crate) h_stage: Option<CudaSlice<f32>>,
1304    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1305    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1306    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1307    /// captured/raw address it uses must be layer-invariant).
1308    attn_in: Vec<CudaSlice<f32>>,
1309    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1310    raw_h_stage: u64,
1311    raw_pos_stage: u64,
1312    raw_attn_in: Vec<u64>,
1313    raw_pos: Vec<u64>,
1314    raw_o_partial1: u64,
1315    raw_peer_partial: u64,
1316    raw_k1: u64,
1317    raw_v1: u64,
1318    raw_k_shadow: u64,
1319    raw_v_shadow: u64,
1320    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1321    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1322    /// children read same-context memory (cross-context kernel args are capture-illegal).
1323    raw_mixed_stage_e: u64,
1324    raw_reduce_a: u64,
1325    raw_shadow_stage_e: (u64, u64),
1326    ev_entry: CudaEvent,
1327    e_device: usize,
1328    // geometry pins
1329    local_q_dim: usize,
1330    local_kv_dim: usize,
1331    heads: usize,
1332    pub(crate) o_out: usize,
1333    o_block_cols: usize,
1334    blocks_per_rank: usize,
1335}
1336
1337impl TpE4m3HostBounce {
1338    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1339        Self::new_inner(devices, false, false, false, false)
1340    }
1341
1342    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1343        Self::new_inner(devices, false, true, false, false)
1344    }
1345
1346    pub fn new_native_p2p_device_arithmetic(
1347        devices: &[usize],
1348    ) -> Result<Self, Box<dyn std::error::Error>> {
1349        Self::new_inner(devices, false, true, true, false)
1350    }
1351
1352    pub(crate) fn new_configured(
1353        devices: &[usize],
1354        native_p2p: bool,
1355        ep_device_arithmetic: bool,
1356        bulk_p2p: bool,
1357    ) -> Result<Self, Box<dyn std::error::Error>> {
1358        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1359    }
1360
1361    /// Single-rank execution of the canonical checkpoint-block TP program.
1362    ///
1363    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1364    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1365    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1366        Self::new_inner(&[device], true, false, false, false)
1367    }
1368
1369    fn new_inner(
1370        devices: &[usize],
1371        allow_single_rank: bool,
1372        native_p2p: bool,
1373        ep_device_arithmetic: bool,
1374        bulk_p2p: bool,
1375    ) -> Result<Self, Box<dyn std::error::Error>> {
1376        if ep_device_arithmetic && !native_p2p {
1377            return Err("device-resident EP arithmetic requires native P2P".into());
1378        }
1379        if bulk_p2p && !native_p2p {
1380            return Err("bulk TP transport requires native P2P".into());
1381        }
1382        let minimum = if allow_single_rank { 1 } else { 2 };
1383        if !(minimum..=8).contains(&devices.len()) {
1384            return Err(format!(
1385                "TP reference requires {minimum}..=8 devices, got {}",
1386                devices.len()
1387            )
1388            .into());
1389        }
1390        let mut unique = devices.to_vec();
1391        unique.sort_unstable();
1392        unique.dedup();
1393        if unique.len() != devices.len() {
1394            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1395        }
1396        let ranks = devices
1397            .iter()
1398            .map(|&device| Engine::new(device))
1399            .collect::<Result<Vec<_>, _>>()?;
1400        if native_p2p {
1401            configure_native_p2p(&ranks, devices)?;
1402        }
1403        if allow_single_rank {
1404            eprintln!(
1405                "[tp] canonical oracle transport=local device={} performance_claim=false",
1406                devices[0]
1407            );
1408        } else if native_p2p {
1409            if ep_device_arithmetic {
1410                eprintln!(
1411                    "[tp] correctness transport=native-p2p devices={devices:?} \
1412                     native_p2p=true activation=device-host-exact \
1413                     accumulation=device-host-exact output=root-readback \
1414                     bulk_p2p={bulk_p2p} performance_claim=false"
1415                );
1416            } else {
1417                eprintln!(
1418                    "[tp] correctness transport=native-p2p devices={devices:?} \
1419                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1420                     performance_claim=false"
1421                );
1422            }
1423        } else {
1424            eprintln!(
1425                "[tp] correctness transport=host-bounce devices={devices:?} \
1426                 native_p2p=false performance_claim=false"
1427            );
1428        }
1429        Ok(Self {
1430            devices: devices.to_vec(),
1431            ranks,
1432            native_p2p,
1433            ep_device_arithmetic,
1434            bulk_p2p,
1435            decode_v2: std::sync::Mutex::new(Vec::new()),
1436        })
1437    }
1438
1439    pub fn devices(&self) -> &[usize] {
1440        &self.devices
1441    }
1442
1443    pub fn native_p2p(&self) -> bool {
1444        self.native_p2p
1445    }
1446
1447    pub fn bulk_p2p(&self) -> bool {
1448        self.bulk_p2p
1449    }
1450
1451    pub fn expert_activation_label(&self) -> &'static str {
1452        if self.ep_device_arithmetic {
1453            "device-host-exact"
1454        } else {
1455            "host-canonical"
1456        }
1457    }
1458
1459    pub fn expert_accumulation_label(&self) -> &'static str {
1460        self.expert_activation_label()
1461    }
1462
1463    pub fn expert_output_label(&self) -> &'static str {
1464        if self.ep_device_arithmetic {
1465            "root-readback"
1466        } else {
1467            "host-accumulated"
1468        }
1469    }
1470
1471    pub fn transport_label(&self) -> &'static str {
1472        if self.devices.len() == 1 {
1473            "local"
1474        } else if self.native_p2p {
1475            "native-p2p"
1476        } else {
1477            "host-bounce"
1478        }
1479    }
1480
1481    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1482        self.ranks
1483            .iter()
1484            .map(|rank| rank.ctx().name().map_err(Into::into))
1485            .collect()
1486    }
1487
1488    /// Correctness-gate access to the engine that owns one TP rank.
1489    ///
1490    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1491    /// focused gates can prove that the rank-local projection outputs remain device-resident
1492    /// through the next ownership boundary before that boundary is wired into serving.
1493    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1494        self.ranks.get(rank)
1495    }
1496
1497    pub fn allocate_tp_kv_cache(
1498        &self,
1499        kv_dim_k: usize,
1500        kv_dim_v: usize,
1501        capacity: usize,
1502    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1503        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1504    }
1505
1506    pub fn allocate_tp_swa_kv_cache(
1507        &self,
1508        kv_dim_k: usize,
1509        kv_dim_v: usize,
1510        capacity: usize,
1511        window: usize,
1512    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1513        if window == 0 {
1514            return Err("TP SWA KV window must be nonzero".into());
1515        }
1516        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1517    }
1518
1519    fn allocate_tp_kv_cache_inner(
1520        &self,
1521        kv_dim_k: usize,
1522        kv_dim_v: usize,
1523        capacity: usize,
1524        window: Option<usize>,
1525    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1526        if capacity == 0 || capacity > i32::MAX as usize {
1527            return Err(
1528                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1529            );
1530        }
1531        let tp = self.ranks.len();
1532        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1533        let physical_rows = window
1534            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1535            .unwrap_or(capacity);
1536        let k_plane_bytes = physical_rows
1537            .checked_mul(shape.k_token_bytes)
1538            .and_then(|bytes| bytes.checked_add(8))
1539            .ok_or("TP KV K plane-byte overflow")?;
1540        let v_plane_bytes = physical_rows
1541            .checked_mul(shape.v_token_bytes)
1542            .and_then(|bytes| bytes.checked_add(8))
1543            .ok_or("TP KV V plane-byte overflow")?;
1544        let mut ranks = Vec::with_capacity(tp);
1545        for engine in &self.ranks {
1546            let _main = engine.gpu.enter_main()?;
1547            ranks.push(ResidentTpKvCacheRank::new(
1548                engine.alloc_u8(k_plane_bytes)?,
1549                engine.alloc_u8(v_plane_bytes)?,
1550                engine.htod_i32(&[0])?,
1551            ));
1552        }
1553        Ok(match window {
1554            Some(window) => ResidentTpKvCache::new_swa(
1555                ranks,
1556                shape.kv_dim_k,
1557                shape.kv_dim_v,
1558                shape.k_token_bytes,
1559                shape.v_token_bytes,
1560                capacity,
1561                window,
1562            ),
1563            None => ResidentTpKvCache::new(
1564                ranks,
1565                shape.kv_dim_k,
1566                shape.kv_dim_v,
1567                shape.k_token_bytes,
1568                shape.v_token_bytes,
1569                capacity,
1570            ),
1571        })
1572    }
1573
1574    pub fn grow_tp_kv_cache(
1575        &self,
1576        source: &ResidentTpKvCache,
1577        target_capacity: usize,
1578        rows: usize,
1579    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1580        self.validate_tp_kv_cache(source)?;
1581        let plan = source.prepare_grow(target_capacity, rows)?;
1582        let ranks = self.ranks.len();
1583        let global_k = source
1584            .kv_dim_k()
1585            .checked_mul(ranks)
1586            .ok_or("TP KV grow global K dimension overflow")?;
1587        let global_v = source
1588            .kv_dim_v()
1589            .checked_mul(ranks)
1590            .ok_or("TP KV grow global V dimension overflow")?;
1591        let mut target = match source.ring_window() {
1592            Some(window) => {
1593                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1594            }
1595            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1596        };
1597        self.validate_tp_kv_cache(&target)?;
1598
1599        for (rank, engine) in self.ranks.iter().enumerate() {
1600            let _main = engine.gpu.enter_main()?;
1601            let src = source
1602                .rank(rank)
1603                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1604            let dst = target
1605                .rank_mut(rank)
1606                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1607            if plan.k_bytes() > 0 {
1608                engine.copy_u8_range_into(
1609                    dst.k_mut(),
1610                    0,
1611                    src.k(),
1612                    plan.source_row() * source.k_tok_bytes(),
1613                    plan.k_bytes(),
1614                )?;
1615            }
1616            if plan.v_bytes() > 0 {
1617                engine.copy_u8_range_into(
1618                    dst.v_mut(),
1619                    0,
1620                    src.v(),
1621                    plan.source_row() * source.v_tok_bytes(),
1622                    plan.v_bytes(),
1623                )?;
1624            }
1625        }
1626        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1627
1628        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1629        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1630        for engine in &self.ranks {
1631            let _main = engine.gpu.enter_main()?;
1632            engine.stream().synchronize()?;
1633        }
1634        let physical_copy_rows = plan.copy_rows();
1635        target.publish_grow(plan)?;
1636        eprintln!(
1637            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1638             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1639             rank_streams_synchronized=true generation_preserved=true",
1640            rows,
1641            source.capacity(),
1642            target_capacity,
1643            ranks,
1644            physical_copy_rows,
1645            source.ring_window(),
1646        );
1647        Ok(target)
1648    }
1649
1650    pub fn hydrate_tp_kv_cache(
1651        &self,
1652        cache: &mut ResidentTpKvCache,
1653        rows: usize,
1654        k_rows: &[u8],
1655        v_rows: &[u8],
1656    ) -> Result<(), Box<dyn std::error::Error>> {
1657        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1658    }
1659
1660    pub fn hydrate_tp_kv_cache_from(
1661        &self,
1662        cache: &mut ResidentTpKvCache,
1663        logical_len: usize,
1664        resident_start: usize,
1665        k_rows: &[u8],
1666        v_rows: &[u8],
1667    ) -> Result<(), Box<dyn std::error::Error>> {
1668        self.validate_tp_kv_cache(cache)?;
1669        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1670            return Err(format!(
1671                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1672                cache.committed_len(),
1673                cache.staged_len()
1674            )
1675            .into());
1676        }
1677        if resident_start > logical_len || logical_len > cache.capacity() {
1678            return Err(format!(
1679                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1680                cache.capacity(),
1681            )
1682            .into());
1683        }
1684        let rows = logical_len - resident_start;
1685        if rows > cache.physical_capacity() {
1686            return Err(format!(
1687                "TP KV hydration rows {rows} exceed physical capacity {}",
1688                cache.physical_capacity()
1689            )
1690            .into());
1691        }
1692        for rank in 0..self.ranks.len() {
1693            let k_rank =
1694                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1695            let v_rank =
1696                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1697            let engine = &self.ranks[rank];
1698            let _main = engine.gpu.enter_main()?;
1699            let rank_cache = cache
1700                .rank_mut(rank)
1701                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1702            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1703            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1704        }
1705        cache.publish_hydration(logical_len, resident_start)?;
1706        Ok(())
1707    }
1708
1709    pub fn append_tp_kv_transaction(
1710        &self,
1711        cache: &mut ResidentTpKvCache,
1712        transaction: TpKvTransaction,
1713        k_shards: &[CudaSlice<f32>],
1714        v_shards: &[CudaSlice<f32>],
1715        rows: usize,
1716    ) -> Result<(), Box<dyn std::error::Error>> {
1717        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1718    }
1719
1720    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1721    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1722    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1723    /// which land the same value the in-stream inc produced).
1724    #[allow(clippy::too_many_arguments)]
1725    pub fn append_tp_kv_transaction_inner(
1726        &self,
1727        cache: &mut ResidentTpKvCache,
1728        transaction: TpKvTransaction,
1729        k_shards: &[CudaSlice<f32>],
1730        v_shards: &[CudaSlice<f32>],
1731        rows: usize,
1732        external_rank_appends: bool,
1733    ) -> Result<(), Box<dyn std::error::Error>> {
1734        self.validate_tp_kv_cache(cache)?;
1735        let plan = cache.prepare_append(transaction, rows)?;
1736        let target = plan.target();
1737        let expected_k = rows
1738            .checked_mul(cache.kv_dim_k())
1739            .ok_or("TP KV K append size overflow")?;
1740        let expected_v = rows
1741            .checked_mul(cache.kv_dim_v())
1742            .ok_or("TP KV V append size overflow")?;
1743        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1744        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1745        if !external_rank_appends
1746            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1747        {
1748            return Err(format!(
1749                "TP KV append shard counts k={} v={} != ranks {}",
1750                k_shards.len(),
1751                v_shards.len(),
1752                self.ranks.len()
1753            )
1754            .into());
1755        }
1756        let kv_dim_k = cache.kv_dim_k();
1757        let kv_dim_v = cache.kv_dim_v();
1758        let k_tok_bytes = cache.k_tok_bytes();
1759        let v_tok_bytes = cache.v_tok_bytes();
1760        if let Some(KvRingAppend::Rebase {
1761            src_row,
1762            keep_rows,
1763            new_base,
1764            ..
1765        }) = plan.ring_append()
1766        {
1767            for rank in 0..self.ranks.len() {
1768                let engine = &self.ranks[rank];
1769                let _main = engine.gpu.enter_main()?;
1770                let rank_cache = cache
1771                    .rank_mut(rank)
1772                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1773                if keep_rows > 0 {
1774                    let k_len = keep_rows
1775                        .checked_mul(k_tok_bytes)
1776                        .ok_or("TP KV K rebase-byte overflow")?;
1777                    let v_len = keep_rows
1778                        .checked_mul(v_tok_bytes)
1779                        .ok_or("TP KV V rebase-byte overflow")?;
1780                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
1781                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
1782                    engine.copy_u8_range_into(
1783                        &mut k_tmp,
1784                        0,
1785                        rank_cache.k(),
1786                        src_row * k_tok_bytes,
1787                        k_len,
1788                    )?;
1789                    engine.copy_u8_range_into(
1790                        &mut v_tmp,
1791                        0,
1792                        rank_cache.v(),
1793                        src_row * v_tok_bytes,
1794                        v_len,
1795                    )?;
1796                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
1797                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
1798                }
1799                // dcw base mirror (graph increment A): physical row 0 now holds logical
1800                // row `new_base`; armed device mirrors track it (rebases are rare host
1801                // events, so a host set here is the whole maintenance cost).
1802                if rank_cache.base_d().is_some() {
1803                    let value = new_base as i32;
1804                    let rank_cache = cache
1805                        .rank_mut(rank)
1806                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1807                    if let Some(base_d) = rank_cache.base_d_mut() {
1808                        engine.set_i32_one(base_d, value)?;
1809                    }
1810                }
1811            }
1812        }
1813        cache.publish_append_rebase(plan)?;
1814        let write_row = plan.write_row();
1815        for rank in 0..self.ranks.len() {
1816            if external_rank_appends {
1817                break;
1818            }
1819            let engine = &self.ranks[rank];
1820            let _main = engine.gpu.enter_main()?;
1821            if k_shards[rank].len() != expected_k
1822                || v_shards[rank].len() != expected_v
1823                || k_shards[rank].ordinal() != engine.ctx().ordinal()
1824                || v_shards[rank].ordinal() != engine.ctx().ordinal()
1825            {
1826                return Err(format!(
1827                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
1828                     != expected {expected_k}/{expected_v} on device {}",
1829                    k_shards[rank].len(),
1830                    k_shards[rank].ordinal(),
1831                    v_shards[rank].len(),
1832                    v_shards[rank].ordinal(),
1833                    engine.ctx().ordinal(),
1834                )
1835                .into());
1836            }
1837            let rank_cache = cache
1838                .rank_mut(rank)
1839                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1840            let (rank_k, rank_v) = rank_cache.planes_mut();
1841            engine.append_kv_quantized_rows(
1842                &k_shards[rank],
1843                &v_shards[rank],
1844                rank_k,
1845                rank_v,
1846                write_row,
1847                rows,
1848                kv_dim_k,
1849                kv_dim_v,
1850                k_tok_bytes,
1851                v_tok_bytes,
1852                Engine::kv_fp8_on(),
1853            )?;
1854        }
1855        if !external_rank_appends {
1856            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
1857            // here would race the merged per-rank append (it reads len_d for its write row).
1858            self.set_tp_kv_len_mirrors(cache, target)?;
1859        }
1860        cache.publish_append_plan(plan)?;
1861        Ok(())
1862    }
1863
1864    pub fn commit_tp_kv_transaction(
1865        &self,
1866        cache: &mut ResidentTpKvCache,
1867        transaction: TpKvTransaction,
1868        accepted_rows: usize,
1869    ) -> Result<(), Box<dyn std::error::Error>> {
1870        self.validate_tp_kv_cache(cache)?;
1871        let target = cache.commit_target(transaction, accepted_rows)?;
1872        self.set_tp_kv_len_mirrors(cache, target)?;
1873        cache.publish_finalize(transaction, target)?;
1874        Ok(())
1875    }
1876
1877    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
1878    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
1879    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
1880    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
1881    /// counter backward mid-token.
1882    pub fn commit_tp_kv_transaction_external(
1883        &self,
1884        cache: &mut ResidentTpKvCache,
1885        transaction: TpKvTransaction,
1886        accepted_rows: usize,
1887    ) -> Result<(), Box<dyn std::error::Error>> {
1888        self.validate_tp_kv_cache(cache)?;
1889        let target = cache.commit_target(transaction, accepted_rows)?;
1890        cache.publish_finalize(transaction, target)?;
1891        Ok(())
1892    }
1893
1894    pub fn rollback_tp_kv_transaction(
1895        &self,
1896        cache: &mut ResidentTpKvCache,
1897        transaction: TpKvTransaction,
1898    ) -> Result<(), Box<dyn std::error::Error>> {
1899        self.validate_tp_kv_cache(cache)?;
1900        cache.validate_transaction(transaction)?;
1901        let target = transaction.base_len();
1902        self.set_tp_kv_len_mirrors(cache, target)?;
1903        cache.publish_finalize(transaction, target)?;
1904        Ok(())
1905    }
1906
1907    pub fn tp_kv_device_lengths(
1908        &self,
1909        cache: &ResidentTpKvCache,
1910    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
1911        self.validate_tp_kv_cache(cache)?;
1912        let mut lengths = Vec::with_capacity(self.ranks.len());
1913        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
1914            let _main = engine.gpu.enter_main()?;
1915            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
1916        }
1917        Ok(lengths)
1918    }
1919
1920    fn set_tp_kv_len_mirrors(
1921        &self,
1922        cache: &mut ResidentTpKvCache,
1923        len: usize,
1924    ) -> Result<(), Box<dyn std::error::Error>> {
1925        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
1926        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
1927            let _main = engine.gpu.enter_main()?;
1928            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
1929        }
1930        Ok(())
1931    }
1932
1933    fn validate_tp_kv_cache(
1934        &self,
1935        cache: &ResidentTpKvCache,
1936    ) -> Result<(), Box<dyn std::error::Error>> {
1937        if cache.ranks_len() != self.ranks.len() {
1938            return Err(format!(
1939                "TP KV cache ranks {} != runtime ranks {}",
1940                cache.ranks_len(),
1941                self.ranks.len()
1942            )
1943            .into());
1944        }
1945        let expected_k = cache
1946            .physical_capacity()
1947            .checked_mul(cache.k_tok_bytes())
1948            .and_then(|bytes| bytes.checked_add(8))
1949            .ok_or("TP KV K plane validation overflow")?;
1950        let expected_v = cache
1951            .physical_capacity()
1952            .checked_mul(cache.v_tok_bytes())
1953            .and_then(|bytes| bytes.checked_add(8))
1954            .ok_or("TP KV V plane validation overflow")?;
1955        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
1956            let device = engine.ctx().ordinal();
1957            if rank_cache.k().len() != expected_k
1958                || rank_cache.v().len() != expected_v
1959                || rank_cache.len_d().len() != 1
1960                || rank_cache.k().ordinal() != device
1961                || rank_cache.v().ordinal() != device
1962                || rank_cache.len_d().ordinal() != device
1963            {
1964                return Err(format!(
1965                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
1966                )
1967                .into());
1968            }
1969        }
1970        Ok(())
1971    }
1972
1973    pub fn full(
1974        &self,
1975        matrix: E4m3BlockMatrix<'_>,
1976        activations: &[f32],
1977        tokens: usize,
1978    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
1979        matrix.validate()?;
1980        validate_activations(activations, tokens, matrix.in_features)?;
1981        run_rank(&self.ranks[0], matrix, activations, tokens)
1982    }
1983
1984    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
1985    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
1986    /// output is host-gathered in rank order.
1987    pub fn column_parallel(
1988        &self,
1989        matrix: E4m3BlockMatrix<'_>,
1990        activations: &[f32],
1991        tokens: usize,
1992    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
1993        matrix.validate()?;
1994        validate_activations(activations, tokens, matrix.in_features)?;
1995        let tp = self.ranks.len();
1996        if matrix.out_features % tp != 0 {
1997            return Err(format!(
1998                "column-parallel out_features {} is not divisible by TP={tp}",
1999                matrix.out_features
2000            )
2001            .into());
2002        }
2003        let local_out = matrix.out_features / tp;
2004        if local_out % FP8_BLOCK != 0 {
2005            return Err(format!(
2006                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2007                 E4M3 scale block"
2008            )
2009            .into());
2010        }
2011
2012        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2013        let mut rank_outputs = Vec::with_capacity(tp);
2014        for (rank_index, rank) in self.ranks.iter().enumerate() {
2015            let shard = column_shard(matrix, tp, rank_index)?;
2016            let output = run_rank(rank, shard, activations, tokens)?;
2017            let row_start = rank_index * local_out;
2018            for token in 0..tokens {
2019                gathered[token * matrix.out_features + row_start
2020                    ..token * matrix.out_features + row_start + local_out]
2021                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2022            }
2023            rank_outputs.push(output);
2024        }
2025        Ok(ColumnParallelResult {
2026            gathered,
2027            rank_outputs,
2028        })
2029    }
2030
2031    pub fn upload_column_parallel(
2032        &self,
2033        matrix: E4m3BlockMatrix<'_>,
2034    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2035        matrix.validate()?;
2036        let tp = self.ranks.len();
2037        validate_column_shape(matrix, tp)?;
2038        let mut ranks = Vec::with_capacity(tp);
2039        for (rank_index, engine) in self.ranks.iter().enumerate() {
2040            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2041        }
2042        Ok(ResidentColumnParallel {
2043            ranks,
2044            out_features: matrix.out_features,
2045            in_features: matrix.in_features,
2046        })
2047    }
2048
2049    pub fn column_parallel_resident(
2050        &self,
2051        matrix: &ResidentColumnParallel,
2052        activations: &[f32],
2053        tokens: usize,
2054    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2055        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2056        validate_activations(activations, tokens, matrix.in_features)?;
2057        let local_out = matrix.out_features / self.ranks.len();
2058        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2059        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2060        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2061            let output = run_resident_rank(engine, shard, activations, tokens)?;
2062            let row_start = rank_index * local_out;
2063            for token in 0..tokens {
2064                gathered[token * matrix.out_features + row_start
2065                    ..token * matrix.out_features + row_start + local_out]
2066                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2067            }
2068            rank_outputs.push(output);
2069        }
2070        Ok(ColumnParallelResult {
2071            gathered,
2072            rank_outputs,
2073        })
2074    }
2075
2076    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2077    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2078    /// rank order.
2079    pub fn row_parallel(
2080        &self,
2081        matrix: E4m3BlockMatrix<'_>,
2082        activations: &[f32],
2083        tokens: usize,
2084    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2085        matrix.validate()?;
2086        validate_activations(activations, tokens, matrix.in_features)?;
2087        let tp = self.ranks.len();
2088        if matrix.in_features % tp != 0 {
2089            return Err(format!(
2090                "row-parallel in_features {} is not divisible by TP={tp}",
2091                matrix.in_features
2092            )
2093            .into());
2094        }
2095        let local_in = matrix.in_features / tp;
2096        if local_in % FP8_BLOCK != 0 {
2097            return Err(format!(
2098                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2099                 E4M3 scale block"
2100            )
2101            .into());
2102        }
2103
2104        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2105        let mut rank_partials = Vec::with_capacity(tp);
2106        for (rank_index, rank) in self.ranks.iter().enumerate() {
2107            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2108            let local_activations =
2109                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2110            let shard = E4m3BlockMatrix {
2111                codes: &codes,
2112                scales: &scales,
2113                out_features: matrix.out_features,
2114                in_features: local_in,
2115            };
2116            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2117            for (sum, value) in reduced.iter_mut().zip(&partial) {
2118                *sum += *value;
2119            }
2120            rank_partials.push(partial);
2121        }
2122        Ok(RowParallelResult {
2123            reduced,
2124            rank_partials,
2125        })
2126    }
2127
2128    pub fn upload_row_parallel(
2129        &self,
2130        matrix: E4m3BlockMatrix<'_>,
2131    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2132        matrix.validate()?;
2133        let tp = self.ranks.len();
2134        validate_row_shape(matrix, tp)?;
2135        let local_in = matrix.in_features / tp;
2136        let mut ranks = Vec::with_capacity(tp);
2137        for (rank_index, engine) in self.ranks.iter().enumerate() {
2138            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2139            ranks.push(upload_rank(
2140                engine,
2141                E4m3BlockMatrix {
2142                    codes: &codes,
2143                    scales: &scales,
2144                    out_features: matrix.out_features,
2145                    in_features: local_in,
2146                },
2147            )?);
2148        }
2149        Ok(ResidentRowParallel {
2150            ranks,
2151            out_features: matrix.out_features,
2152            in_features: matrix.in_features,
2153        })
2154    }
2155
2156    pub fn row_parallel_resident(
2157        &self,
2158        matrix: &ResidentRowParallel,
2159        activations: &[f32],
2160        tokens: usize,
2161    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2162        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2163        validate_activations(activations, tokens, matrix.in_features)?;
2164        let tp = self.ranks.len();
2165        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2166        let mut rank_partials = Vec::with_capacity(tp);
2167        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2168            let local_activations =
2169                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2170            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2171            for (sum, value) in reduced.iter_mut().zip(&partial) {
2172                *sum += *value;
2173            }
2174            rank_partials.push(partial);
2175        }
2176        Ok(RowParallelResult {
2177            reduced,
2178            rank_partials,
2179        })
2180    }
2181
2182    pub fn upload_bf16_column_parallel(
2183        &self,
2184        matrix: Bf16Matrix<'_>,
2185    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2186        self.upload_bf16_column_parallel_inner(matrix, None, false)
2187    }
2188
2189    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2190    pub fn upload_step_bf16_column_parallel(
2191        &self,
2192        matrix: Bf16Matrix<'_>,
2193    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2194        self.upload_step_bf16_column_parallel_inner(matrix, false)
2195    }
2196
2197    /// Load-time exact F32 expansion of a Step BF16 shard.
2198    ///
2199    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2200    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2201    pub fn upload_step_bf16_column_parallel_f32_mirror(
2202        &self,
2203        matrix: Bf16Matrix<'_>,
2204    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2205        self.upload_step_bf16_column_parallel_inner(matrix, true)
2206    }
2207
2208    fn upload_step_bf16_column_parallel_inner(
2209        &self,
2210        matrix: Bf16Matrix<'_>,
2211        f32_mirror: bool,
2212    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2213        let canonical_chunk_rows =
2214            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2215        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2216    }
2217
2218    fn upload_bf16_column_parallel_inner(
2219        &self,
2220        matrix: Bf16Matrix<'_>,
2221        canonical_chunk_rows: Option<usize>,
2222        f32_mirror: bool,
2223    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2224        matrix.validate()?;
2225        let tp = self.ranks.len();
2226        if matrix.out_features % tp != 0 {
2227            return Err(format!(
2228                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2229                matrix.out_features
2230            )
2231            .into());
2232        }
2233        let mut ranks = Vec::with_capacity(tp);
2234        for (rank, engine) in self.ranks.iter().enumerate() {
2235            ranks.push(upload_bf16_rank(
2236                engine,
2237                bf16_column_shard(matrix, tp, rank)?,
2238                f32_mirror,
2239            )?);
2240        }
2241        Ok(ResidentBf16ColumnParallel {
2242            ranks,
2243            out_features: matrix.out_features,
2244            in_features: matrix.in_features,
2245            canonical_chunk_rows,
2246        })
2247    }
2248
2249    pub fn bf16_column_parallel_resident(
2250        &self,
2251        matrix: &ResidentBf16ColumnParallel,
2252        activations: &[f32],
2253        tokens: usize,
2254    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2255        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2256        validate_activations(activations, tokens, matrix.in_features)?;
2257        let local_out = matrix.out_features / self.ranks.len();
2258        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2259        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2260        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2261            let output = run_resident_bf16_rank(
2262                engine,
2263                shard,
2264                activations,
2265                tokens,
2266                matrix.canonical_chunk_rows,
2267            )?;
2268            for token in 0..tokens {
2269                let src = &output[token * local_out..(token + 1) * local_out];
2270                let dst_start = token * matrix.out_features + rank * local_out;
2271                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2272            }
2273            rank_outputs.push(output);
2274        }
2275        Ok(ColumnParallelResult {
2276            gathered,
2277            rank_outputs,
2278        })
2279    }
2280
2281    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2282    ///
2283    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2284    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2285    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2286    /// attention and KV ownership are separate milestones.
2287    pub fn bf16_column_parallel_resident_native(
2288        &self,
2289        matrix: &ResidentBf16ColumnParallel,
2290        activations: &[f32],
2291        tokens: usize,
2292    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2293        let rank_outputs =
2294            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2295        let local_out = matrix.out_features / self.ranks.len();
2296        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2297    }
2298
2299    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2300    ///
2301    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2302    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2303    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2304    /// and cache ownership; callers must not treat its existence as serving qualification.
2305    pub fn bf16_column_parallel_resident_device_shards(
2306        &self,
2307        matrix: &ResidentBf16ColumnParallel,
2308        activations: &[f32],
2309        tokens: usize,
2310    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2311        if self.ranks.len() > 1 && !self.native_p2p {
2312            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2313        }
2314        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2315        validate_activations(activations, tokens, matrix.in_features)?;
2316
2317        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2318        let root_input = {
2319            let root = &self.ranks[0];
2320            let _main = root.gpu.enter_main()?;
2321            root.htod(activations)?
2322        };
2323        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2324        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2325        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2326        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2327        // `upload_replicated_device_rows`.
2328        {
2329            let root = &self.ranks[0];
2330            let _main = root.gpu.enter_main()?;
2331            root.stream().synchronize()?;
2332        }
2333        rank_inputs.push(root_input);
2334        for engine in &self.ranks[1..] {
2335            let peer_input = {
2336                let _main = engine.gpu.enter_main()?;
2337                let mut peer_input = engine.uninit(activations.len())?;
2338                engine
2339                    .stream()
2340                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2341                peer_input
2342            };
2343            rank_inputs.push(peer_input);
2344        }
2345
2346        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2347        for rank in 0..self.ranks.len() {
2348            rank_outputs.push(run_resident_bf16_rank_device(
2349                &self.ranks[rank],
2350                &matrix.ranks[rank],
2351                &rank_inputs[rank],
2352                tokens,
2353                matrix.canonical_chunk_rows,
2354                self.bulk_p2p,
2355            )?);
2356        }
2357        Ok(rank_outputs)
2358    }
2359
2360    /// Allocate one fixed-shape replicated batch without initializing its contents.
2361    ///
2362    /// Callers must refresh every rank before passing the batch to an operator.
2363    pub fn allocate_replicated_device_rows(
2364        &self,
2365        tokens: usize,
2366        width: usize,
2367    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2368        if self.ranks.len() > 1 && !self.native_p2p {
2369            return Err("replicated device rows require native P2P ranks".into());
2370        }
2371        let values = tokens
2372            .checked_mul(width)
2373            .ok_or("replicated device row size overflow")?;
2374        let rank_lengths = vec![values; self.ranks.len()];
2375        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2376        let mut ranks = Vec::with_capacity(self.ranks.len());
2377        for engine in &self.ranks {
2378            let _main = engine.gpu.enter_main()?;
2379            ranks.push(engine.uninit(values)?);
2380        }
2381        Ok(ResidentReplicatedDeviceRows {
2382            ranks,
2383            tokens,
2384            width,
2385        })
2386    }
2387
2388    /// Replace a fixed-shape replicated batch from a root-device source.
2389    pub fn refresh_replicated_device_rows_from_root(
2390        &self,
2391        rows: &mut ResidentReplicatedDeviceRows,
2392        source: &CudaSlice<f32>,
2393    ) -> Result<(), Box<dyn std::error::Error>> {
2394        if self.ranks.len() > 1 && !self.native_p2p {
2395            return Err("replicated device rows require native P2P ranks".into());
2396        }
2397        validate_replicated_device_rows(&self.ranks, rows)?;
2398        let root = self
2399            .ranks
2400            .first()
2401            .ok_or("replicated rows have no root rank")?;
2402        let values = replicated_device_row_source_values(
2403            rows.tokens,
2404            rows.width,
2405            source.len(),
2406            source.ordinal(),
2407            root.ctx().ordinal(),
2408        )?;
2409        let (root_rows, peer_rows) = rows
2410            .ranks
2411            .split_first_mut()
2412            .ok_or("replicated rows have no root allocation")?;
2413        {
2414            let _main = root.gpu.enter_main()?;
2415            let mut destination = root_rows.slice_mut(0..values);
2416            root.stream()
2417                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2418            root.stream().synchronize()?;
2419        }
2420        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2421            let _main = engine.gpu.enter_main()?;
2422            let mut destination = peer_rows.slice_mut(0..values);
2423            engine
2424                .stream()
2425                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2426        }
2427        Ok(())
2428    }
2429
2430    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2431    pub fn upload_replicated_device_rows(
2432        &self,
2433        rows: &[f32],
2434        tokens: usize,
2435        width: usize,
2436    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2437        if self.ranks.len() > 1 && !self.native_p2p {
2438            return Err("replicated device rows require native P2P ranks".into());
2439        }
2440        validate_activations(rows, tokens, width)?;
2441        let root = self
2442            .ranks
2443            .first()
2444            .ok_or("replicated rows have no root rank")?;
2445        let root_rows = {
2446            let _main = root.gpu.enter_main()?;
2447            root.htod(rows)?
2448        };
2449        {
2450            let _main = root.gpu.enter_main()?;
2451            root.stream().synchronize()?;
2452        }
2453        let mut ranks = Vec::with_capacity(self.ranks.len());
2454        ranks.push(root_rows);
2455        for engine in self.ranks.iter().skip(1) {
2456            let _main = engine.gpu.enter_main()?;
2457            let mut peer_rows = engine.uninit(rows.len())?;
2458            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2459            ranks.push(peer_rows);
2460        }
2461        Ok(ResidentReplicatedDeviceRows {
2462            ranks,
2463            tokens,
2464            width,
2465        })
2466    }
2467
2468    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2469    pub fn bf16_column_parallel_resident_replicated_device_shards(
2470        &self,
2471        matrix: &ResidentBf16ColumnParallel,
2472        activations: &ResidentReplicatedDeviceRows,
2473    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2474        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2475        validate_replicated_device_rows(&self.ranks, activations)?;
2476        if activations.width != matrix.in_features {
2477            return Err(format!(
2478                "replicated BF16 column input width {} != matrix width {}",
2479                activations.width, matrix.in_features
2480            )
2481            .into());
2482        }
2483        let mut outputs = Vec::with_capacity(self.ranks.len());
2484        for rank in 0..self.ranks.len() {
2485            outputs.push(run_resident_bf16_rank_device(
2486                &self.ranks[rank],
2487                &matrix.ranks[rank],
2488                &activations.ranks[rank],
2489                activations.tokens,
2490                matrix.canonical_chunk_rows,
2491                self.bulk_p2p,
2492            )?);
2493        }
2494        Ok(outputs)
2495    }
2496
2497    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2498    #[allow(clippy::too_many_arguments)]
2499    pub fn upload_sigmoid_topk_router(
2500        &self,
2501        weight: Bf16Matrix<'_>,
2502        correction_bias: &[f32],
2503        active: Option<&[bool]>,
2504        experts_per_token: usize,
2505        scaling_factor: f32,
2506        route_norm: bool,
2507    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2508        weight.validate()?;
2509        if correction_bias.len() != weight.out_features
2510            || experts_per_token == 0
2511            || experts_per_token > weight.out_features
2512            || !correction_bias.iter().all(|value| value.is_finite())
2513            || !scaling_factor.is_finite()
2514            || scaling_factor <= 0.0
2515        {
2516            return Err(format!(
2517                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2518                weight.out_features,
2519                weight.in_features,
2520                correction_bias.len(),
2521                experts_per_token,
2522            )
2523            .into());
2524        }
2525        let active_row = active
2526            .map(|mask| {
2527                if mask.len() != weight.out_features {
2528                    return Err(format!(
2529                        "sigmoid router active mask {} != experts {}",
2530                        mask.len(),
2531                        weight.out_features
2532                    ));
2533                }
2534                Ok(mask
2535                    .iter()
2536                    .map(|&enabled| u8::from(enabled))
2537                    .collect::<Vec<_>>())
2538            })
2539            .transpose()?
2540            .unwrap_or_else(|| vec![1; weight.out_features]);
2541        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2542        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2543
2544        let root = self
2545            .ranks
2546            .first()
2547            .ok_or("sigmoid router runtime has no root rank")?;
2548        let _main = root.gpu.enter_main()?;
2549        let bf16 = root.htod_bytes(weight.bytes)?;
2550        let weight_f32 = root.bf16_to_f32(
2551            &bf16.slice(0..bf16.len()),
2552            weight.out_features * weight.in_features,
2553        )?;
2554        Ok(ResidentSigmoidTopKRouter {
2555            weight: weight_f32,
2556            correction_bias: root.htod(correction_bias)?,
2557            active: root.htod_bytes(&active_row)?,
2558            root_device: root.ctx().ordinal(),
2559            input_width: weight.in_features,
2560            expert_count: weight.out_features,
2561            experts_per_token,
2562            active_count,
2563            scaling_factor,
2564            route_norm,
2565        })
2566    }
2567
2568    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2569    ///
2570    /// The logits readback exists for independent oracle comparison. This method is a correctness
2571    /// surface; a serving scheduler may retain logits and selected routes on device.
2572    pub fn sigmoid_topk_replicated_device_rows_host(
2573        &self,
2574        router: &ResidentSigmoidTopKRouter,
2575        input: &ResidentReplicatedDeviceRows,
2576    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2577        validate_replicated_device_rows(&self.ranks, input)?;
2578        if input.width != router.input_width {
2579            return Err(format!(
2580                "sigmoid router input width {} != resident width {}",
2581                input.width, router.input_width
2582            )
2583            .into());
2584        }
2585        let root = self
2586            .ranks
2587            .first()
2588            .ok_or("sigmoid router runtime has no root rank")?;
2589        let _main = root.gpu.enter_main()?;
2590        if root.ctx().ordinal() != router.root_device
2591            || router.weight.ordinal() != router.root_device
2592            || router.correction_bias.ordinal() != router.root_device
2593            || router.active.ordinal() != router.root_device
2594        {
2595            return Err("sigmoid router root residency changed".into());
2596        }
2597        let logits = root.router_gemv(
2598            &router.weight,
2599            &input.ranks[0],
2600            router.input_width,
2601            router.expert_count,
2602            input.tokens,
2603        )?;
2604        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2605            &logits,
2606            input.tokens,
2607            router.expert_count,
2608            router.experts_per_token,
2609            router.active_count,
2610            &router.correction_bias,
2611            &router.active,
2612            router.scaling_factor,
2613            router.route_norm,
2614        )?;
2615        Ok(SigmoidTopKHostOutput {
2616            logits: root.dtoh(&logits)?,
2617            selected,
2618            weights,
2619        })
2620    }
2621
2622    /// Replicate a full BF16 SwiGLU bank on every rank.
2623    pub fn upload_replicated_bf16_swiglu(
2624        &self,
2625        gate: Bf16Matrix<'_>,
2626        up: Bf16Matrix<'_>,
2627        down: Bf16Matrix<'_>,
2628    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2629        gate.validate()?;
2630        up.validate()?;
2631        down.validate()?;
2632        if gate.in_features != up.in_features
2633            || gate.out_features != up.out_features
2634            || down.in_features != gate.out_features
2635            || down.out_features != gate.in_features
2636        {
2637            return Err(format!(
2638                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2639                gate.out_features,
2640                gate.in_features,
2641                up.out_features,
2642                up.in_features,
2643                down.out_features,
2644                down.in_features,
2645            )
2646            .into());
2647        }
2648        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2649        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2650        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2651        for engine in &self.ranks {
2652            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2653            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2654            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2655        }
2656        Ok(ResidentReplicatedBf16SwiGlu {
2657            gate: gate_ranks,
2658            up: up_ranks,
2659            down: down_ranks,
2660            input_width: gate.in_features,
2661            intermediate_width: gate.out_features,
2662        })
2663    }
2664
2665    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2666    pub fn replicated_bf16_swiglu_resident_device(
2667        &self,
2668        mlp: &ResidentReplicatedBf16SwiGlu,
2669        input: &ResidentReplicatedDeviceRows,
2670        activation_limit: Option<f32>,
2671    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2672        validate_step_expert_activation_limit(activation_limit)?;
2673        validate_replicated_device_rows(&self.ranks, input)?;
2674        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
2675        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
2676        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
2677        if input.width != mlp.input_width
2678            || mlp.gate.len() != self.ranks.len()
2679            || mlp.up.len() != self.ranks.len()
2680            || mlp.down.len() != self.ranks.len()
2681        {
2682            return Err("replicated BF16 SwiGLU residency or input width changed".into());
2683        }
2684
2685        let mut outputs = Vec::with_capacity(self.ranks.len());
2686        for rank in 0..self.ranks.len() {
2687            let engine = &self.ranks[rank];
2688            let gate = run_resident_bf16_rank_device(
2689                engine,
2690                &mlp.gate[rank],
2691                &input.ranks[rank],
2692                input.tokens,
2693                None,
2694                self.bulk_p2p,
2695            )?;
2696            let up = run_resident_bf16_rank_device(
2697                engine,
2698                &mlp.up[rank],
2699                &input.ranks[rank],
2700                input.tokens,
2701                None,
2702                self.bulk_p2p,
2703            )?;
2704            let _main = engine.gpu.enter_main()?;
2705            let values = input
2706                .tokens
2707                .checked_mul(mlp.intermediate_width)
2708                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
2709            let mut activation = engine.uninit(values)?;
2710            if let Some(limit) = activation_limit {
2711                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
2712            } else {
2713                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
2714            }
2715            outputs.push(run_resident_bf16_rank_device(
2716                engine,
2717                &mlp.down[rank],
2718                &activation,
2719                input.tokens,
2720                None,
2721                self.bulk_p2p,
2722            )?);
2723        }
2724        Ok(ResidentReplicatedDeviceRows {
2725            ranks: outputs,
2726            tokens: input.tokens,
2727            width: mlp.input_width,
2728        })
2729    }
2730
2731    /// Apply the same RMS-norm row program independently on every replicated rank.
2732    pub fn rms_norm_replicated_device_rows(
2733        &self,
2734        input: &ResidentReplicatedDeviceRows,
2735        weight: &[f32],
2736        eps: f32,
2737    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2738        validate_replicated_device_rows(&self.ranks, input)?;
2739        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
2740            return Err(format!(
2741                "replicated RMS norm weight/eps {}/{} != width {}",
2742                weight.len(),
2743                eps,
2744                input.width
2745            )
2746            .into());
2747        }
2748        let mut ranks = Vec::with_capacity(self.ranks.len());
2749        for (rank, engine) in self.ranks.iter().enumerate() {
2750            let _main = engine.gpu.enter_main()?;
2751            let weight = engine.htod(weight)?;
2752            let mut output = engine.uninit(input.tokens * input.width)?;
2753            engine.rms_norm(
2754                &input.ranks[rank],
2755                &weight,
2756                &mut output,
2757                input.width,
2758                input.tokens,
2759                eps,
2760            )?;
2761            ranks.push(output);
2762        }
2763        Ok(ResidentReplicatedDeviceRows {
2764            ranks,
2765            tokens: input.tokens,
2766            width: input.width,
2767        })
2768    }
2769
2770    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
2771    pub fn add_rms_norm_replicated_device_rows(
2772        &self,
2773        input: &ResidentReplicatedDeviceRows,
2774        update: &ResidentReplicatedDeviceRows,
2775        weight: &[f32],
2776        eps: f32,
2777    ) -> Result<
2778        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
2779        Box<dyn std::error::Error>,
2780    > {
2781        validate_replicated_device_rows(&self.ranks, input)?;
2782        validate_replicated_device_rows(&self.ranks, update)?;
2783        if input.tokens != update.tokens
2784            || input.width != update.width
2785            || weight.len() != input.width
2786            || !eps.is_finite()
2787            || eps <= 0.0
2788        {
2789            return Err(format!(
2790                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
2791                input.tokens,
2792                input.width,
2793                update.tokens,
2794                update.width,
2795                weight.len(),
2796            )
2797            .into());
2798        }
2799        let values = input.tokens * input.width;
2800        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
2801        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
2802        for (rank, engine) in self.ranks.iter().enumerate() {
2803            let _main = engine.gpu.enter_main()?;
2804            let weight = engine.htod(weight)?;
2805            let mut residual = engine.uninit(values)?;
2806            let mut normalized = engine.uninit(values)?;
2807            engine.add_rms_norm(
2808                &input.ranks[rank],
2809                &update.ranks[rank],
2810                &weight,
2811                &mut residual,
2812                &mut normalized,
2813                input.width,
2814                input.tokens,
2815                eps,
2816            )?;
2817            residual_ranks.push(residual);
2818            normalized_ranks.push(normalized);
2819        }
2820        Ok((
2821            ResidentReplicatedDeviceRows {
2822                ranks: residual_ranks,
2823                tokens: input.tokens,
2824                width: input.width,
2825            },
2826            ResidentReplicatedDeviceRows {
2827                ranks: normalized_ranks,
2828                tokens: input.tokens,
2829                width: input.width,
2830            },
2831        ))
2832    }
2833
2834    pub fn collect_replicated_device_rows(
2835        &self,
2836        rows: &ResidentReplicatedDeviceRows,
2837    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
2838        validate_replicated_device_rows(&self.ranks, rows)?;
2839        let mut outputs = Vec::with_capacity(self.ranks.len());
2840        for (rank, engine) in self.ranks.iter().enumerate() {
2841            let _main = engine.gpu.enter_main()?;
2842            outputs.push(engine.dtoh(&rows.ranks[rank])?);
2843        }
2844        Ok(outputs)
2845    }
2846
2847    pub fn upload_bf16_row_parallel(
2848        &self,
2849        matrix: Bf16Matrix<'_>,
2850    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
2851        matrix.validate()?;
2852        let tp = self.ranks.len();
2853        if matrix.in_features % tp != 0 {
2854            return Err(format!(
2855                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
2856                matrix.in_features
2857            )
2858            .into());
2859        }
2860        let mut ranks = Vec::with_capacity(tp);
2861        for (rank, engine) in self.ranks.iter().enumerate() {
2862            let shard = bf16_row_shard(matrix, tp, rank)?;
2863            ranks.push(upload_bf16_rank(
2864                engine,
2865                Bf16Matrix {
2866                    bytes: &shard,
2867                    out_features: matrix.out_features,
2868                    in_features: matrix.in_features / tp,
2869                },
2870                false,
2871            )?);
2872        }
2873        Ok(ResidentBf16RowParallel {
2874            ranks,
2875            out_features: matrix.out_features,
2876            in_features: matrix.in_features,
2877        })
2878    }
2879
2880    pub fn bf16_row_parallel_resident(
2881        &self,
2882        matrix: &ResidentBf16RowParallel,
2883        activations: &[f32],
2884        tokens: usize,
2885    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2886        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2887        validate_activations(activations, tokens, matrix.in_features)?;
2888        let tp = self.ranks.len();
2889        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2890        let mut rank_partials = Vec::with_capacity(tp);
2891        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2892            let local_activations =
2893                activation_shard(activations, tokens, matrix.in_features, tp, rank);
2894            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
2895            for (sum, value) in reduced.iter_mut().zip(&partial) {
2896                *sum += value;
2897            }
2898            rank_partials.push(partial);
2899        }
2900        Ok(RowParallelResult {
2901            reduced,
2902            rank_partials,
2903        })
2904    }
2905
2906    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
2907    pub fn upload_step_bf16_row_parallel(
2908        &self,
2909        matrix: Bf16Matrix<'_>,
2910    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2911        self.upload_step_bf16_row_parallel_inner(matrix, false)
2912    }
2913
2914    pub fn upload_step_bf16_row_parallel_f32_mirror(
2915        &self,
2916        matrix: Bf16Matrix<'_>,
2917    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2918        self.upload_step_bf16_row_parallel_inner(matrix, true)
2919    }
2920
2921    fn upload_step_bf16_row_parallel_inner(
2922        &self,
2923        matrix: Bf16Matrix<'_>,
2924        f32_mirror: bool,
2925    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2926        matrix.validate()?;
2927        let tp = self.ranks.len();
2928        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
2929        let local_in = matrix.in_features / tp;
2930        let blocks_per_rank = local_in / canonical_chunk_cols;
2931        let mut ranks = Vec::with_capacity(tp);
2932        for (rank, engine) in self.ranks.iter().enumerate() {
2933            let mut blocks = Vec::with_capacity(blocks_per_rank);
2934            for block in 0..blocks_per_rank {
2935                let global_block = rank * blocks_per_rank + block;
2936                let col_start = global_block * canonical_chunk_cols;
2937                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
2938                blocks.push(upload_bf16_rank(
2939                    engine,
2940                    Bf16Matrix {
2941                        bytes: &bytes,
2942                        out_features: matrix.out_features,
2943                        in_features: canonical_chunk_cols,
2944                    },
2945                    f32_mirror,
2946                )?);
2947            }
2948            ranks.push(blocks);
2949        }
2950        Ok(ResidentStepBf16RowParallel {
2951            ranks,
2952            out_features: matrix.out_features,
2953            in_features: matrix.in_features,
2954            canonical_chunk_cols,
2955        })
2956    }
2957
2958    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
2959    ///
2960    /// Block inputs and partials cross host memory, but every partial is added on the root device
2961    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
2962    pub fn step_bf16_row_parallel_resident(
2963        &self,
2964        matrix: &ResidentStepBf16RowParallel,
2965        activations: &[f32],
2966        tokens: usize,
2967    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2968        validate_step_bf16_row_residency(&self.ranks, matrix)?;
2969        validate_activations(activations, tokens, matrix.in_features)?;
2970        let root = &self.ranks[0];
2971        let output_len = tokens
2972            .checked_mul(matrix.out_features)
2973            .ok_or("Step BF16 row output size overflow")?;
2974        let mut reduced = {
2975            let _main = root.gpu.enter_main()?;
2976            root.htod(&vec![0.0f32; output_len])?
2977        };
2978        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
2979        for (rank, blocks) in matrix.ranks.iter().enumerate() {
2980            for (block, resident) in blocks.iter().enumerate() {
2981                let global_block = rank * blocks_per_rank + block;
2982                let input = activation_shard(
2983                    activations,
2984                    tokens,
2985                    matrix.in_features,
2986                    PRODUCT_MAX_CARDS,
2987                    global_block,
2988                );
2989                let partial =
2990                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
2991                let next = {
2992                    let _main = root.gpu.enter_main()?;
2993                    let partial = root.htod(&partial)?;
2994                    let mut next = root.uninit(output_len)?;
2995                    root.add(&reduced, &partial, &mut next, output_len)?;
2996                    next
2997                };
2998                reduced = next;
2999            }
3000        }
3001        let _main = root.gpu.enter_main()?;
3002        root.dtoh(&reduced)
3003    }
3004
3005    /// Native-P2P Step row projection with canonical global K-block reduction.
3006    ///
3007    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3008    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3009    /// replay the same eight-block order as TP1 and the host-staged oracle.
3010    pub fn step_bf16_row_parallel_resident_native(
3011        &self,
3012        matrix: &ResidentStepBf16RowParallel,
3013        activations: &[f32],
3014        tokens: usize,
3015    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3016        if self.ranks.len() > 1 && !self.native_p2p {
3017            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3018        }
3019        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3020        validate_activations(activations, tokens, matrix.in_features)?;
3021        let root = &self.ranks[0];
3022        let root_input = {
3023            let _main = root.gpu.enter_main()?;
3024            root.htod(activations)?
3025        };
3026        let output_len = tokens
3027            .checked_mul(matrix.out_features)
3028            .ok_or("native Step BF16 row output size overflow")?;
3029        let mut reduced = {
3030            let _main = root.gpu.enter_main()?;
3031            root.htod(&vec![0.0f32; output_len])?
3032        };
3033        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3034        // from the other ranks' streams while root's clone_htod may still be in flight.
3035        {
3036            let _main = root.gpu.enter_main()?;
3037            root.stream().synchronize()?;
3038        }
3039        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3040        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3041        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3042        let mut remote_partial_keepalive = Vec::new();
3043        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3044            for (block, resident) in blocks.iter().enumerate() {
3045                let global_block = rank * blocks_per_rank + block;
3046                let col_start = global_block * matrix.canonical_chunk_cols;
3047                let block_len = tokens
3048                    .checked_mul(matrix.canonical_chunk_cols)
3049                    .ok_or("native Step BF16 row block size overflow")?;
3050                let block_input = if self.bulk_p2p {
3051                    let root_packed = {
3052                        let _main = root.gpu.enter_main()?;
3053                        let mut root_packed = root.uninit(block_len)?;
3054                        root.copy_rows_strided(
3055                            &root_input,
3056                            &mut root_packed,
3057                            matrix.canonical_chunk_cols,
3058                            tokens,
3059                            matrix.in_features,
3060                            col_start,
3061                        )?;
3062                        root_packed
3063                    };
3064                    if rank == 0 {
3065                        root_packed
3066                    } else {
3067                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3068                        // root stream; this rank's peer read must not overtake it.
3069                        {
3070                            let _main = root.gpu.enter_main()?;
3071                            root.stream().synchronize()?;
3072                        }
3073                        let engine = &self.ranks[rank];
3074                        let _main = engine.gpu.enter_main()?;
3075                        let mut block_input = engine.uninit(block_len)?;
3076                        engine
3077                            .stream()
3078                            .memcpy_dtod(&root_packed, &mut block_input)?;
3079                        root_packed_keepalive.push(root_packed);
3080                        block_input
3081                    }
3082                } else {
3083                    let engine = &self.ranks[rank];
3084                    let _main = engine.gpu.enter_main()?;
3085                    let mut block_input = engine.uninit(block_len)?;
3086                    for token in 0..tokens {
3087                        let source_start = token * matrix.in_features + col_start;
3088                        let source = root_input
3089                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3090                        let destination_start = token * matrix.canonical_chunk_cols;
3091                        let mut destination = block_input.slice_mut(
3092                            destination_start..destination_start + matrix.canonical_chunk_cols,
3093                        );
3094                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3095                    }
3096                    block_input
3097                };
3098                let partial = run_resident_bf16_rank_device(
3099                    &self.ranks[rank],
3100                    resident,
3101                    &block_input,
3102                    tokens,
3103                    None,
3104                    self.bulk_p2p,
3105                )?;
3106                block_input_keepalive.push(block_input);
3107                let root_partial = if rank == 0 {
3108                    partial
3109                } else {
3110                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3111                    // rank's kernel on its own stream; root's peer read must not overtake it.
3112                    {
3113                        let engine = &self.ranks[rank];
3114                        let _main = engine.gpu.enter_main()?;
3115                        engine.stream().synchronize()?;
3116                    }
3117                    let _main = root.gpu.enter_main()?;
3118                    let mut peer_partial = root.uninit(output_len)?;
3119                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3120                    remote_partial_keepalive.push(partial);
3121                    peer_partial
3122                };
3123                let next = {
3124                    let _main = root.gpu.enter_main()?;
3125                    let mut next = root.uninit(output_len)?;
3126                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3127                    next
3128                };
3129                reduced = next;
3130            }
3131        }
3132        let output = {
3133            let _main = root.gpu.enter_main()?;
3134            root.dtoh(&reduced)?
3135        };
3136        drop(remote_partial_keepalive);
3137        drop(root_packed_keepalive);
3138        drop(block_input_keepalive);
3139        Ok(output)
3140    }
3141
3142    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3143    /// on the root device.
3144    pub fn step_bf16_row_parallel_resident_root_device(
3145        &self,
3146        matrix: &ResidentStepBf16RowParallel,
3147        rank_activations: &[CudaSlice<f32>],
3148        tokens: usize,
3149    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3150        if self.ranks.len() > 1 && !self.native_p2p {
3151            return Err(
3152                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3153            );
3154        }
3155        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3156        let local_width = matrix.in_features / self.ranks.len();
3157        let shard_len = tokens
3158            .checked_mul(local_width)
3159            .ok_or("device Step BF16 row shard size overflow")?;
3160        if tokens == 0
3161            || rank_activations.len() != self.ranks.len()
3162            || rank_activations
3163                .iter()
3164                .zip(&self.ranks)
3165                .any(|(rows, engine)| {
3166                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3167                })
3168        {
3169            return Err("device Step BF16 row activation shard geometry changed".into());
3170        }
3171
3172        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3173        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3174        let mut partials = Vec::with_capacity(self.ranks.len());
3175        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3176            if blocks.len() != blocks_per_rank {
3177                return Err(format!(
3178                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3179                    blocks.len()
3180                )
3181                .into());
3182            }
3183            let engine = &self.ranks[rank];
3184            let _main = engine.gpu.enter_main()?;
3185            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3186            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3187            for (block, resident) in blocks.iter().enumerate() {
3188                let block_len = tokens
3189                    .checked_mul(matrix.canonical_chunk_cols)
3190                    .ok_or("device Step BF16 row block size overflow")?;
3191                let mut block_input = engine.uninit(block_len)?;
3192                let local_col_start = block * matrix.canonical_chunk_cols;
3193                if self.bulk_p2p {
3194                    engine.copy_rows_strided(
3195                        &rank_activations[rank],
3196                        &mut block_input,
3197                        matrix.canonical_chunk_cols,
3198                        tokens,
3199                        local_width,
3200                        local_col_start,
3201                    )?;
3202                } else {
3203                    for token in 0..tokens {
3204                        let source_start = token * local_width + local_col_start;
3205                        let source = rank_activations[rank]
3206                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3207                        let destination_start = token * matrix.canonical_chunk_cols;
3208                        let mut destination = block_input.slice_mut(
3209                            destination_start..destination_start + matrix.canonical_chunk_cols,
3210                        );
3211                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3212                    }
3213                }
3214                let partial = run_resident_bf16_rank_device(
3215                    engine,
3216                    resident,
3217                    &block_input,
3218                    tokens,
3219                    None,
3220                    self.bulk_p2p,
3221                )?;
3222                rank_inputs.push(block_input);
3223                rank_partials.push(partial);
3224            }
3225            block_inputs.push(rank_inputs);
3226            partials.push(rank_partials);
3227        }
3228        for engine in self.ranks.iter().skip(1) {
3229            let _main = engine.gpu.enter_main()?;
3230            engine.stream().synchronize()?;
3231        }
3232
3233        let output_len = tokens
3234            .checked_mul(matrix.out_features)
3235            .ok_or("device Step BF16 row output size overflow")?;
3236        let root = &self.ranks[0];
3237        let _main = root.gpu.enter_main()?;
3238        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3239        let mut remote_partials = Vec::new();
3240        for (rank, rank_partials) in partials.into_iter().enumerate() {
3241            for partial in rank_partials {
3242                let root_partial = if rank == 0 {
3243                    partial
3244                } else {
3245                    let mut peer_partial = root.uninit(output_len)?;
3246                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3247                    remote_partials.push(partial);
3248                    peer_partial
3249                };
3250                let mut next = root.uninit(output_len)?;
3251                root.add(&reduced, &root_partial, &mut next, output_len)?;
3252                reduced = next;
3253            }
3254        }
3255        root.stream().synchronize()?;
3256        drop(remote_partials);
3257        drop(block_inputs);
3258        Ok(reduced)
3259    }
3260
3261    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3262    pub fn step_bf16_row_parallel_resident_replicated_device(
3263        &self,
3264        matrix: &ResidentStepBf16RowParallel,
3265        rank_activations: &[CudaSlice<f32>],
3266        tokens: usize,
3267    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3268        let reduced =
3269            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3270        let output_len = tokens
3271            .checked_mul(matrix.out_features)
3272            .ok_or("device Step BF16 row output size overflow")?;
3273        let mut ranks = Vec::with_capacity(self.ranks.len());
3274        ranks.push(reduced);
3275        for engine in self.ranks.iter().skip(1) {
3276            let _main = engine.gpu.enter_main()?;
3277            let mut peer_output = engine.uninit(output_len)?;
3278            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3279            ranks.push(peer_output);
3280        }
3281        Ok(ResidentReplicatedDeviceRows {
3282            ranks,
3283            tokens,
3284            width: matrix.out_features,
3285        })
3286    }
3287
3288    pub fn upload_expert(
3289        &self,
3290        gate: E4m3BlockMatrix<'_>,
3291        up: E4m3BlockMatrix<'_>,
3292        down: E4m3BlockMatrix<'_>,
3293    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3294        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3295            return Err("TP expert gate/up dimensions differ".into());
3296        }
3297        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3298            return Err(format!(
3299                "TP expert down {}x{} does not invert gate/up {}x{}",
3300                down.out_features, down.in_features, gate.out_features, gate.in_features
3301            )
3302            .into());
3303        }
3304        Ok(ResidentTpExpert {
3305            gate: self.upload_column_parallel(gate)?,
3306            up: self.upload_column_parallel(up)?,
3307            down: self.upload_row_parallel(down)?,
3308            input_width: gate.in_features,
3309            expert_width: gate.out_features,
3310        })
3311    }
3312
3313    pub fn run_expert(
3314        &self,
3315        expert: &ResidentTpExpert,
3316        input: &[f32],
3317        tokens: usize,
3318    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3319        validate_activations(input, tokens, expert.input_width)?;
3320        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3321        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3322        let activated: Vec<f32> = gate
3323            .gathered
3324            .iter()
3325            .zip(&up.gathered)
3326            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3327            .collect();
3328        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3329        Ok(self
3330            .row_parallel_resident(&expert.down, &activated, tokens)?
3331            .reduced)
3332    }
3333
3334    pub fn upload_expert_parallel(
3335        &self,
3336        gate: E4m3ExpertBank<'_>,
3337        up: E4m3ExpertBank<'_>,
3338        down: E4m3ExpertBank<'_>,
3339    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3340        gate.validate()?;
3341        up.validate()?;
3342        down.validate()?;
3343        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3344            return Err("EP gate/up/down expert counts differ".into());
3345        }
3346        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3347            return Err("EP gate/up dimensions differ".into());
3348        }
3349        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3350            return Err(format!(
3351                "EP down {}x{} does not invert gate/up {}x{}",
3352                down.out_features, down.in_features, gate.out_features, gate.in_features
3353            )
3354            .into());
3355        }
3356        if gate.expert_count % self.ranks.len() != 0 {
3357            return Err(format!(
3358                "EP expert count {} is not divisible by {} ranks",
3359                gate.expert_count,
3360                self.ranks.len()
3361            )
3362            .into());
3363        }
3364
3365        let per_rank = gate.expert_count / self.ranks.len();
3366        let mut ranks = Vec::with_capacity(self.ranks.len());
3367        for (rank, engine) in self.ranks.iter().enumerate() {
3368            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3369            ranks.push(ResidentEpRank {
3370                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3371                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3372                down: upload_expert_bank_rank(engine, down, expert_range)?,
3373            });
3374        }
3375        Ok(ResidentExpertParallel {
3376            ranks,
3377            expert_count: gate.expert_count,
3378            input_width: gate.in_features,
3379            expert_width: gate.out_features,
3380        })
3381    }
3382
3383    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3384    ///
3385    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3386    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3387    /// without routing, transport, or combine changing underneath it.
3388    #[allow(clippy::too_many_arguments)]
3389    pub fn prepare_step_grouped_fp8_gate(
3390        &self,
3391        gate: E4m3ExpertBank<'_>,
3392        up: E4m3ExpertBank<'_>,
3393        down: E4m3ExpertBank<'_>,
3394        input: &[f32],
3395        tokens: usize,
3396        selected: &[usize],
3397        activation_limit: Option<f32>,
3398    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3399        gate.validate()?;
3400        up.validate()?;
3401        down.validate()?;
3402        validate_step_expert_activation_limit(activation_limit)?;
3403        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3404            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3405            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3406        {
3407            return Err(format!(
3408                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3409                 got gate/up/down={}/{}/{}",
3410                gate.expert_count, up.expert_count, down.expert_count,
3411            )
3412            .into());
3413        }
3414        if gate.in_features != up.in_features
3415            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3416            || up.out_features != STEP_GROUPED_FP8_WIDTH
3417            || down.in_features != STEP_GROUPED_FP8_WIDTH
3418            || down.out_features != gate.in_features
3419        {
3420            return Err(format!(
3421                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3422                gate.out_features,
3423                gate.in_features,
3424                up.out_features,
3425                up.in_features,
3426                down.out_features,
3427                down.in_features,
3428            )
3429            .into());
3430        }
3431        validate_activations(input, tokens, gate.in_features)?;
3432        let pairs = tokens
3433            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3434            .ok_or("official Step grouped FP8 route count overflow")?;
3435        if selected.len() != pairs {
3436            return Err(format!(
3437                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3438                 ({pairs})",
3439                selected.len()
3440            )
3441            .into());
3442        }
3443        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3444            let mut unique = routes.to_vec();
3445            unique.sort_unstable();
3446            unique.dedup();
3447            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3448                return Err(format!(
3449                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3450                     {routes:?}"
3451                )
3452                .into());
3453            }
3454        }
3455
3456        let engine = self
3457            .ranks
3458            .first()
3459            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3460        let _main = engine.gpu.enter_main()?;
3461        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3462        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3463        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3464        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3465        let input = engine.htod(input)?;
3466        let route_csr = ExpertCsr::from_token_routes(
3467            STEP_GROUPED_FP8_EXPERTS,
3468            tokens,
3469            STEP_GROUPED_FP8_TOP_K,
3470            selected,
3471        )?
3472        .upload(engine)?;
3473        let pair_rows = (0..pairs).collect::<Vec<_>>();
3474        let down_csr =
3475            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3476                .upload(engine)?;
3477        let gate_workspace =
3478            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3479        let up_workspace =
3480            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3481        let down_workspace =
3482            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3483        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3484        Ok(PreparedStepGroupedFp8Gate {
3485            device: engine.ctx().ordinal(),
3486            gate,
3487            up,
3488            down,
3489            input,
3490            route_csr,
3491            down_csr,
3492            gate_workspace,
3493            up_workspace,
3494            down_workspace,
3495            activation,
3496            activation_limit,
3497            tokens,
3498            pairs,
3499        })
3500    }
3501
3502    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3503    pub fn run_step_grouped_fp8_gate(
3504        &self,
3505        plan: &mut PreparedStepGroupedFp8Gate,
3506    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3507        let engine = self
3508            .ranks
3509            .first()
3510            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3511        if engine.ctx().ordinal() != plan.device {
3512            return Err(format!(
3513                "official Step grouped FP8 plan device {} != rank-zero device {}",
3514                plan.device,
3515                engine.ctx().ordinal()
3516            )
3517            .into());
3518        }
3519        let _main = engine.gpu.enter_main()?;
3520
3521        plan.gate_workspace.quantize(engine, &plan.input)?;
3522        plan.gate_workspace.project(
3523            engine,
3524            &plan.gate.codes,
3525            &plan.gate.scales,
3526            &plan.route_csr,
3527            plan.gate.code_stride,
3528            plan.gate.scale_stride,
3529            1.0,
3530        )?;
3531        plan.up_workspace.quantize(engine, &plan.input)?;
3532        plan.up_workspace.project(
3533            engine,
3534            &plan.up.codes,
3535            &plan.up.scales,
3536            &plan.route_csr,
3537            plan.up.code_stride,
3538            plan.up.scale_stride,
3539            1.0,
3540        )?;
3541        if let Some(limit) = plan.activation_limit {
3542            engine.silu_clamped_mul_host_expf(
3543                plan.gate_workspace.output(),
3544                plan.up_workspace.output(),
3545                limit,
3546                &mut plan.activation,
3547                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3548            )?;
3549        } else {
3550            engine.silu_mul_host_expf(
3551                plan.gate_workspace.output(),
3552                plan.up_workspace.output(),
3553                &mut plan.activation,
3554                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3555            )?;
3556        }
3557        plan.down_workspace.quantize(engine, &plan.activation)?;
3558        plan.down_workspace.project(
3559            engine,
3560            &plan.down.codes,
3561            &plan.down.scales,
3562            &plan.down_csr,
3563            plan.down.code_stride,
3564            plan.down.scale_stride,
3565            1.0,
3566        )?;
3567
3568        Ok(StepGroupedFp8ProjectionOutput {
3569            gate: engine.dtoh(plan.gate_workspace.output())?,
3570            up: engine.dtoh(plan.up_workspace.output())?,
3571            down: engine.dtoh(plan.down_workspace.output())?,
3572        })
3573    }
3574
3575    pub fn prepare_step_grouped_expert_parallel_gate(
3576        &self,
3577        experts: &ResidentExpertParallel,
3578        input: &[f32],
3579        tokens: usize,
3580        selected: &[usize],
3581        activation_limit: Option<f32>,
3582    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3583        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3584            experts,
3585            input,
3586            tokens,
3587            selected,
3588            activation_limit,
3589            tokens,
3590        )
3591    }
3592
3593    #[allow(clippy::too_many_arguments)]
3594    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3595        &self,
3596        experts: &ResidentExpertParallel,
3597        input: &[f32],
3598        tokens: usize,
3599        selected: &[usize],
3600        activation_limit: Option<f32>,
3601        max_tokens: usize,
3602    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3603        if !self.native_p2p || !self.ep_device_arithmetic {
3604            return Err(
3605                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3606            );
3607        }
3608        validate_step_expert_activation_limit(activation_limit)?;
3609        validate_ep_residency(&self.ranks, experts)?;
3610        validate_activations(input, tokens, experts.input_width)?;
3611        if max_tokens < tokens || max_tokens > i32::MAX as usize {
3612            return Err(format!(
3613                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
3614            )
3615            .into());
3616        }
3617        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
3618            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
3619        {
3620            return Err(format!(
3621                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
3622                STEP_GROUPED_FP8_EXPERTS,
3623                STEP_GROUPED_FP8_WIDTH,
3624                experts.expert_count,
3625                experts.expert_width,
3626            )
3627            .into());
3628        }
3629        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3630        let max_pairs = max_tokens
3631            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3632            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
3633        let input_capacity = max_tokens
3634            .checked_mul(experts.input_width)
3635            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
3636
3637        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3638        for engine in &self.ranks {
3639            let _main = engine.gpu.enter_main()?;
3640            rank_inputs.push(engine.uninit(input_capacity)?);
3641        }
3642
3643        let mut owners = Vec::with_capacity(self.ranks.len());
3644        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
3645            if rank.gate.expert_range != rank.up.expert_range
3646                || rank.gate.expert_range != rank.down.expert_range
3647            {
3648                return Err(format!(
3649                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
3650                    owner_rank
3651                )
3652                .into());
3653            }
3654            let local_experts = rank.gate.expert_range.len();
3655            let engine = &self.ranks[owner_rank];
3656            let _main = engine.gpu.enter_main()?;
3657            let route_csr =
3658                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
3659            let down_csr =
3660                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
3661            let gate_workspace = Fp8GroupedWorkspace::new(
3662                engine,
3663                experts.input_width,
3664                experts.expert_width,
3665                max_tokens,
3666                max_pairs,
3667            )?;
3668            let up_workspace = Fp8GroupedWorkspace::new(
3669                engine,
3670                experts.input_width,
3671                experts.expert_width,
3672                max_tokens,
3673                max_pairs,
3674            )?;
3675            let down_workspace = Fp8GroupedWorkspace::new(
3676                engine,
3677                experts.expert_width,
3678                experts.input_width,
3679                max_pairs,
3680                max_pairs,
3681            )?;
3682            let activation = engine.uninit(
3683                max_pairs
3684                    .checked_mul(experts.expert_width)
3685                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
3686            )?;
3687            owners.push(PreparedStepGroupedExpertOwner {
3688                rank: owner_rank,
3689                global_pairs: Vec::new(),
3690                route_csr,
3691                down_csr,
3692                gate_workspace,
3693                up_workspace,
3694                down_workspace,
3695                activation,
3696            });
3697        }
3698
3699        let mut plan = PreparedStepGroupedExpertParallelGate {
3700            rank_inputs,
3701            owners,
3702            activation_limit,
3703            tokens: 0,
3704            pairs: 0,
3705            max_tokens,
3706            max_pairs,
3707            input_width: experts.input_width,
3708            expert_width: experts.expert_width,
3709            generation: 0,
3710            executed_generation: None,
3711            ready: false,
3712        };
3713        self.refresh_step_grouped_expert_parallel_gate(
3714            experts, &mut plan, input, tokens, selected,
3715        )?;
3716        Ok(plan)
3717    }
3718
3719    fn prepare_step_grouped_expert_parallel_refresh(
3720        &self,
3721        experts: &ResidentExpertParallel,
3722        plan: &PreparedStepGroupedExpertParallelGate,
3723        tokens: usize,
3724        selected: &[usize],
3725    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
3726    {
3727        validate_ep_residency(&self.ranks, experts)?;
3728        if plan.rank_inputs.len() != self.ranks.len()
3729            || plan.owners.len() != self.ranks.len()
3730            || plan.input_width != experts.input_width
3731            || plan.expert_width != experts.expert_width
3732            || tokens > plan.max_tokens
3733        {
3734            return Err(format!(
3735                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
3736                 input={}/{} expert={}/{} tokens={}/{}",
3737                plan.rank_inputs.len(),
3738                self.ranks.len(),
3739                plan.owners.len(),
3740                self.ranks.len(),
3741                plan.input_width,
3742                experts.input_width,
3743                plan.expert_width,
3744                experts.expert_width,
3745                tokens,
3746                plan.max_tokens,
3747            )
3748            .into());
3749        }
3750        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3751        if pairs > plan.max_pairs {
3752            return Err(format!(
3753                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
3754                plan.max_pairs
3755            )
3756            .into());
3757        }
3758        let next_generation = plan
3759            .generation
3760            .checked_add(1)
3761            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
3762        let owner_routes = partition_expert_owner_routes(
3763            experts.expert_count,
3764            self.ranks.len(),
3765            tokens,
3766            STEP_GROUPED_FP8_TOP_K,
3767            selected,
3768        )?;
3769        let mut schedules = Vec::with_capacity(self.ranks.len());
3770        for routes in owner_routes {
3771            if routes.selected.is_empty() {
3772                schedules.push(None);
3773                continue;
3774            }
3775            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
3776            let local_pairs = routes.selected.len();
3777            let route_csr = ExpertCsr::from_pair_rows(
3778                local_experts,
3779                tokens,
3780                &routes.selected,
3781                &routes.token_rows,
3782            )?;
3783            let down_rows = (0..local_pairs).collect::<Vec<_>>();
3784            let down_csr = ExpertCsr::from_pair_rows(
3785                local_experts,
3786                local_pairs,
3787                &routes.selected,
3788                &down_rows,
3789            )?;
3790            schedules.push(Some(StepGroupedExpertOwnerSchedule {
3791                global_pairs: routes.global_pairs,
3792                route_csr,
3793                down_csr,
3794            }));
3795        }
3796        Ok((pairs, next_generation, schedules))
3797    }
3798
3799    fn commit_step_grouped_expert_parallel_refresh(
3800        &self,
3801        plan: &mut PreparedStepGroupedExpertParallelGate,
3802        tokens: usize,
3803        pairs: usize,
3804        next_generation: u64,
3805        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
3806    ) -> Result<(), Box<dyn std::error::Error>> {
3807        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
3808            let engine = &self.ranks[owner.rank];
3809            let _main = engine.gpu.enter_main()?;
3810            if let Some(schedule) = schedule {
3811                owner.route_csr.refresh(engine, &schedule.route_csr)?;
3812                owner.down_csr.refresh(engine, &schedule.down_csr)?;
3813                owner.global_pairs = schedule.global_pairs;
3814            } else {
3815                owner.route_csr.clear();
3816                owner.down_csr.clear();
3817                owner.global_pairs.clear();
3818            }
3819        }
3820        plan.tokens = tokens;
3821        plan.pairs = pairs;
3822        plan.generation = next_generation;
3823        plan.ready = true;
3824        Ok(())
3825    }
3826
3827    pub fn refresh_step_grouped_expert_parallel_gate(
3828        &self,
3829        experts: &ResidentExpertParallel,
3830        plan: &mut PreparedStepGroupedExpertParallelGate,
3831        input: &[f32],
3832        tokens: usize,
3833        selected: &[usize],
3834    ) -> Result<(), Box<dyn std::error::Error>> {
3835        validate_activations(input, tokens, experts.input_width)?;
3836        let (pairs, next_generation, schedules) =
3837            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
3838
3839        plan.ready = false;
3840        plan.executed_generation = None;
3841        {
3842            let root = &self.ranks[0];
3843            let _main = root.gpu.enter_main()?;
3844            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
3845            root.stream().memcpy_htod(input, &mut destination)?;
3846            root.stream().synchronize()?;
3847        }
3848        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
3849        let root_input = &root_inputs[0];
3850        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
3851            let engine = &self.ranks[rank + 1];
3852            let _main = engine.gpu.enter_main()?;
3853            let mut destination = peer_input.slice_mut(0..input.len());
3854            engine
3855                .stream()
3856                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
3857        }
3858        self.commit_step_grouped_expert_parallel_refresh(
3859            plan,
3860            tokens,
3861            pairs,
3862            next_generation,
3863            schedules,
3864        )
3865    }
3866
3867    /// Refresh routes and inputs from an already-resident rank-zero activation.
3868    ///
3869    /// The caller must order the source producer before this call. The root copy is completed
3870    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
3871    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
3872        &self,
3873        experts: &ResidentExpertParallel,
3874        plan: &mut PreparedStepGroupedExpertParallelGate,
3875        input: &CudaSlice<f32>,
3876        tokens: usize,
3877        selected: &[usize],
3878    ) -> Result<(), Box<dyn std::error::Error>> {
3879        let input_values = tokens
3880            .checked_mul(experts.input_width)
3881            .ok_or("Step owner-grouped FP8 input size overflow")?;
3882        let root = self
3883            .ranks
3884            .first()
3885            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
3886        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
3887            return Err(format!(
3888                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
3889                 device {}",
3890                input.len(),
3891                input.ordinal(),
3892                input_values,
3893                root.ctx().ordinal(),
3894            )
3895            .into());
3896        }
3897        let (pairs, next_generation, schedules) =
3898            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
3899
3900        plan.ready = false;
3901        plan.executed_generation = None;
3902        {
3903            let _main = root.gpu.enter_main()?;
3904            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
3905            root.stream()
3906                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
3907            root.stream().synchronize()?;
3908        }
3909        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
3910        let root_input = &root_inputs[0];
3911        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
3912            let engine = &self.ranks[rank + 1];
3913            let _main = engine.gpu.enter_main()?;
3914            let mut destination = peer_input.slice_mut(0..input_values);
3915            engine
3916                .stream()
3917                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
3918        }
3919        self.commit_step_grouped_expert_parallel_refresh(
3920            plan,
3921            tokens,
3922            pairs,
3923            next_generation,
3924            schedules,
3925        )
3926    }
3927
3928    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
3929    ///
3930    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
3931    /// and combine result, so callers must refresh combine metadata before executing again.
3932    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
3933        &self,
3934        experts: &ResidentExpertParallel,
3935        plan: &mut PreparedStepGroupedExpertParallelGate,
3936        input: &ResidentReplicatedDeviceRows,
3937    ) -> Result<(), Box<dyn std::error::Error>> {
3938        validate_ep_residency(&self.ranks, experts)?;
3939        validate_replicated_device_rows(&self.ranks, input)?;
3940        if !plan.ready
3941            || input.tokens != plan.tokens
3942            || input.width != plan.input_width
3943            || input.tokens > plan.max_tokens
3944            || plan.rank_inputs.len() != self.ranks.len()
3945            || plan.owners.len() != self.ranks.len()
3946            || plan.input_width != experts.input_width
3947            || plan.expert_width != experts.expert_width
3948        {
3949            return Err("Step owner-grouped replicated input geometry changed".into());
3950        }
3951        let values = input
3952            .tokens
3953            .checked_mul(input.width)
3954            .ok_or("Step owner-grouped replicated input size overflow")?;
3955        let next_generation = plan
3956            .generation
3957            .checked_add(1)
3958            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
3959        plan.ready = false;
3960        plan.executed_generation = None;
3961        for (rank, engine) in self.ranks.iter().enumerate() {
3962            let _main = engine.gpu.enter_main()?;
3963            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
3964            engine
3965                .stream()
3966                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
3967        }
3968        plan.generation = next_generation;
3969        plan.ready = true;
3970        Ok(())
3971    }
3972
3973    pub fn execute_step_grouped_expert_parallel_gate(
3974        &self,
3975        experts: &ResidentExpertParallel,
3976        plan: &mut PreparedStepGroupedExpertParallelGate,
3977    ) -> Result<(), Box<dyn std::error::Error>> {
3978        validate_ep_residency(&self.ranks, experts)?;
3979        if !plan.ready
3980            || plan.rank_inputs.len() != self.ranks.len()
3981            || plan.owners.len() != self.ranks.len()
3982            || plan.input_width != experts.input_width
3983            || plan.expert_width != experts.expert_width
3984        {
3985            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
3986        }
3987        plan.executed_generation = None;
3988
3989        for owner in &mut plan.owners {
3990            if owner.global_pairs.is_empty() {
3991                continue;
3992            }
3993            let engine = &self.ranks[owner.rank];
3994            let bank = &experts.ranks[owner.rank];
3995            let _main = engine.gpu.enter_main()?;
3996            let local_pairs = owner.global_pairs.len();
3997            owner.gate_workspace.quantize_for_shape(
3998                engine,
3999                &plan.rank_inputs[owner.rank],
4000                plan.tokens,
4001                local_pairs,
4002            )?;
4003            owner.gate_workspace.project(
4004                engine,
4005                &bank.gate.codes,
4006                &bank.gate.scales,
4007                &owner.route_csr,
4008                bank.gate.code_stride,
4009                bank.gate.scale_stride,
4010                1.0,
4011            )?;
4012            owner.up_workspace.quantize_for_shape(
4013                engine,
4014                &plan.rank_inputs[owner.rank],
4015                plan.tokens,
4016                local_pairs,
4017            )?;
4018            owner.up_workspace.project(
4019                engine,
4020                &bank.up.codes,
4021                &bank.up.scales,
4022                &owner.route_csr,
4023                bank.up.code_stride,
4024                bank.up.scale_stride,
4025                1.0,
4026            )?;
4027        }
4028        for owner in &mut plan.owners {
4029            if owner.global_pairs.is_empty() {
4030                continue;
4031            }
4032            let engine = &self.ranks[owner.rank];
4033            let _main = engine.gpu.enter_main()?;
4034            let values = owner.global_pairs.len() * plan.expert_width;
4035            if let Some(limit) = plan.activation_limit {
4036                engine.silu_clamped_mul_host_expf(
4037                    owner.gate_workspace.output(),
4038                    owner.up_workspace.output(),
4039                    limit,
4040                    &mut owner.activation,
4041                    values,
4042                )?;
4043            } else {
4044                engine.silu_mul_host_expf(
4045                    owner.gate_workspace.output(),
4046                    owner.up_workspace.output(),
4047                    &mut owner.activation,
4048                    values,
4049                )?;
4050            }
4051        }
4052        for owner in &mut plan.owners {
4053            if owner.global_pairs.is_empty() {
4054                continue;
4055            }
4056            let engine = &self.ranks[owner.rank];
4057            let bank = &experts.ranks[owner.rank];
4058            let _main = engine.gpu.enter_main()?;
4059            let local_pairs = owner.global_pairs.len();
4060            owner.down_workspace.quantize_for_shape(
4061                engine,
4062                &owner.activation,
4063                local_pairs,
4064                local_pairs,
4065            )?;
4066            owner.down_workspace.project(
4067                engine,
4068                &bank.down.codes,
4069                &bank.down.scales,
4070                &owner.down_csr,
4071                bank.down.code_stride,
4072                bank.down.scale_stride,
4073                1.0,
4074            )?;
4075        }
4076        plan.executed_generation = Some(plan.generation);
4077        Ok(())
4078    }
4079
4080    pub fn collect_step_grouped_expert_parallel_gate(
4081        &self,
4082        plan: &PreparedStepGroupedExpertParallelGate,
4083    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4084        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4085            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4086        }
4087        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4088        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4089        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4090        for owner in &plan.owners {
4091            if owner.global_pairs.is_empty() {
4092                continue;
4093            }
4094            let engine = &self.ranks[owner.rank];
4095            let _main = engine.gpu.enter_main()?;
4096            let owner_gate = engine.dtoh_view(
4097                &owner
4098                    .gate_workspace
4099                    .output()
4100                    .slice(0..owner.gate_workspace.output_len()),
4101            )?;
4102            let owner_up = engine.dtoh_view(
4103                &owner
4104                    .up_workspace
4105                    .output()
4106                    .slice(0..owner.up_workspace.output_len()),
4107            )?;
4108            let owner_down = engine.dtoh_view(
4109                &owner
4110                    .down_workspace
4111                    .output()
4112                    .slice(0..owner.down_workspace.output_len()),
4113            )?;
4114            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4115                let local_expert = local_pair * plan.expert_width;
4116                let global_expert = global_pair * plan.expert_width;
4117                gate[global_expert..global_expert + plan.expert_width]
4118                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4119                up[global_expert..global_expert + plan.expert_width]
4120                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4121
4122                let local_hidden = local_pair * plan.input_width;
4123                let global_hidden = global_pair * plan.input_width;
4124                down[global_hidden..global_hidden + plan.input_width]
4125                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4126            }
4127        }
4128        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4129    }
4130
4131    pub fn run_step_grouped_expert_parallel_gate(
4132        &self,
4133        experts: &ResidentExpertParallel,
4134        plan: &mut PreparedStepGroupedExpertParallelGate,
4135    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4136        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4137        self.collect_step_grouped_expert_parallel_gate(plan)
4138    }
4139
4140    pub fn prepare_step_grouped_expert_parallel_combine(
4141        &self,
4142        plan: &PreparedStepGroupedExpertParallelGate,
4143        route_weights: &[f32],
4144    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4145        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4146            return Err(
4147                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4148            );
4149        }
4150        let owner_pairs = plan
4151            .owners
4152            .iter()
4153            .map(|owner| owner.global_pairs.as_slice())
4154            .collect::<Vec<_>>();
4155        let shape = validate_weighted_route_combine(
4156            plan.input_width,
4157            STEP_GROUPED_FP8_TOP_K,
4158            plan.max_tokens,
4159            plan.tokens,
4160            &owner_pairs,
4161            route_weights,
4162        )?;
4163        if shape.max_pairs != plan.max_pairs {
4164            return Err(format!(
4165                "Step owner-grouped combine capacity {} != projection capacity {}",
4166                shape.max_pairs, plan.max_pairs
4167            )
4168            .into());
4169        }
4170        let root = self
4171            .ranks
4172            .first()
4173            .ok_or("Step owner-grouped combine has no root rank")?;
4174        let slot_values = shape
4175            .max_pairs
4176            .checked_mul(plan.input_width)
4177            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4178        let output_values = plan
4179            .max_tokens
4180            .checked_mul(plan.input_width)
4181            .ok_or("Step owner-grouped combine output capacity overflow")?;
4182        let (root_device, owners, peer_staging, slots, weights, output) = {
4183            let _main = root.gpu.enter_main()?;
4184            let mut owners = Vec::with_capacity(plan.owners.len());
4185            for _ in &plan.owners {
4186                owners.push(PreparedPeerWeightedRouteOwner {
4187                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4188                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4189                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4190                    active_pairs: 0,
4191                });
4192            }
4193            (
4194                root.ctx().ordinal(),
4195                owners,
4196                root.uninit(slot_values)?,
4197                root.uninit(slot_values)?,
4198                root.uninit(shape.max_pairs)?,
4199                root.uninit(output_values)?,
4200            )
4201        };
4202        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4203        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4204        for engine in self.ranks.iter().skip(1) {
4205            let _main = engine.gpu.enter_main()?;
4206            peer_devices.push(engine.ctx().ordinal());
4207            peer_outputs.push(engine.uninit(output_values)?);
4208        }
4209        let mut combine = PreparedPeerWeightedRouteCombine {
4210            root_device,
4211            owners,
4212            peer_staging,
4213            slots,
4214            weights,
4215            output,
4216            peer_devices,
4217            peer_outputs,
4218            width: plan.input_width,
4219            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4220            max_tokens: plan.max_tokens,
4221            max_pairs: shape.max_pairs,
4222            tokens: 0,
4223            pairs: 0,
4224            projection_generation: 0,
4225            output_generation: None,
4226            broadcast_generation: None,
4227            ready: false,
4228        };
4229        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4230        Ok(combine)
4231    }
4232
4233    pub fn refresh_step_grouped_expert_parallel_combine(
4234        &self,
4235        plan: &PreparedStepGroupedExpertParallelGate,
4236        combine: &mut PreparedPeerWeightedRouteCombine,
4237        route_weights: &[f32],
4238    ) -> Result<(), Box<dyn std::error::Error>> {
4239        let output_capacity = combine
4240            .max_tokens
4241            .checked_mul(combine.width)
4242            .ok_or("Step owner-grouped combine output capacity overflow")?;
4243        if !plan.ready
4244            || combine.owners.len() != plan.owners.len()
4245            || combine.peer_devices.len() + 1 != self.ranks.len()
4246            || combine.peer_outputs.len() + 1 != self.ranks.len()
4247            || combine.width != plan.input_width
4248            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4249            || combine.max_tokens != plan.max_tokens
4250            || combine.max_pairs != plan.max_pairs
4251            || combine.output.len() < output_capacity
4252            || combine
4253                .peer_outputs
4254                .iter()
4255                .any(|output| output.len() < output_capacity)
4256        {
4257            return Err("Step owner-grouped combine/projection geometry changed".into());
4258        }
4259        if self
4260            .ranks
4261            .iter()
4262            .skip(1)
4263            .zip(&combine.peer_devices)
4264            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4265        {
4266            return Err("Step owner-grouped combine peer devices changed".into());
4267        }
4268        let owner_pairs = plan
4269            .owners
4270            .iter()
4271            .map(|owner| owner.global_pairs.as_slice())
4272            .collect::<Vec<_>>();
4273        let shape = validate_weighted_route_combine(
4274            combine.width,
4275            combine.experts_per_token,
4276            combine.max_tokens,
4277            plan.tokens,
4278            &owner_pairs,
4279            route_weights,
4280        )?;
4281        if shape.max_pairs != combine.max_pairs {
4282            return Err("Step owner-grouped combine capacity changed during refresh".into());
4283        }
4284        let metadata = owner_pairs
4285            .iter()
4286            .map(|pairs| {
4287                let token_rows = pairs
4288                    .iter()
4289                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4290                    .collect::<Vec<_>>();
4291                let slots = pairs
4292                    .iter()
4293                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4294                    .collect::<Vec<_>>();
4295                let weights = pairs
4296                    .iter()
4297                    .map(|&pair| route_weights[pair])
4298                    .collect::<Vec<_>>();
4299                (token_rows, slots, weights)
4300            })
4301            .collect::<Vec<_>>();
4302
4303        combine.ready = false;
4304        combine.output_generation = None;
4305        combine.broadcast_generation = None;
4306        let root = self
4307            .ranks
4308            .first()
4309            .ok_or("Step owner-grouped combine has no root rank")?;
4310        let _main = root.gpu.enter_main()?;
4311        if root.ctx().ordinal() != combine.root_device {
4312            return Err(format!(
4313                "Step owner-grouped combine root device changed {} != {}",
4314                root.ctx().ordinal(),
4315                combine.root_device
4316            )
4317            .into());
4318        }
4319        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4320            if token_rows.is_empty() {
4321                owner.active_pairs = 0;
4322                continue;
4323            }
4324            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4325            root.htod_i32_into(&mut owner.slots, &slots)?;
4326            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4327            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4328            owner.active_pairs = token_rows.len();
4329        }
4330        combine.tokens = plan.tokens;
4331        combine.pairs = shape.pairs;
4332        combine.projection_generation = plan.generation;
4333        combine.ready = true;
4334        Ok(())
4335    }
4336
4337    pub fn execute_step_grouped_expert_parallel_combine(
4338        &self,
4339        plan: &PreparedStepGroupedExpertParallelGate,
4340        combine: &mut PreparedPeerWeightedRouteCombine,
4341    ) -> Result<(), Box<dyn std::error::Error>> {
4342        if !plan.ready
4343            || plan.executed_generation != Some(plan.generation)
4344            || !combine.ready
4345            || combine.tokens != plan.tokens
4346            || combine.pairs != plan.pairs
4347            || combine.width != plan.input_width
4348            || combine.owners.len() != plan.owners.len()
4349            || combine.projection_generation != plan.generation
4350        {
4351            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4352        }
4353        combine.output_generation = None;
4354        combine.broadcast_generation = None;
4355        for owner in &plan.owners {
4356            if owner.rank == 0 || owner.global_pairs.is_empty() {
4357                continue;
4358            }
4359            let engine = &self.ranks[owner.rank];
4360            let _main = engine.gpu.enter_main()?;
4361            engine.stream().synchronize()?;
4362        }
4363        let root = self
4364            .ranks
4365            .first()
4366            .ok_or("Step owner-grouped combine has no root rank")?;
4367        let _main = root.gpu.enter_main()?;
4368        if root.ctx().ordinal() != combine.root_device {
4369            return Err("Step owner-grouped combine is not resident on the root device".into());
4370        }
4371        for (index, owner) in plan.owners.iter().enumerate() {
4372            let metadata = &combine.owners[index];
4373            if owner.global_pairs.len() != metadata.active_pairs {
4374                return Err(format!(
4375                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4376                    owner.global_pairs.len(),
4377                    metadata.active_pairs
4378                )
4379                .into());
4380            }
4381            if metadata.active_pairs == 0 {
4382                continue;
4383            }
4384            let values = metadata
4385                .active_pairs
4386                .checked_mul(combine.width)
4387                .ok_or("Step owner-grouped combine peer value count overflow")?;
4388            if owner.rank == 0 {
4389                root.scatter_slot(
4390                    owner.down_workspace.output(),
4391                    &metadata.token_rows,
4392                    &metadata.slots,
4393                    &metadata.weights,
4394                    &mut combine.slots,
4395                    &mut combine.weights,
4396                    combine.width,
4397                    combine.experts_per_token,
4398                    metadata.active_pairs,
4399                )?;
4400            } else {
4401                let source = owner.down_workspace.output().slice(0..values);
4402                let mut destination = combine.peer_staging.slice_mut(0..values);
4403                root.stream().memcpy_dtod(&source, &mut destination)?;
4404                root.scatter_slot(
4405                    &combine.peer_staging,
4406                    &metadata.token_rows,
4407                    &metadata.slots,
4408                    &metadata.weights,
4409                    &mut combine.slots,
4410                    &mut combine.weights,
4411                    combine.width,
4412                    combine.experts_per_token,
4413                    metadata.active_pairs,
4414                )?;
4415            }
4416        }
4417        root.reduce_slots_host(
4418            &combine.slots,
4419            &combine.weights,
4420            &mut combine.output,
4421            combine.width,
4422            combine.experts_per_token,
4423            combine.tokens,
4424        )?;
4425        combine.output_generation = Some(plan.generation);
4426        Ok(())
4427    }
4428
4429    pub fn collect_step_grouped_expert_parallel_combine(
4430        &self,
4431        plan: &PreparedStepGroupedExpertParallelGate,
4432        combine: &PreparedPeerWeightedRouteCombine,
4433    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4434        if !plan.ready
4435            || combine.output_generation != Some(plan.generation)
4436            || combine.projection_generation != plan.generation
4437        {
4438            return Err("Step owner-grouped combine output is stale or has not executed".into());
4439        }
4440        let root = self
4441            .ranks
4442            .first()
4443            .ok_or("Step owner-grouped combine has no root rank")?;
4444        let _main = root.gpu.enter_main()?;
4445        if root.ctx().ordinal() != combine.root_device {
4446            return Err("Step owner-grouped combine is not resident on the root device".into());
4447        }
4448        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4449    }
4450
4451    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4452    ///
4453    /// The persistent combine buffer remains reusable by the next route generation; the returned
4454    /// allocation follows the serving runtime's ordinary transient-output ownership.
4455    pub fn copy_step_grouped_expert_parallel_combine_root(
4456        &self,
4457        plan: &PreparedStepGroupedExpertParallelGate,
4458        combine: &PreparedPeerWeightedRouteCombine,
4459        destination: &Engine,
4460    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4461        if !plan.ready
4462            || combine.output_generation != Some(plan.generation)
4463            || combine.projection_generation != plan.generation
4464        {
4465            return Err("Step owner-grouped combine output is stale or has not executed".into());
4466        }
4467        let root = self
4468            .ranks
4469            .first()
4470            .ok_or("Step owner-grouped combine has no root rank")?;
4471        if root.ctx().ordinal() != combine.root_device
4472            || destination.ctx().ordinal() != combine.root_device
4473        {
4474            return Err(format!(
4475                "Step owner-grouped combine root/destination devices {}/{} != {}",
4476                root.ctx().ordinal(),
4477                destination.ctx().ordinal(),
4478                combine.root_device,
4479            )
4480            .into());
4481        }
4482        let values = combine
4483            .tokens
4484            .checked_mul(combine.width)
4485            .ok_or("Step owner-grouped combine copy size overflow")?;
4486        {
4487            let _main = root.gpu.enter_main()?;
4488            root.stream().synchronize()?;
4489        }
4490        let _main = destination.gpu.enter_main()?;
4491        let mut output = destination.uninit(values)?;
4492        destination
4493            .stream()
4494            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4495        Ok(output)
4496    }
4497
4498    pub fn broadcast_step_grouped_expert_parallel_combine(
4499        &self,
4500        plan: &PreparedStepGroupedExpertParallelGate,
4501        combine: &mut PreparedPeerWeightedRouteCombine,
4502    ) -> Result<(), Box<dyn std::error::Error>> {
4503        if !plan.ready
4504            || combine.output_generation != Some(plan.generation)
4505            || combine.projection_generation != plan.generation
4506            || combine.peer_devices.len() + 1 != self.ranks.len()
4507            || combine.peer_outputs.len() + 1 != self.ranks.len()
4508        {
4509            return Err("Step owner-grouped combine output cannot be broadcast".into());
4510        }
4511        combine.broadcast_generation = None;
4512        let values = combine
4513            .tokens
4514            .checked_mul(combine.width)
4515            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4516        {
4517            let root = self
4518                .ranks
4519                .first()
4520                .ok_or("Step owner-grouped combine has no root rank")?;
4521            let _main = root.gpu.enter_main()?;
4522            if root.ctx().ordinal() != combine.root_device {
4523                return Err("Step owner-grouped combine root device changed".into());
4524            }
4525            root.stream().synchronize()?;
4526        }
4527        let source = &combine.output;
4528        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4529            let engine = &self.ranks[index + 1];
4530            let _main = engine.gpu.enter_main()?;
4531            if engine.ctx().ordinal() != combine.peer_devices[index] {
4532                return Err(format!(
4533                    "Step owner-grouped combine peer {} device changed",
4534                    index + 1
4535                )
4536                .into());
4537            }
4538            let mut destination = destination_buffer.slice_mut(0..values);
4539            engine
4540                .stream()
4541                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4542        }
4543        combine.broadcast_generation = Some(plan.generation);
4544        Ok(())
4545    }
4546
4547    pub fn collect_step_grouped_expert_parallel_broadcast(
4548        &self,
4549        plan: &PreparedStepGroupedExpertParallelGate,
4550        combine: &PreparedPeerWeightedRouteCombine,
4551    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4552        if !plan.ready
4553            || combine.output_generation != Some(plan.generation)
4554            || combine.broadcast_generation != Some(plan.generation)
4555            || combine.peer_outputs.len() + 1 != self.ranks.len()
4556        {
4557            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4558        }
4559        let values = combine
4560            .tokens
4561            .checked_mul(combine.width)
4562            .ok_or("Step owner-grouped combine collection size overflow")?;
4563        let mut outputs = Vec::with_capacity(self.ranks.len());
4564        {
4565            let root = &self.ranks[0];
4566            let _main = root.gpu.enter_main()?;
4567            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4568        }
4569        for (index, output) in combine.peer_outputs.iter().enumerate() {
4570            let engine = &self.ranks[index + 1];
4571            let _main = engine.gpu.enter_main()?;
4572            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4573        }
4574        Ok(outputs)
4575    }
4576
4577    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4578    pub fn finish_step_grouped_expert_parallel_layer(
4579        &self,
4580        plan: &PreparedStepGroupedExpertParallelGate,
4581        combine: &PreparedPeerWeightedRouteCombine,
4582        shared: &ResidentReplicatedDeviceRows,
4583        residual: &ResidentReplicatedDeviceRows,
4584    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4585        validate_replicated_device_rows(&self.ranks, shared)?;
4586        validate_replicated_device_rows(&self.ranks, residual)?;
4587        if !plan.ready
4588            || plan.executed_generation != Some(plan.generation)
4589            || combine.output_generation != Some(plan.generation)
4590            || combine.broadcast_generation != Some(plan.generation)
4591            || combine.projection_generation != plan.generation
4592            || combine.peer_outputs.len() + 1 != self.ranks.len()
4593            || shared.tokens != combine.tokens
4594            || residual.tokens != combine.tokens
4595            || shared.width != combine.width
4596            || residual.width != combine.width
4597        {
4598            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4599        }
4600        let values = combine
4601            .tokens
4602            .checked_mul(combine.width)
4603            .ok_or("Step full-layer output size overflow")?;
4604        let mut ranks = Vec::with_capacity(self.ranks.len());
4605        for rank in 0..self.ranks.len() {
4606            let engine = &self.ranks[rank];
4607            let _main = engine.gpu.enter_main()?;
4608            let routed = if rank == 0 {
4609                &combine.output
4610            } else {
4611                &combine.peer_outputs[rank - 1]
4612            };
4613            let mut ffn = engine.uninit(values)?;
4614            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
4615            let mut output = engine.uninit(values)?;
4616            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
4617            ranks.push(output);
4618        }
4619        Ok(ResidentReplicatedDeviceRows {
4620            ranks,
4621            tokens: combine.tokens,
4622            width: combine.width,
4623        })
4624    }
4625
4626    pub fn run_step_grouped_expert_parallel_combine(
4627        &self,
4628        plan: &PreparedStepGroupedExpertParallelGate,
4629        combine: &mut PreparedPeerWeightedRouteCombine,
4630    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4631        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
4632        self.collect_step_grouped_expert_parallel_combine(plan, combine)
4633    }
4634
4635    pub fn upload_tensor_parallel(
4636        &self,
4637        gate: E4m3ExpertBank<'_>,
4638        up: E4m3ExpertBank<'_>,
4639        down: E4m3ExpertBank<'_>,
4640    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
4641        gate.validate()?;
4642        up.validate()?;
4643        down.validate()?;
4644        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
4645            return Err("TP gate/up/down expert counts differ".into());
4646        }
4647        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4648            return Err("TP gate/up dimensions differ".into());
4649        }
4650        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4651            return Err(format!(
4652                "TP down {}x{} does not invert gate/up {}x{}",
4653                down.out_features, down.in_features, gate.out_features, gate.in_features
4654            )
4655            .into());
4656        }
4657        let tp = self.ranks.len();
4658        validate_column_bank_shape(gate, tp)?;
4659        validate_column_bank_shape(up, tp)?;
4660        validate_row_bank_shape(down, tp)?;
4661
4662        let mut gate_ranks = Vec::with_capacity(tp);
4663        let mut up_ranks = Vec::with_capacity(tp);
4664        let mut down_ranks = Vec::with_capacity(tp);
4665        for (rank, engine) in self.ranks.iter().enumerate() {
4666            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
4667            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
4668            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
4669        }
4670        Ok(ResidentTensorParallel {
4671            bank: ResidentTpExpertBank {
4672                gate: gate_ranks,
4673                up: up_ranks,
4674                down: down_ranks,
4675                expert_count: gate.expert_count,
4676                input_width: gate.in_features,
4677                expert_width: gate.out_features,
4678            },
4679        })
4680    }
4681
4682    pub fn run_tensor_parallel_routes(
4683        &self,
4684        experts: &ResidentTensorParallel,
4685        input: &[f32],
4686        tokens: usize,
4687        selected: &[usize],
4688        route_weights: &[f32],
4689        experts_per_token: usize,
4690    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4691        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
4692        validate_activations(input, tokens, experts.bank.input_width)?;
4693        let pairs = tokens
4694            .checked_mul(experts_per_token)
4695            .ok_or("TP route count overflow")?;
4696        if selected.len() != pairs || route_weights.len() != pairs {
4697            return Err(format!(
4698                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
4699                 {experts_per_token} ({pairs})",
4700                selected.len(),
4701                route_weights.len(),
4702            )
4703            .into());
4704        }
4705        if !route_weights.iter().all(|weight| weight.is_finite()) {
4706            return Err("TP route weights contain a non-finite value".into());
4707        }
4708
4709        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
4710        for token in 0..tokens {
4711            let input_row =
4712                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
4713            for slot in 0..experts_per_token {
4714                let pair = token * experts_per_token + slot;
4715                let expert = selected[pair];
4716                if expert >= experts.bank.expert_count {
4717                    return Err(format!(
4718                        "TP selected expert {expert} outside 0..{}",
4719                        experts.bank.expert_count
4720                    )
4721                    .into());
4722                }
4723                let down = if self.native_p2p {
4724                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
4725                } else {
4726                    let gate =
4727                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
4728                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
4729                    let activated: Vec<f32> = gate
4730                        .iter()
4731                        .zip(&up)
4732                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4733                        .collect();
4734                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
4735                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
4736                };
4737                let weight = route_weights[pair];
4738                for (sum, value) in output
4739                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
4740                    .iter_mut()
4741                    .zip(down)
4742                {
4743                    *sum += weight * value;
4744                }
4745            }
4746        }
4747        Ok(output)
4748    }
4749
4750    fn run_column_bank_expert(
4751        &self,
4752        ranks: &[ResidentE4m3ExpertBankRank],
4753        expert: usize,
4754        input: &[f32],
4755    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4756        let local_out = ranks
4757            .first()
4758            .ok_or("TP column bank has no ranks")?
4759            .out_features;
4760        let mut gathered = vec![0.0f32; local_out * ranks.len()];
4761        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
4762            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
4763            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
4764        }
4765        Ok(gathered)
4766    }
4767
4768    fn run_row_bank_expert(
4769        &self,
4770        ranks: &[ResidentE4m3ExpertBankRank],
4771        expert: usize,
4772        input: &[f32],
4773    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4774        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
4775        if input.len() != local_in * ranks.len() {
4776            return Err(format!(
4777                "TP row input {} != {} ranks x {local_in}",
4778                input.len(),
4779                ranks.len()
4780            )
4781            .into());
4782        }
4783        let out_features = ranks[0].out_features;
4784        let mut reduced = vec![0.0f32; out_features];
4785        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
4786            let blocks = bank
4787                .k_blocks
4788                .ok_or("TP row bank is not packed in native K-block order")?;
4789            if blocks * FP8_BLOCK != local_in {
4790                return Err(format!(
4791                    "TP row bank has {blocks} blocks but local input width is {local_in}"
4792                )
4793                .into());
4794            }
4795            for block in 0..blocks {
4796                let global_start = rank * local_in + block * FP8_BLOCK;
4797                let partial = run_resident_bank_expert_block(
4798                    engine,
4799                    bank,
4800                    expert,
4801                    block,
4802                    &input[global_start..global_start + FP8_BLOCK],
4803                )?;
4804                for (sum, value) in reduced.iter_mut().zip(partial) {
4805                    *sum += value;
4806                }
4807            }
4808        }
4809        Ok(reduced)
4810    }
4811
4812    fn run_tensor_parallel_expert_native(
4813        &self,
4814        bank: &ResidentTpExpertBank,
4815        expert: usize,
4816        input: &[f32],
4817    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4818        if !self.native_p2p || self.ranks.len() < 2 {
4819            return Err("native TP expert execution requires at least two P2P ranks".into());
4820        }
4821        let local_out = bank
4822            .gate
4823            .first()
4824            .ok_or("native TP gate bank has no ranks")?
4825            .out_features;
4826        if local_out * self.ranks.len() != bank.expert_width {
4827            return Err(format!(
4828                "native TP gate shards {}x{local_out} != expert width {}",
4829                self.ranks.len(),
4830                bank.expert_width
4831            )
4832            .into());
4833        }
4834
4835        // The caller's routed input is already host-canonical. Upload once on rank zero, then
4836        // broadcast over peer copies so no other rank receives a host-staged duplicate.
4837        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
4838        let root_input = {
4839            let root = &self.ranks[0];
4840            let _main = root.gpu.enter_main()?;
4841            root.htod(input)?
4842        };
4843        rank_inputs.push(root_input);
4844        for engine in &self.ranks[1..] {
4845            let peer_input = {
4846                let _main = engine.gpu.enter_main()?;
4847                let mut peer_input = engine.uninit(input.len())?;
4848                engine
4849                    .stream()
4850                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
4851                peer_input
4852            };
4853            rank_inputs.push(peer_input);
4854        }
4855
4856        let mut gate_shards = Vec::with_capacity(self.ranks.len());
4857        let mut up_shards = Vec::with_capacity(self.ranks.len());
4858        for rank in 0..self.ranks.len() {
4859            gate_shards.push(run_resident_bank_expert_device(
4860                &self.ranks[rank],
4861                &bank.gate[rank],
4862                expert,
4863                &rank_inputs[rank],
4864                1,
4865            )?);
4866            up_shards.push(run_resident_bank_expert_device(
4867                &self.ranks[rank],
4868                &bank.up[rank],
4869                expert,
4870                &rank_inputs[rank],
4871                1,
4872            )?);
4873        }
4874
4875        // Preserve the established canonical activation program for the first native transport
4876        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
4877        // executes on host. A later device-activation increment must earn its own exactness gate.
4878        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
4879        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
4880        let activated = gate
4881            .iter()
4882            .zip(&up)
4883            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4884            .collect::<Vec<_>>();
4885        debug_assert_eq!(activated.len(), bank.expert_width);
4886
4887        let root_activated = {
4888            let root = &self.ranks[0];
4889            let _main = root.gpu.enter_main()?;
4890            root.htod(&activated)?
4891        };
4892        let mut rank_activated = Vec::with_capacity(self.ranks.len());
4893        for (rank, engine) in self.ranks.iter().enumerate() {
4894            let start = rank * local_out;
4895            let source = root_activated.slice(start..start + local_out);
4896            let local = {
4897                let _main = engine.gpu.enter_main()?;
4898                let mut local = engine.uninit(local_out)?;
4899                engine.stream().memcpy_dtod(&source, &mut local)?;
4900                local
4901            };
4902            rank_activated.push(local);
4903        }
4904
4905        let out_features = bank
4906            .down
4907            .first()
4908            .ok_or("native TP down bank has no ranks")?
4909            .out_features;
4910        let mut reduced = {
4911            let root = &self.ranks[0];
4912            let _main = root.gpu.enter_main()?;
4913            root.htod(&vec![0.0f32; out_features])?
4914        };
4915        let mut remote_partial_keepalive = Vec::new();
4916        for rank in 0..self.ranks.len() {
4917            let down = &bank.down[rank];
4918            let blocks = down
4919                .k_blocks
4920                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
4921            if blocks * FP8_BLOCK != local_out {
4922                return Err(format!(
4923                    "native TP rank {rank} has {blocks} blocks but local activation width is \
4924                     {local_out}"
4925                )
4926                .into());
4927            }
4928            for block in 0..blocks {
4929                let start = block * FP8_BLOCK;
4930                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
4931                let partial = run_resident_bank_expert_block_device(
4932                    &self.ranks[rank],
4933                    down,
4934                    expert,
4935                    block,
4936                    &input_block,
4937                )?;
4938                let root_partial = if rank == 0 {
4939                    partial
4940                } else {
4941                    let root = &self.ranks[0];
4942                    let _main = root.gpu.enter_main()?;
4943                    let mut peer_partial = root.uninit(out_features)?;
4944                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
4945                    remote_partial_keepalive.push(partial);
4946                    peer_partial
4947                };
4948                let next = {
4949                    let root = &self.ranks[0];
4950                    let _main = root.gpu.enter_main()?;
4951                    let mut next = root.uninit(out_features)?;
4952                    root.add(&reduced, &root_partial, &mut next, out_features)?;
4953                    next
4954                };
4955                reduced = next;
4956            }
4957        }
4958        let output = {
4959            let root = &self.ranks[0];
4960            let _main = root.gpu.enter_main()?;
4961            root.dtoh(&reduced)?
4962        };
4963        drop(remote_partial_keepalive);
4964        Ok(output)
4965    }
4966
4967    /// Gather token-major rank-local columns into one canonical root-device matrix.
4968    pub fn gather_native_column_shards_device(
4969        &self,
4970        shards: &[CudaSlice<f32>],
4971        tokens: usize,
4972        local_out: usize,
4973    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4974        let shard_len = tokens
4975            .checked_mul(local_out)
4976            .ok_or("native TP gather shard size overflow")?;
4977        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
4978            return Err("native TP gather shard geometry mismatch".into());
4979        }
4980        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
4981        // the other ranks' streams; without fencing those producers the copy can read a partial
4982        // kernel output.
4983        for engine in &self.ranks[1..] {
4984            let _main = engine.gpu.enter_main()?;
4985            engine.stream().synchronize()?;
4986        }
4987        let root = &self.ranks[0];
4988        let _main = root.gpu.enter_main()?;
4989        let global_out = shards
4990            .len()
4991            .checked_mul(local_out)
4992            .ok_or("native TP gather output width overflow")?;
4993        let gathered_len = tokens
4994            .checked_mul(global_out)
4995            .ok_or("native TP gather output size overflow")?;
4996        let mut gathered = root.uninit(gathered_len)?;
4997        if self.bulk_p2p {
4998            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
4999            if shards.len() > 1 {
5000                let mut staging = root.uninit(shard_len)?;
5001                for (rank, shard) in shards.iter().enumerate().skip(1) {
5002                    root.stream().memcpy_dtod(shard, &mut staging)?;
5003                    root.place_rows_strided(
5004                        &staging,
5005                        &mut gathered,
5006                        local_out,
5007                        tokens,
5008                        global_out,
5009                        rank * local_out,
5010                    )?;
5011                }
5012            }
5013        } else {
5014            for token in 0..tokens {
5015                for (rank, shard) in shards.iter().enumerate() {
5016                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5017                    let start = token * global_out + rank * local_out;
5018                    let mut destination = gathered.slice_mut(start..start + local_out);
5019                    root.stream().memcpy_dtod(&source, &mut destination)?;
5020                }
5021            }
5022        }
5023        Ok(gathered)
5024    }
5025
5026    pub fn gather_native_column_shards(
5027        &self,
5028        shards: &[CudaSlice<f32>],
5029        tokens: usize,
5030        local_out: usize,
5031    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5032        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5033        let root = &self.ranks[0];
5034        let _main = root.gpu.enter_main()?;
5035        root.dtoh(&gathered)
5036    }
5037
5038    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5039        &self.decode_v2
5040    }
5041
5042    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5043    /// return the index of the matching one. Attention geometry varies across the trunk
5044    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5045    /// handful exist per model, never one per layer.
5046    ///
5047    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5048    /// holds per residency class, and only the mirror class has no per-call weight expansion
5049    /// to hide allocation churn behind.
5050    pub(crate) fn decode_v2_ensure(
5051        &self,
5052        e: &Engine,
5053        q_m: &ResidentBf16ColumnParallel,
5054        k_m: &ResidentBf16ColumnParallel,
5055        v_m: &ResidentBf16ColumnParallel,
5056        o_m: &ResidentStepBf16RowParallel,
5057        heads: usize,
5058    ) -> Result<usize, Box<dyn std::error::Error>> {
5059        if self.ranks.len() > 1 && !self.native_p2p {
5060            return Err("step TP decode v2 requires native P2P ranks".into());
5061        }
5062        let ranks = self.ranks.len();
5063        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5064        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5065        // traffic), so bf16 residency is accepted when that door is on.
5066        let fused_door = step_tp_qkv_fused_enabled()?;
5067        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5068            ResidentBf16Weight::F32(_) => true,
5069            ResidentBf16Weight::Bf16(_) => fused_door,
5070        };
5071        for matrix in [q_m, k_m, v_m] {
5072            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5073            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5074                return Err("step TP decode v2 QKV geometry mismatch".into());
5075            }
5076            for rank in &matrix.ranks {
5077                if !arm_ok(&rank.weight) {
5078                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5079                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5080                        .into());
5081                }
5082            }
5083        }
5084        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5085        for blocks in &o_m.ranks {
5086            for block in blocks {
5087                if !arm_ok(&block.weight) {
5088                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5089                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5090                        .into());
5091                }
5092            }
5093        }
5094        if v_m.out_features != k_m.out_features
5095            || o_m.in_features != q_m.out_features
5096            || heads == 0
5097            || heads % ranks != 0
5098        {
5099            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5100        }
5101        let local_q_dim = q_m.out_features / ranks;
5102        let local_kv_dim = k_m.out_features / ranks;
5103        let o_out = o_m.out_features;
5104        let o_block_cols = o_m.canonical_chunk_cols;
5105        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5106        if blocks_per_rank == 0
5107            || o_m
5108                .ranks
5109                .iter()
5110                .any(|blocks| blocks.len() != blocks_per_rank)
5111            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5112        {
5113            return Err("step TP decode v2 O canonical block grid mismatch".into());
5114        }
5115
5116        let mut guard = self
5117            .decode_v2
5118            .lock()
5119            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5120        if let Some(index) = guard.iter().position(|ws| {
5121            ws.local_q_dim == local_q_dim
5122                && ws.local_kv_dim == local_kv_dim
5123                && ws.heads == heads
5124                && ws.o_out == o_out
5125                && ws.o_block_cols == o_block_cols
5126                && ws.blocks_per_rank == blocks_per_rank
5127                && ws.e_device == e.ctx().ordinal()
5128                && ws.q.len() == ranks
5129        }) {
5130            return Ok(index);
5131        }
5132
5133        let mut q_raw = Vec::with_capacity(ranks);
5134        let mut k_raw = Vec::with_capacity(ranks);
5135        let mut v_raw = Vec::with_capacity(ranks);
5136        let mut q = Vec::with_capacity(ranks);
5137        let mut k = Vec::with_capacity(ranks);
5138        let mut pos = Vec::with_capacity(ranks);
5139        let mut gate = Vec::with_capacity(ranks);
5140        let mut attn_out = Vec::with_capacity(ranks);
5141        let mut gated = Vec::with_capacity(ranks);
5142        let mut fuse_ctr = Vec::with_capacity(ranks);
5143        let mut o_partials = Vec::with_capacity(ranks);
5144        let mut ev_rank = Vec::with_capacity(ranks);
5145        let direct_join = oproj_direct_on();
5146        for (rank, engine) in self.ranks.iter().enumerate() {
5147            let _main = engine.gpu.enter_main()?;
5148            q_raw.push(engine.uninit(local_q_dim)?);
5149            k_raw.push(engine.uninit(local_kv_dim)?);
5150            v_raw.push(engine.uninit(local_kv_dim)?);
5151            q.push(engine.uninit(local_q_dim)?);
5152            k.push(engine.uninit(local_kv_dim)?);
5153            pos.push(engine.htod_i32(&[0])?);
5154            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5155            gate.push(engine.uninit(heads / ranks)?);
5156            attn_out.push(engine.uninit(local_q_dim)?);
5157            gated.push(engine.uninit(local_q_dim)?);
5158            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5159            for _ in 0..blocks_per_rank {
5160                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5161                // stores land there over P2P (UVA) and no pull copy is needed.
5162                if direct_join && rank != 0 {
5163                    let root = &self.ranks[0];
5164                    let _root_main = root.gpu.enter_main()?;
5165                    rank_partials.push(root.uninit(o_out)?);
5166                } else {
5167                    rank_partials.push(engine.uninit(o_out)?);
5168                }
5169            }
5170            o_partials.push(rank_partials);
5171            ev_rank.push(engine.ctx().new_event(None)?);
5172        }
5173        let root = &self.ranks[0];
5174        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5175            let _main = root.gpu.enter_main()?;
5176            (
5177                root.uninit(o_out)?,
5178                root.uninit(o_out)?,
5179                root.uninit(o_out)?,
5180                root.htod(&vec![0.0f32; o_out])?,
5181                root.uninit(ranks * local_kv_dim)?,
5182                root.uninit(ranks * local_kv_dim)?,
5183                root.ctx().new_event(None)?,
5184                root.ctx().new_event(None)?,
5185            )
5186        };
5187        let (gate_e, ev_entry) = {
5188            let _main = e.gpu.enter_main()?;
5189            (e.uninit(heads)?, e.ctx().new_event(None)?)
5190        };
5191        let raw_attn_in = Vec::new();
5192        let raw_pos = Vec::new();
5193        guard.push(StepTpDecodeV2Ws {
5194            tcol_q: Vec::new(),
5195            tcol_k: Vec::new(),
5196            tcol_v: Vec::new(),
5197            tcol_g: Vec::new(),
5198            tcol_in: Vec::new(),
5199            tcol_cap: 0,
5200            tcol_gated: Vec::new(),
5201            tcol_opart: Vec::new(),
5202            tcol_opeer: None,
5203            tcol_omix: None,
5204            tcol_ocap: 0,
5205            q_raw,
5206            k_raw,
5207            v_raw,
5208            q,
5209            k,
5210            pos,
5211            fuse_ctr,
5212            gate,
5213            attn_out,
5214            gated,
5215            o_partials,
5216            ev_rank,
5217            peer_partial,
5218            reduce_a,
5219            reduce_b,
5220            zeros,
5221            k_shadow,
5222            v_shadow,
5223            ev_refresh,
5224            ev_oproj,
5225            gate_e,
5226            attn_in: Vec::new(),
5227            h_stage: None,
5228            pos_stage: None,
5229            raw_h_stage: 0,
5230            raw_pos_stage: 0,
5231            raw_attn_in,
5232            raw_pos,
5233            raw_o_partial1: 0,
5234            raw_peer_partial: 0,
5235            raw_k1: 0,
5236            raw_v1: 0,
5237            raw_k_shadow: 0,
5238            raw_v_shadow: 0,
5239            raw_mixed_stage_e: 0,
5240            raw_reduce_a: 0,
5241            raw_shadow_stage_e: (0, 0),
5242            ev_entry,
5243            e_device: e.ctx().ordinal(),
5244            local_q_dim,
5245            local_kv_dim,
5246            heads,
5247            o_out,
5248            o_block_cols,
5249            blocks_per_rank,
5250        });
5251        eprintln!(
5252            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5253             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5254             residency=persistent ordering=evented performance_claim=false"
5255        );
5256        Ok(guard.len() - 1)
5257    }
5258
5259    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5260    /// all into the persistent workspace, ordered by events instead of host syncs.
5261    ///
5262    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5263    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5264    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5265    /// previous layer's outputs was queued on `e`'s stream before this record).
5266    #[allow(clippy::too_many_arguments)]
5267    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5268    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5269    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5270    /// column vs the t=1 kernel by construction.
5271    #[allow(clippy::too_many_arguments)]
5272    pub fn decode_v2_input_qkv_tcol(
5273        &self,
5274        ws_index: usize,
5275        e: &Engine,
5276        h_t: &CudaSlice<f32>,
5277        t: usize,
5278        q_m: &ResidentBf16ColumnParallel,
5279        k_m: &ResidentBf16ColumnParallel,
5280        v_m: &ResidentBf16ColumnParallel,
5281        gate_shards: Option<StepTpGateShards<'_>>,
5282    ) -> Result<(), Box<dyn std::error::Error>> {
5283        let ranks = self.ranks.len();
5284        let mut guard = self
5285            .decode_v2
5286            .lock()
5287            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5288        let ws = guard
5289            .get_mut(ws_index)
5290            .ok_or("step TP decode v2 workspace index out of range")?;
5291        let in_f = q_m.in_features;
5292        if h_t.len() < t * in_f || t == 0 || t > 8 {
5293            return Err("decode_v2_input_qkv_tcol geometry".into());
5294        }
5295        // Lazily arm the slabs to capacity.
5296        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5297            ws.tcol_q.clear();
5298            ws.tcol_k.clear();
5299            ws.tcol_v.clear();
5300            ws.tcol_g.clear();
5301            ws.tcol_in.clear();
5302            for engine in &self.ranks {
5303                let _m = engine.gpu.enter_main()?;
5304                ws.tcol_q.push(engine.uninit(8 * ws.local_q_dim)?);
5305                ws.tcol_k.push(engine.uninit(8 * ws.local_kv_dim)?);
5306                ws.tcol_v.push(engine.uninit(8 * ws.local_kv_dim)?);
5307                ws.tcol_g
5308                    .push(engine.uninit(8 * (ws.heads / ranks).max(1))?);
5309                ws.tcol_in.push(engine.uninit(8 * in_f)?);
5310            }
5311            ws.tcol_cap = 8;
5312        }
5313        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5314        use cudarc::driver::DevicePtr;
5315        let raw_src = {
5316            let _main = e.gpu.enter_main()?;
5317            let stream = e.stream();
5318            let (p, _g) = h_t.device_ptr(&stream);
5319            ws.ev_entry.record(&stream)?;
5320            p as u64
5321        };
5322        for rank in 0..ranks {
5323            let engine = &self.ranks[rank];
5324            let _main = engine.gpu.enter_main()?;
5325            engine.stream().wait(&ws.ev_entry)?;
5326            let raw_dst = {
5327                let stream = engine.stream();
5328                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5329                p as u64
5330            };
5331            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5332            let out_g = match &gate_shards {
5333                Some(_) => ws.heads / ranks,
5334                None => 0,
5335            };
5336            match (
5337                &q_m.ranks[rank].weight,
5338                &k_m.ranks[rank].weight,
5339                &v_m.ranks[rank].weight,
5340            ) {
5341                (
5342                    ResidentBf16Weight::Bf16(wq),
5343                    ResidentBf16Weight::Bf16(wk),
5344                    ResidentBf16Weight::Bf16(wv),
5345                ) => {
5346                    let wg = match &gate_shards {
5347                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5348                        Some(StepTpGateShards::F32(_)) => {
5349                            return Err(
5350                                "tcol verify: gate shard class does not match bf16 QKV".into()
5351                            );
5352                        }
5353                        None => wq,
5354                    };
5355                    let StepTpDecodeV2Ws {
5356                        tcol_q,
5357                        tcol_k,
5358                        tcol_v,
5359                        tcol_g,
5360                        tcol_in,
5361                        local_q_dim,
5362                        local_kv_dim,
5363                        ..
5364                    } = &mut *ws;
5365                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5366                    // column — separates driver bugs from tcol-kernel bugs.
5367                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5368                    let refk = *REFK
5369                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5370                    if refk {
5371                        let lq = *local_q_dim;
5372                        let lkv = *local_kv_dim;
5373                        let mut hrow = engine.uninit(in_f)?;
5374                        let mut qr = engine.uninit(lq)?;
5375                        let mut kr = engine.uninit(lkv)?;
5376                        let mut vr = engine.uninit(lkv)?;
5377                        let mut gr = engine.uninit(out_g.max(1))?;
5378                        for c in 0..t {
5379                            {
5380                                let mut dst = hrow.slice_mut(0..in_f);
5381                                engine.stream().memcpy_dtod(
5382                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5383                                    &mut dst,
5384                                )?;
5385                            }
5386                            engine.matvec_bf16_qkvg_into(
5387                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5388                                lq, lkv, out_g,
5389                            )?;
5390                            let stream = engine.stream();
5391                            {
5392                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5393                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5394                            }
5395                            {
5396                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5397                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5398                            }
5399                            {
5400                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5401                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5402                            }
5403                            if out_g > 0 {
5404                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5405                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5406                            }
5407                        }
5408                    } else {
5409                        engine.matvec_bf16_qkvg_tcol_into(
5410                            wq,
5411                            wk,
5412                            wv,
5413                            wg,
5414                            &tcol_in[rank],
5415                            &mut tcol_q[rank],
5416                            &mut tcol_k[rank],
5417                            &mut tcol_v[rank],
5418                            &mut tcol_g[rank],
5419                            in_f,
5420                            *local_q_dim,
5421                            *local_kv_dim,
5422                            out_g,
5423                            t,
5424                        )?;
5425                    }
5426                }
5427                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5428            }
5429        }
5430        Ok(())
5431    }
5432
5433    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5434    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5435    /// skipped — so it requires the same doors that arm dictate that finish shape.
5436    pub(crate) fn decode_v2_oproj_tcol_eligible(
5437        &self,
5438        ws: &StepTpDecodeV2Ws,
5439        o_m: &ResidentStepBf16RowParallel,
5440    ) -> bool {
5441        self.ranks.len() == 2
5442            && ws.blocks_per_rank == 4
5443            && step_tp_qkv_fused_enabled().unwrap_or(false)
5444            && no_local_shadow_on()
5445            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5446            && o_m
5447                .ranks
5448                .iter()
5449                .flatten()
5450                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5451    }
5452
5453    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
5454    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
5455    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
5456    /// every column afterwards.
5457    pub(crate) fn decode_v2_stash_gated(
5458        &self,
5459        ws: &mut StepTpDecodeV2Ws,
5460        e: &Engine,
5461        col: usize,
5462    ) -> Result<(), Box<dyn std::error::Error>> {
5463        let ranks = self.ranks.len();
5464        if col >= 8 {
5465            return Err("decode_v2_stash_gated column out of range".into());
5466        }
5467        let lq = ws.local_q_dim;
5468        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
5469            ws.tcol_gated.clear();
5470            ws.tcol_opart.clear();
5471            for engine in &self.ranks {
5472                let _m = engine.gpu.enter_main()?;
5473                ws.tcol_gated.push(engine.uninit(8 * lq)?);
5474                ws.tcol_opart.push(engine.uninit(8 * ws.o_out)?);
5475            }
5476            let root = &self.ranks[0];
5477            let _m = root.gpu.enter_main()?;
5478            ws.tcol_opeer = Some(root.uninit(8 * ws.o_out)?);
5479            ws.tcol_omix = Some(root.uninit(8 * ws.o_out)?);
5480            ws.tcol_ocap = 8;
5481        }
5482        for rank in 0..ranks {
5483            let engine = &self.ranks[rank];
5484            let _main = engine.gpu.enter_main()?;
5485            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
5486            engine
5487                .stream()
5488                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
5489            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
5490            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
5491            // Record each rank here and make e wait — same protection, no o_proj work.
5492            ws.ev_rank[rank].record(&engine.stream())?;
5493        }
5494        {
5495            let _main = e.gpu.enter_main()?;
5496            for ev in ws.ev_rank.iter() {
5497                e.stream().wait(ev)?;
5498            }
5499        }
5500        Ok(())
5501    }
5502
5503    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
5504    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
5505    /// partial slab, one elementwise slab add on the root (independent elements — each
5506    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
5507    /// lands on `e`. Returns [t, o_out] on the model engine.
5508    pub(crate) fn decode_v2_oproj_tcol(
5509        &self,
5510        ws_index: usize,
5511        e: &Engine,
5512        o_m: &ResidentStepBf16RowParallel,
5513        t: usize,
5514    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5515        let ranks = self.ranks.len();
5516        let mut guard = self
5517            .decode_v2
5518            .lock()
5519            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5520        let ws = guard
5521            .get_mut(ws_index)
5522            .ok_or("step TP decode v2 workspace index out of range")?;
5523        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 8 || ws.tcol_ocap < t {
5524            return Err("decode_v2_oproj_tcol geometry".into());
5525        }
5526        for rank in 0..ranks {
5527            let engine = &self.ranks[rank];
5528            let _main = engine.gpu.enter_main()?;
5529            let mut weights = Vec::with_capacity(4);
5530            for block in 0..4 {
5531                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
5532                    return Err("tcol o_proj requires bf16-resident O blocks".into());
5533                };
5534                weights.push(weight);
5535            }
5536            {
5537                let StepTpDecodeV2Ws {
5538                    tcol_gated,
5539                    tcol_opart,
5540                    local_q_dim,
5541                    o_block_cols,
5542                    o_out,
5543                    ..
5544                } = &mut *ws;
5545                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
5546                // kernel per column — separates choreography bugs from tcol-kernel bugs.
5547                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5548                let refk = *REFK
5549                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
5550                if refk {
5551                    let lq = *local_q_dim;
5552                    let mut xr = engine.uninit(lq)?;
5553                    let mut yr = engine.uninit(*o_out)?;
5554                    for c in 0..t {
5555                        {
5556                            let mut dst = xr.slice_mut(0..lq);
5557                            engine.stream().memcpy_dtod(
5558                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
5559                                &mut dst,
5560                            )?;
5561                        }
5562                        engine.matvec_bf16_b4_into(
5563                            [weights[0], weights[1], weights[2], weights[3]],
5564                            &xr,
5565                            &mut yr,
5566                            *o_block_cols,
5567                            *o_out,
5568                        )?;
5569                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
5570                        engine
5571                            .stream()
5572                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
5573                    }
5574                } else {
5575                    engine.matvec_bf16_b4_tcol_into(
5576                        [weights[0], weights[1], weights[2], weights[3]],
5577                        &tcol_gated[rank],
5578                        &mut tcol_opart[rank],
5579                        *o_block_cols,
5580                        *o_out,
5581                        t,
5582                    )?;
5583                }
5584            }
5585            if rank != 0 {
5586                ws.ev_rank[rank].record(&engine.stream())?;
5587            }
5588        }
5589        let root = &self.ranks[0];
5590        {
5591            let _main = root.gpu.enter_main()?;
5592            for ev in ws.ev_rank.iter().skip(1) {
5593                root.stream().wait(ev)?;
5594            }
5595            {
5596                let StepTpDecodeV2Ws {
5597                    tcol_opart,
5598                    tcol_opeer,
5599                    tcol_omix,
5600                    o_out,
5601                    ..
5602                } = &mut *ws;
5603                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
5604                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
5605                {
5606                    let mut dst = opeer.slice_mut(0..t * *o_out);
5607                    root.stream()
5608                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
5609                }
5610                // Elementwise over the whole slab: per element identical to the per-column
5611                // direct-join add (independent lanes, same operand values).
5612                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
5613            }
5614            ws.ev_oproj.record(&root.stream())?;
5615        }
5616        let _main = e.gpu.enter_main()?;
5617        e.stream().wait(&ws.ev_oproj)?;
5618        let mut out = e.uninit(t * ws.o_out)?;
5619        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
5620        e.stream().memcpy_dtod(
5621            &omix.slice(0..t * ws.o_out),
5622            &mut out.slice_mut(0..t * ws.o_out),
5623        )?;
5624        Ok(out)
5625    }
5626
5627    pub(crate) fn decode_v2_input_qkv(
5628        &self,
5629        ws: &mut StepTpDecodeV2Ws,
5630        e: &Engine,
5631        h: &CudaSlice<f32>,
5632        pos_d: &CudaSlice<i32>,
5633        gate_raw: Option<&CudaSlice<f32>>,
5634        gate_shards: Option<StepTpGateShards<'_>>,
5635        decode_input: &mut ResidentReplicatedDeviceRows,
5636        q_m: &ResidentBf16ColumnParallel,
5637        k_m: &ResidentBf16ColumnParallel,
5638        v_m: &ResidentBf16ColumnParallel,
5639        q_norm: &[CudaSlice<f32>],
5640        k_norm: &[CudaSlice<f32>],
5641        head_dim: usize,
5642        n_rot: usize,
5643        rope_base: f32,
5644        rope_freqs: &[Option<&CudaSlice<f32>>],
5645        rms_eps: f32,
5646        defer_norm_rope: bool,
5647        tcol_col: Option<usize>,
5648    ) -> Result<(), Box<dyn std::error::Error>> {
5649        let ranks = self.ranks.len();
5650        validate_replicated_device_rows(&self.ranks, decode_input)?;
5651        if decode_input.tokens != 1
5652            || decode_input.width != q_m.in_features
5653            || pos_d.len() != 1
5654            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
5655            || gate_raw.is_none() != gate_shards.is_some()
5656            || gate_shards.as_ref().is_some_and(|shards| match shards {
5657                StepTpGateShards::F32(shards) => shards.len() != ranks,
5658                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
5659            })
5660            || q_norm.len() != ranks
5661            || k_norm.len() != ranks
5662            || rope_freqs.len() != ranks
5663            || e.ctx().ordinal() != ws.e_device
5664        {
5665            return Err("step TP decode v2 input geometry mismatch".into());
5666        }
5667
5668        let qkv_fused = step_tp_qkv_fused_enabled()?;
5669        if gate_shards.is_some() && !qkv_fused {
5670            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
5671        }
5672        let values = decode_input.width;
5673        if h.len() != values {
5674            return Err(format!(
5675                "step TP decode v2 hidden width {} != replicated width {values}",
5676                h.len()
5677            )
5678            .into());
5679        }
5680
5681        if qkv_fused {
5682            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
5683            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
5684            // from the stages on its own stream — exactly the shape graph capture wraps.
5685            if ws.h_stage.is_none() {
5686                use cudarc::driver::DevicePtr;
5687                let _main = e.gpu.enter_main()?;
5688                let h_stage = e.uninit(values)?;
5689                let pos_stage = e.htod_i32(&[0])?;
5690                {
5691                    let stream = e.stream();
5692                    let (hp, _g0) = h_stage.device_ptr(&stream);
5693                    let (pp, _g1) = pos_stage.device_ptr(&stream);
5694                    ws.raw_h_stage = hp as u64;
5695                    ws.raw_pos_stage = pp as u64;
5696                }
5697                ws.h_stage = Some(h_stage);
5698                ws.pos_stage = Some(pos_stage);
5699                for rank in 0..ranks {
5700                    use cudarc::driver::DevicePtr;
5701                    let engine = &self.ranks[rank];
5702                    let _rmain = engine.gpu.enter_main()?;
5703                    let attn_in = engine.uninit(values)?;
5704                    let (dp, pp) = {
5705                        let stream = engine.stream();
5706                        let (dp, _g2) = attn_in.device_ptr(&stream);
5707                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
5708                        (dp as u64, pp as u64)
5709                    };
5710                    ws.raw_attn_in.push(dp);
5711                    ws.raw_pos.push(pp);
5712                    ws.attn_in.push(attn_in);
5713                }
5714                {
5715                    use cudarc::driver::DevicePtr;
5716                    let root = &self.ranks[0];
5717                    let _rmain = root.gpu.enter_main()?;
5718                    let stream = root.stream();
5719                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
5720                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
5721                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
5722                    ws.raw_peer_partial = a as u64;
5723                    ws.raw_k_shadow = b as u64;
5724                    ws.raw_v_shadow = c as u64;
5725                }
5726                {
5727                    use cudarc::driver::DevicePtr;
5728                    let rank1 = &self.ranks[1];
5729                    let _rmain = rank1.gpu.enter_main()?;
5730                    let stream = rank1.stream();
5731                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
5732                    let (b, _g) = ws.k[1].device_ptr(&stream);
5733                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
5734                    ws.raw_o_partial1 = a as u64;
5735                    ws.raw_k1 = b as u64;
5736                    ws.raw_v1 = c as u64;
5737                }
5738            }
5739            {
5740                let _main = e.gpu.enter_main()?;
5741                {
5742                    // (Always staged: a tcol column below the dcw floor falls back to the
5743                    // normal fused arm, which reads h through this stage.)
5744                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
5745                    let mut dst = h_stage.slice_mut(0..values);
5746                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
5747                }
5748                {
5749                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
5750                    let mut dst = pos_stage.slice_mut(0..1);
5751                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
5752                }
5753                ws.ev_entry.record(&e.stream())?;
5754            }
5755            for rank in 0..ranks {
5756                let engine = &self.ranks[rank];
5757                let _main = engine.gpu.enter_main()?;
5758                engine.stream().wait(&ws.ev_entry)?;
5759            }
5760        } else {
5761            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
5762            {
5763                let _main = e.gpu.enter_main()?;
5764                if let Some(gate_raw) = gate_raw {
5765                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
5766                    e.stream()
5767                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
5768                }
5769                ws.ev_entry.record(&e.stream())?;
5770            }
5771            {
5772                let root = &self.ranks[0];
5773                let _main = root.gpu.enter_main()?;
5774                root.stream().wait(&ws.ev_entry)?;
5775                let mut destination = decode_input.ranks[0].slice_mut(0..values);
5776                root.stream()
5777                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
5778                ws.ev_refresh.record(&root.stream())?;
5779            }
5780            for rank in 1..ranks {
5781                let engine = &self.ranks[rank];
5782                let _main = engine.gpu.enter_main()?;
5783                engine.stream().wait(&ws.ev_refresh)?;
5784                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
5785                let mut destination = peer_rows[0].slice_mut(0..values);
5786                engine
5787                    .stream()
5788                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
5789            }
5790        }
5791        for rank in 0..ranks {
5792            self.decode_v2_input_qkv_rank(
5793                ws,
5794                pos_d,
5795                decode_input,
5796                q_m,
5797                k_m,
5798                v_m,
5799                q_norm,
5800                k_norm,
5801                head_dim,
5802                n_rot,
5803                rope_base,
5804                rope_freqs,
5805                rms_eps,
5806                gate_shards.as_ref(),
5807                qkv_fused,
5808                defer_norm_rope,
5809                rank,
5810                tcol_col,
5811            )?;
5812        }
5813        Ok(())
5814    }
5815
5816    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
5817    /// per-device issue unit the whole-token graph captures on that rank's stream.
5818    #[allow(clippy::too_many_arguments)]
5819    pub(crate) fn decode_v2_input_qkv_rank(
5820        &self,
5821        ws: &mut StepTpDecodeV2Ws,
5822        pos_d: &CudaSlice<i32>,
5823        decode_input: &mut ResidentReplicatedDeviceRows,
5824        q_m: &ResidentBf16ColumnParallel,
5825        k_m: &ResidentBf16ColumnParallel,
5826        v_m: &ResidentBf16ColumnParallel,
5827        q_norm: &[CudaSlice<f32>],
5828        k_norm: &[CudaSlice<f32>],
5829        head_dim: usize,
5830        n_rot: usize,
5831        rope_base: f32,
5832        rope_freqs: &[Option<&CudaSlice<f32>>],
5833        rms_eps: f32,
5834        gate_shards: Option<&StepTpGateShards<'_>>,
5835        qkv_fused: bool,
5836        defer_norm_rope: bool,
5837        rank: usize,
5838        tcol_col: Option<usize>,
5839    ) -> Result<(), Box<dyn std::error::Error>> {
5840        let ranks = self.ranks.len();
5841        let local_heads = ws.local_q_dim / head_dim;
5842        let local_kv_heads = ws.local_kv_dim / head_dim;
5843        let engine = &self.ranks[rank];
5844        let _main = engine.gpu.enter_main()?;
5845        let ws_e_device = ws.e_device;
5846        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
5847        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
5848        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
5849        // below exactly as in the t=1 program.
5850        if qkv_fused && tcol_col.is_some() {
5851            let c = tcol_col.expect("checked");
5852            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
5853                return Err("tcol select without precompute".into());
5854            }
5855            // The select skips the matvec but NOT the position: rope/append below still
5856            // read this rank's pos buffer, which only the (skipped) stage path fills for
5857            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
5858            if engine.ctx().ordinal() != ws_e_device {
5859                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
5860            }
5861            let StepTpDecodeV2Ws {
5862                tcol_q,
5863                tcol_k,
5864                tcol_v,
5865                tcol_g,
5866                q_raw,
5867                k_raw,
5868                v_raw,
5869                gate,
5870                local_q_dim,
5871                local_kv_dim,
5872                heads,
5873                ..
5874            } = &mut *ws;
5875            let lg = *heads / ranks;
5876            let stream = engine.stream();
5877            {
5878                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
5879                stream.memcpy_dtod(
5880                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
5881                    &mut dst,
5882                )?;
5883            }
5884            {
5885                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
5886                stream.memcpy_dtod(
5887                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
5888                    &mut dst,
5889                )?;
5890            }
5891            {
5892                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
5893                stream.memcpy_dtod(
5894                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
5895                    &mut dst,
5896                )?;
5897            }
5898            if lg > 0 {
5899                let mut dst = gate[rank].slice_mut(0..lg);
5900                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
5901            }
5902            if !defer_norm_rope {
5903                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
5904                // fall through and recompute this column's QKV from the REAL h row — the
5905                // caller always passes it. The slab copies above are dead stores.
5906            } else {
5907                return Ok(());
5908            }
5909        }
5910        if qkv_fused {
5911            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
5912            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
5913            // SHARING e's device reads the stages directly — same context (probed), ordering
5914            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
5915            let same_dev = engine.ctx().ordinal() == ws.e_device;
5916            if !same_dev {
5917                raw_copy_bytes(
5918                    ws.raw_attn_in[rank],
5919                    ws.raw_h_stage,
5920                    q_m.in_features * 4,
5921                    engine,
5922                )?;
5923                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
5924            }
5925            let StepTpDecodeV2Ws {
5926                q_raw,
5927                k_raw,
5928                v_raw,
5929                gate,
5930                gate_e,
5931                attn_in,
5932                h_stage,
5933                heads,
5934                local_q_dim,
5935                local_kv_dim,
5936                ..
5937            } = &mut *ws;
5938            let input_ref: &CudaSlice<f32> = if same_dev {
5939                h_stage
5940                    .as_ref()
5941                    .ok_or("step TP decode v2 stage not armed")?
5942            } else {
5943                &attn_in[rank]
5944            };
5945            match (
5946                &q_m.ranks[rank].weight,
5947                &k_m.ranks[rank].weight,
5948                &v_m.ranks[rank].weight,
5949            ) {
5950                (
5951                    ResidentBf16Weight::F32(wq),
5952                    ResidentBf16Weight::F32(wk),
5953                    ResidentBf16Weight::F32(wv),
5954                ) => {
5955                    let (wg, out_g) = match &gate_shards {
5956                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
5957                        Some(StepTpGateShards::Bf16(_)) => {
5958                            return Err("step TP decode v2 gate shard class does not \
5959                                            match the F32 projections"
5960                                .into());
5961                        }
5962                        // out_g = 0: the kernel never reads wg; any resident buffer works.
5963                        None => (&*gate_e, 0),
5964                    };
5965                    engine.matvec_f32_qkv_into(
5966                        wq,
5967                        wk,
5968                        wv,
5969                        wg,
5970                        input_ref,
5971                        &mut q_raw[rank],
5972                        &mut k_raw[rank],
5973                        &mut v_raw[rank],
5974                        &mut gate[rank],
5975                        q_m.in_features,
5976                        *local_q_dim,
5977                        *local_kv_dim,
5978                        out_g,
5979                    )?;
5980                }
5981                (
5982                    ResidentBf16Weight::Bf16(wq),
5983                    ResidentBf16Weight::Bf16(wk),
5984                    ResidentBf16Weight::Bf16(wv),
5985                ) => {
5986                    let (wg, out_g) = match &gate_shards {
5987                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
5988                        Some(StepTpGateShards::F32(_)) => {
5989                            return Err("step TP decode v2 gate shard class does not \
5990                                            match the bf16 projections"
5991                                .into());
5992                        }
5993                        None => (wq, 0),
5994                    };
5995                    engine.matvec_bf16_qkvg_into(
5996                        wq,
5997                        wk,
5998                        wv,
5999                        wg,
6000                        input_ref,
6001                        &mut q_raw[rank],
6002                        &mut k_raw[rank],
6003                        &mut v_raw[rank],
6004                        &mut gate[rank],
6005                        q_m.in_features,
6006                        *local_q_dim,
6007                        *local_kv_dim,
6008                        out_g,
6009                    )?;
6010                }
6011                _ => {
6012                    return Err("step TP decode v2 QKV projections mix residency classes".into());
6013                }
6014            }
6015        } else {
6016            for (matrix, local_out, raw) in [
6017                (q_m, ws.local_q_dim, &mut ws.q_raw),
6018                (k_m, ws.local_kv_dim, &mut ws.k_raw),
6019                (v_m, ws.local_kv_dim, &mut ws.v_raw),
6020            ] {
6021                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
6022                    return Err("step TP decode v2 lost its F32 projection residency".into());
6023                };
6024                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
6025                engine.linear_f32_resident_canonical_rows_t1_into(
6026                    &decode_input.ranks[rank],
6027                    values_w,
6028                    &mut raw[rank],
6029                    matrix.in_features,
6030                    local_out,
6031                    chunk_rows,
6032                )?;
6033            }
6034        }
6035        if qkv_fused && defer_norm_rope {
6036            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
6037        } else if qkv_fused {
6038            // Fused norm+rope: one launch; the position comes from the rank-local staged
6039            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
6040            let StepTpDecodeV2Ws {
6041                q_raw,
6042                k_raw,
6043                q,
6044                k,
6045                pos,
6046                pos_stage,
6047                ..
6048            } = &mut *ws;
6049            let same_dev = engine.ctx().ordinal() == ws_e_device;
6050            let pos_ref: &CudaSlice<i32> = if same_dev {
6051                pos_stage
6052                    .as_ref()
6053                    .ok_or("step TP decode v2 pos stage not armed")?
6054            } else {
6055                &pos[rank]
6056            };
6057            engine.qk_norm_rope_into(
6058                &q_raw[rank],
6059                &k_raw[rank],
6060                &q_norm[rank],
6061                &k_norm[rank],
6062                &mut q[rank],
6063                &mut k[rank],
6064                pos_ref,
6065                head_dim,
6066                n_rot,
6067                local_heads,
6068                local_kv_heads,
6069                rms_eps,
6070                rope_base,
6071                1.0,
6072                rope_freqs[rank],
6073            )?;
6074        } else {
6075            engine.rms_norm(
6076                &ws.q_raw[rank],
6077                &q_norm[rank],
6078                &mut ws.q[rank],
6079                head_dim,
6080                local_heads,
6081                rms_eps,
6082            )?;
6083            engine.rms_norm(
6084                &ws.k_raw[rank],
6085                &k_norm[rank],
6086                &mut ws.k[rank],
6087                head_dim,
6088                local_kv_heads,
6089                rms_eps,
6090            )?;
6091            {
6092                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
6093                engine
6094                    .stream()
6095                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
6096            }
6097            engine.rope_neox2(
6098                &mut ws.q[rank],
6099                &mut ws.k[rank],
6100                &ws.pos[rank],
6101                head_dim,
6102                n_rot,
6103                local_heads,
6104                local_kv_heads,
6105                1,
6106                rope_base,
6107                1.0,
6108                rope_freqs[rank],
6109            )?;
6110        }
6111        if gate_shards.is_none() {
6112            let gate_start = rank * (ws.heads / ranks);
6113            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
6114            engine.stream().memcpy_dtod(
6115                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
6116                &mut gate_dst,
6117            )?;
6118        }
6119        Ok(())
6120    }
6121
6122    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
6123    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
6124    /// eager caller; graphs order via parent edges instead).
6125    pub(crate) fn decode_v2_finish_rank_partial(
6126        &self,
6127        ws: &mut StepTpDecodeV2Ws,
6128        o_m: &ResidentStepBf16RowParallel,
6129        o_fused: bool,
6130        rank: usize,
6131    ) -> Result<(), Box<dyn std::error::Error>> {
6132        let engine = &self.ranks[rank];
6133        let _main = engine.gpu.enter_main()?;
6134        if o_fused {
6135            let StepTpDecodeV2Ws {
6136                gated,
6137                o_partials,
6138                o_block_cols,
6139                o_out,
6140                ..
6141            } = &mut *ws;
6142            let all_f32 = o_m.ranks[rank]
6143                .iter()
6144                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
6145            if all_f32 {
6146                let mut weights = Vec::with_capacity(4);
6147                for block in 0..4 {
6148                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6149                        unreachable!("all_f32 checked above");
6150                    };
6151                    weights.push(weight);
6152                }
6153                engine.matvec_f32_b4_into(
6154                    [weights[0], weights[1], weights[2], weights[3]],
6155                    &gated[rank],
6156                    &mut o_partials[rank][0],
6157                    *o_block_cols,
6158                    *o_out,
6159                )?;
6160            } else {
6161                let mut weights = Vec::with_capacity(4);
6162                for block in 0..4 {
6163                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6164                        return Err("step TP decode v2 O projections mix residency classes".into());
6165                    };
6166                    weights.push(weight);
6167                }
6168                engine.matvec_bf16_b4_into(
6169                    [weights[0], weights[1], weights[2], weights[3]],
6170                    &gated[rank],
6171                    &mut o_partials[rank][0],
6172                    *o_block_cols,
6173                    *o_out,
6174                )?;
6175            }
6176        } else {
6177            for block in 0..ws.blocks_per_rank {
6178                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6179                    return Err("step TP decode v2 lost its F32 O residency".into());
6180                };
6181                let x =
6182                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
6183                let w = weight.slice(0..weight.len());
6184                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
6185                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
6186            }
6187        }
6188        Ok(())
6189    }
6190
6191    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
6192    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
6193    ///
6194    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
6195    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
6196    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
6197    /// rank's blocks, one `add` per block.
6198    pub(crate) fn decode_v2_finish(
6199        &self,
6200        ws: &mut StepTpDecodeV2Ws,
6201        e: &Engine,
6202        o_m: &ResidentStepBf16RowParallel,
6203    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6204        let ranks = self.ranks.len();
6205        if e.ctx().ordinal() != ws.e_device {
6206            return Err("step TP decode v2 finish engine changed".into());
6207        }
6208        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
6209        // (in-order canonical block accumulation per element) and a single peer-copy + add on
6210        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
6211        // numeric-class door and gate as the fused QKV projection.
6212        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
6213
6214        // Per-rank O block partials on the owning rank's stream (serial after the attention
6215        // kernels the driver queued there), then the rank-done event for root's peer reads.
6216        for rank in 0..ranks {
6217            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
6218            if rank == 0 {
6219                // root == rank0: its own stream order covers the partial; only peers need
6220                // the record/wait pair (host-op diet, matches the routes-arm skip).
6221                continue;
6222            }
6223            let engine = &self.ranks[rank];
6224            let _main = engine.gpu.enter_main()?;
6225            ws.ev_rank[rank].record(&engine.stream())?;
6226        }
6227
6228        // Root reduce in canonical order + shadow gathers, all on the root stream.
6229        let root = &self.ranks[0];
6230        #[allow(unused_assignments)]
6231        let mut final_in_a = false;
6232        {
6233            let _main = root.gpu.enter_main()?;
6234            for ev in ws.ev_rank.iter().skip(1) {
6235                root.stream().wait(ev)?;
6236            }
6237            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
6238                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
6239                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
6240                // partial is root-stream-ordered — record ONE event and let the model
6241                // engine do the single add itself, straight into its own output row.
6242                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
6243                ws.ev_oproj.record(&root.stream())?;
6244                let _main = e.gpu.enter_main()?;
6245                e.stream().wait(&ws.ev_oproj)?;
6246                let mut output = e.uninit(ws.o_out)?;
6247                if oproj_tail_on() && oproj_tail_eligible() {
6248                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
6249                    // only the arithmetic moves). `output` is returned unwritten.
6250                    use cudarc::driver::DevicePtr;
6251                    let stream = e.stream();
6252                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
6253                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
6254                    set_oproj_tail((p0 as u64, p1 as u64));
6255                    return Ok(output);
6256                }
6257                e.add(
6258                    &ws.o_partials[0][0],
6259                    &ws.o_partials[1][0],
6260                    &mut output,
6261                    ws.o_out,
6262                )?;
6263                return Ok(output);
6264            }
6265            if o_fused {
6266                self.decode_v2_finish_root_fused(ws)?;
6267                ws.ev_oproj.record(&root.stream())?;
6268                let _main = e.gpu.enter_main()?;
6269                e.stream().wait(&ws.ev_oproj)?;
6270                let mut output = e.uninit(ws.o_out)?;
6271                e.stream().memcpy_dtod(
6272                    &ws.reduce_a.slice(0..ws.o_out),
6273                    &mut output.slice_mut(0..ws.o_out),
6274                )?;
6275                return Ok(output);
6276            }
6277            let mut first = true;
6278            let mut current_is_a = false;
6279            for rank in 0..ranks {
6280                for block in 0..ws.blocks_per_rank {
6281                    let use_peer = rank != 0;
6282                    if use_peer {
6283                        root.stream()
6284                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
6285                    }
6286                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
6287                    match (first, current_is_a, use_peer) {
6288                        (true, _, true) => {
6289                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
6290                        }
6291                        (true, _, false) => root.add(
6292                            &ws.zeros,
6293                            &ws.o_partials[0][block],
6294                            &mut ws.reduce_a,
6295                            ws.o_out,
6296                        )?,
6297                        (false, true, true) => {
6298                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
6299                        }
6300                        (false, true, false) => root.add(
6301                            &ws.reduce_a,
6302                            &ws.o_partials[0][block],
6303                            &mut ws.reduce_b,
6304                            ws.o_out,
6305                        )?,
6306                        (false, false, true) => {
6307                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
6308                        }
6309                        (false, false, false) => root.add(
6310                            &ws.reduce_b,
6311                            &ws.o_partials[0][block],
6312                            &mut ws.reduce_a,
6313                            ws.o_out,
6314                        )?,
6315                    }
6316                    current_is_a = first || !current_is_a;
6317                    first = false;
6318                }
6319            }
6320            final_in_a = current_is_a;
6321
6322            for rank in 0..ranks {
6323                let start = rank * ws.local_kv_dim;
6324                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
6325                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
6326                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
6327                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
6328            }
6329            ws.ev_oproj.record(&root.stream())?;
6330        }
6331
6332        // Model-engine output: e waits the root event, then copies the reduced row into a
6333        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
6334        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
6335        let _main = e.gpu.enter_main()?;
6336        e.stream().wait(&ws.ev_oproj)?;
6337        let mut output = e.uninit(ws.o_out)?;
6338        let source = if final_in_a {
6339            &ws.reduce_a
6340        } else {
6341            &ws.reduce_b
6342        };
6343        e.stream().memcpy_dtod(
6344            &source.slice(0..ws.o_out),
6345            &mut output.slice_mut(0..ws.o_out),
6346        )?;
6347        Ok(output)
6348    }
6349
6350    pub fn run_routed_experts(
6351        &self,
6352        experts: &ResidentExpertParallel,
6353        input: &[f32],
6354        tokens: usize,
6355        selected: &[usize],
6356        route_weights: &[f32],
6357        experts_per_token: usize,
6358        activation_limit: Option<f32>,
6359    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6360        validate_step_expert_activation_limit(activation_limit)?;
6361        validate_ep_residency(&self.ranks, experts)?;
6362        validate_activations(input, tokens, experts.input_width)?;
6363        let pairs = tokens
6364            .checked_mul(experts_per_token)
6365            .ok_or("EP route count overflow")?;
6366        if selected.len() != pairs || route_weights.len() != pairs {
6367            return Err(format!(
6368                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
6369                 {experts_per_token} ({pairs})",
6370                selected.len(),
6371                route_weights.len(),
6372            )
6373            .into());
6374        }
6375        if !route_weights.iter().all(|weight| weight.is_finite()) {
6376            return Err("EP route weights contain a non-finite value".into());
6377        }
6378        if self.native_p2p {
6379            return self.run_routed_experts_native(
6380                experts,
6381                input,
6382                tokens,
6383                selected,
6384                route_weights,
6385                experts_per_token,
6386                activation_limit,
6387            );
6388        }
6389
6390        let mut output = vec![0.0f32; tokens * experts.input_width];
6391        let per_rank = experts.expert_count / experts.ranks.len();
6392        for token in 0..tokens {
6393            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6394            for slot in 0..experts_per_token {
6395                let pair = token * experts_per_token + slot;
6396                let expert = selected[pair];
6397                if expert >= experts.expert_count {
6398                    return Err(format!(
6399                        "EP selected expert {expert} outside 0..{}",
6400                        experts.expert_count
6401                    )
6402                    .into());
6403                }
6404                let owner = expert / per_rank;
6405                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6406                let rank = &experts.ranks[owner];
6407                let engine = &self.ranks[owner];
6408                let gate =
6409                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
6410                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
6411                let activated: Vec<f32> = gate
6412                    .iter()
6413                    .zip(&up)
6414                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
6415                    .collect();
6416                debug_assert_eq!(activated.len(), experts.expert_width);
6417                let down =
6418                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
6419                let weight = route_weights[pair];
6420                for (sum, value) in output
6421                    [token * experts.input_width..(token + 1) * experts.input_width]
6422                    .iter_mut()
6423                    .zip(down)
6424                {
6425                    *sum += weight * value;
6426                }
6427            }
6428        }
6429        Ok(output)
6430    }
6431
6432    fn run_routed_experts_native(
6433        &self,
6434        experts: &ResidentExpertParallel,
6435        input: &[f32],
6436        tokens: usize,
6437        selected: &[usize],
6438        route_weights: &[f32],
6439        experts_per_token: usize,
6440        activation_limit: Option<f32>,
6441    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6442        if !self.native_p2p || self.ranks.len() < 2 {
6443            return Err("native EP execution requires at least two P2P ranks".into());
6444        }
6445        if self.ep_device_arithmetic {
6446            return self.run_routed_experts_native_device(
6447                experts,
6448                input,
6449                tokens,
6450                selected,
6451                route_weights,
6452                experts_per_token,
6453                activation_limit,
6454            );
6455        }
6456        let mut output = vec![0.0f32; tokens * experts.input_width];
6457        let per_rank = experts.expert_count / experts.ranks.len();
6458        for token in 0..tokens {
6459            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6460            let mut rank_inputs = (0..self.ranks.len())
6461                .map(|_| None)
6462                .collect::<Vec<Option<CudaSlice<f32>>>>();
6463            rank_inputs[0] = Some({
6464                let root = &self.ranks[0];
6465                let _main = root.gpu.enter_main()?;
6466                root.htod(input_row)?
6467            });
6468
6469            for slot in 0..experts_per_token {
6470                let pair = token * experts_per_token + slot;
6471                let expert = selected[pair];
6472                if expert >= experts.expert_count {
6473                    return Err(format!(
6474                        "EP selected expert {expert} outside 0..{}",
6475                        experts.expert_count
6476                    )
6477                    .into());
6478                }
6479                let owner = expert / per_rank;
6480                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6481                if rank_inputs[owner].is_none() {
6482                    let peer_input = {
6483                        let root_input = rank_inputs[0]
6484                            .as_ref()
6485                            .ok_or("native EP lost its root input")?;
6486                        let engine = &self.ranks[owner];
6487                        let _main = engine.gpu.enter_main()?;
6488                        let mut peer_input = engine.uninit(experts.input_width)?;
6489                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
6490                        peer_input
6491                    };
6492                    rank_inputs[owner] = Some(peer_input);
6493                }
6494
6495                let rank = &experts.ranks[owner];
6496                let engine = &self.ranks[owner];
6497                let owner_input = rank_inputs[owner]
6498                    .as_ref()
6499                    .ok_or("native EP owner input is absent after dispatch")?;
6500                let gate = run_resident_bank_expert_device(
6501                    engine,
6502                    &rank.gate,
6503                    local_expert,
6504                    owner_input,
6505                    1,
6506                )?;
6507                let up = run_resident_bank_expert_device(
6508                    engine,
6509                    &rank.up,
6510                    local_expert,
6511                    owner_input,
6512                    1,
6513                )?;
6514                let (gate, up) = {
6515                    let _main = engine.gpu.enter_main()?;
6516                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
6517                };
6518                let activated = gate
6519                    .iter()
6520                    .zip(&up)
6521                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
6522                    .collect::<Vec<_>>();
6523                debug_assert_eq!(activated.len(), experts.expert_width);
6524                let activated = {
6525                    let _main = engine.gpu.enter_main()?;
6526                    engine.htod(&activated)?
6527                };
6528                let down = run_resident_bank_expert_device(
6529                    engine,
6530                    &rank.down,
6531                    local_expert,
6532                    &activated,
6533                    1,
6534                )?;
6535                let down = if owner == 0 {
6536                    let _main = engine.gpu.enter_main()?;
6537                    engine.dtoh(&down)?
6538                } else {
6539                    let root = &self.ranks[0];
6540                    let _main = root.gpu.enter_main()?;
6541                    let mut root_down = root.uninit(experts.input_width)?;
6542                    root.stream().memcpy_dtod(&down, &mut root_down)?;
6543                    root.dtoh(&root_down)?
6544                };
6545                let weight = route_weights[pair];
6546                for (sum, value) in output
6547                    [token * experts.input_width..(token + 1) * experts.input_width]
6548                    .iter_mut()
6549                    .zip(down)
6550                {
6551                    *sum += weight * value;
6552                }
6553            }
6554        }
6555        Ok(output)
6556    }
6557
6558    fn run_routed_experts_native_device(
6559        &self,
6560        experts: &ResidentExpertParallel,
6561        input: &[f32],
6562        tokens: usize,
6563        selected: &[usize],
6564        route_weights: &[f32],
6565        experts_per_token: usize,
6566        activation_limit: Option<f32>,
6567    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6568        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
6569            return Err(
6570                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
6571            );
6572        }
6573        let mut output = Vec::with_capacity(tokens * experts.input_width);
6574        let per_rank = experts.expert_count / experts.ranks.len();
6575        let root = &self.ranks[0];
6576        for token in 0..tokens {
6577            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6578            let mut rank_inputs = (0..self.ranks.len())
6579                .map(|_| None)
6580                .collect::<Vec<Option<CudaSlice<f32>>>>();
6581            rank_inputs[0] = Some({
6582                let _main = root.gpu.enter_main()?;
6583                root.htod(input_row)?
6584            });
6585            let mut root_output = {
6586                let _main = root.gpu.enter_main()?;
6587                root.zeros(experts.input_width)?
6588            };
6589            let mut remote_down_keepalive = Vec::new();
6590
6591            for slot in 0..experts_per_token {
6592                let pair = token * experts_per_token + slot;
6593                let expert = selected[pair];
6594                if expert >= experts.expert_count {
6595                    return Err(format!(
6596                        "EP selected expert {expert} outside 0..{}",
6597                        experts.expert_count
6598                    )
6599                    .into());
6600                }
6601                let owner = expert / per_rank;
6602                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6603                if rank_inputs[owner].is_none() {
6604                    let peer_input = {
6605                        let root_input = rank_inputs[0]
6606                            .as_ref()
6607                            .ok_or("native EP lost its root input")?;
6608                        let engine = &self.ranks[owner];
6609                        let _main = engine.gpu.enter_main()?;
6610                        let mut peer_input = engine.uninit(experts.input_width)?;
6611                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
6612                        peer_input
6613                    };
6614                    rank_inputs[owner] = Some(peer_input);
6615                }
6616
6617                let rank = &experts.ranks[owner];
6618                let engine = &self.ranks[owner];
6619                let owner_input = rank_inputs[owner]
6620                    .as_ref()
6621                    .ok_or("native EP owner input is absent after dispatch")?;
6622                let gate = run_resident_bank_expert_device(
6623                    engine,
6624                    &rank.gate,
6625                    local_expert,
6626                    owner_input,
6627                    1,
6628                )?;
6629                let up = run_resident_bank_expert_device(
6630                    engine,
6631                    &rank.up,
6632                    local_expert,
6633                    owner_input,
6634                    1,
6635                )?;
6636                let activated = {
6637                    let _main = engine.gpu.enter_main()?;
6638                    let mut activated = engine.uninit(experts.expert_width)?;
6639                    if let Some(limit) = activation_limit {
6640                        engine.silu_clamped_mul_host_expf(
6641                            &gate,
6642                            &up,
6643                            limit,
6644                            &mut activated,
6645                            experts.expert_width,
6646                        )?;
6647                    } else {
6648                        engine.silu_mul_host_expf(
6649                            &gate,
6650                            &up,
6651                            &mut activated,
6652                            experts.expert_width,
6653                        )?;
6654                    }
6655                    activated
6656                };
6657                let down = run_resident_bank_expert_device(
6658                    engine,
6659                    &rank.down,
6660                    local_expert,
6661                    &activated,
6662                    1,
6663                )?;
6664                let root_down = if owner == 0 {
6665                    down
6666                } else {
6667                    let _main = root.gpu.enter_main()?;
6668                    let mut root_down = root.uninit(experts.input_width)?;
6669                    root.stream().memcpy_dtod(&down, &mut root_down)?;
6670                    // The peer copy runs on the root stream. Keep its remote source alive until
6671                    // the final root readback synchronizes that stream; otherwise async free can
6672                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
6673                    remote_down_keepalive.push(down);
6674                    root_down
6675                };
6676                let _main = root.gpu.enter_main()?;
6677                let mut destination = root_output.slice_mut(0..experts.input_width);
6678                root.axpy_host_into(
6679                    &root_down.slice(0..root_down.len()),
6680                    route_weights[pair],
6681                    &mut destination,
6682                    experts.input_width,
6683                )?;
6684            }
6685
6686            let _main = root.gpu.enter_main()?;
6687            let root_output = root.dtoh(&root_output)?;
6688            drop(remote_down_keepalive);
6689            output.extend(root_output);
6690        }
6691        Ok(output)
6692    }
6693}
6694
6695fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
6696    if matrix.out_features % tp != 0 {
6697        return Err(format!(
6698            "column-parallel out_features {} is not divisible by TP={tp}",
6699            matrix.out_features
6700        ));
6701    }
6702    let local_out = matrix.out_features / tp;
6703    if local_out % FP8_BLOCK != 0 {
6704        return Err(format!(
6705            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
6706             E4M3 scale block"
6707        ));
6708    }
6709    Ok(())
6710}
6711
6712fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
6713    if !matches!(tp, 1 | 2 | 4 | 8) {
6714        return Err(format!(
6715            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
6716        ));
6717    }
6718    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
6719        return Err(format!(
6720            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
6721        ));
6722    }
6723    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
6724    let local_out = out_features / tp;
6725    if local_out % canonical_rows != 0 {
6726        return Err(format!(
6727            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
6728             {canonical_rows}-row chunks"
6729        ));
6730    }
6731    Ok(canonical_rows)
6732}
6733
6734fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
6735    if !matches!(tp, 1 | 2 | 4 | 8) {
6736        return Err(format!(
6737            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
6738        ));
6739    }
6740    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
6741        return Err(format!(
6742            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
6743        ));
6744    }
6745    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
6746    let local_in = in_features / tp;
6747    if local_in % canonical_cols != 0 {
6748        return Err(format!(
6749            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
6750             {canonical_cols}-column chunks"
6751        ));
6752    }
6753    Ok(canonical_cols)
6754}
6755
6756fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
6757    if matrix.in_features % tp != 0 {
6758        return Err(format!(
6759            "row-parallel in_features {} is not divisible by TP={tp}",
6760            matrix.in_features
6761        ));
6762    }
6763    let local_in = matrix.in_features / tp;
6764    if local_in % FP8_BLOCK != 0 {
6765        return Err(format!(
6766            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
6767             E4M3 scale block"
6768        ));
6769    }
6770    Ok(())
6771}
6772
6773fn upload_rank(
6774    engine: &Engine,
6775    matrix: E4m3BlockMatrix<'_>,
6776) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
6777    let _main = engine.gpu.enter_main()?;
6778    matrix.validate()?;
6779    Ok(ResidentE4m3Rank {
6780        codes: engine.htod_bytes(matrix.codes)?,
6781        scales: engine.htod(matrix.scales)?,
6782        out_features: matrix.out_features,
6783        in_features: matrix.in_features,
6784    })
6785}
6786
6787fn upload_bf16_rank(
6788    engine: &Engine,
6789    matrix: Bf16Matrix<'_>,
6790    f32_mirror: bool,
6791) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
6792    let _main = engine.gpu.enter_main()?;
6793    matrix.validate()?;
6794    let bytes = engine.htod_bytes(matrix.bytes)?;
6795    let weight = if f32_mirror {
6796        let values = matrix
6797            .out_features
6798            .checked_mul(matrix.in_features)
6799            .ok_or("resident BF16 mirror element count overflow")?;
6800        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
6801    } else {
6802        ResidentBf16Weight::Bf16(bytes)
6803    };
6804    Ok(ResidentBf16Rank {
6805        weight,
6806        out_features: matrix.out_features,
6807        in_features: matrix.in_features,
6808    })
6809}
6810
6811fn upload_expert_bank_rank(
6812    engine: &Engine,
6813    bank: E4m3ExpertBank<'_>,
6814    expert_range: Range<usize>,
6815) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
6816    let _main = engine.gpu.enter_main()?;
6817    bank.validate()?;
6818    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
6819        return Err(format!(
6820            "invalid EP expert range {expert_range:?} for {} experts",
6821            bank.expert_count
6822        )
6823        .into());
6824    }
6825    let code_stride = bank.out_features * bank.in_features;
6826    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
6827    Ok(ResidentE4m3ExpertBankRank {
6828        codes: engine.htod_bytes(
6829            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
6830        )?,
6831        scales: engine.htod(
6832            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
6833        )?,
6834        expert_range,
6835        out_features: bank.out_features,
6836        in_features: bank.in_features,
6837        code_stride,
6838        scale_stride,
6839        k_blocks: None,
6840    })
6841}
6842
6843fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
6844    if bank.out_features % tp != 0 {
6845        return Err(format!(
6846            "TP expert output width {} is not divisible by TP={tp}",
6847            bank.out_features
6848        ));
6849    }
6850    let local_out = bank.out_features / tp;
6851    if local_out % FP8_BLOCK != 0 {
6852        return Err(format!(
6853            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
6854        ));
6855    }
6856    Ok(())
6857}
6858
6859fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
6860    if bank.in_features % tp != 0 {
6861        return Err(format!(
6862            "TP expert input width {} is not divisible by TP={tp}",
6863            bank.in_features
6864        ));
6865    }
6866    let local_in = bank.in_features / tp;
6867    if local_in % FP8_BLOCK != 0 {
6868        return Err(format!(
6869            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
6870        ));
6871    }
6872    Ok(())
6873}
6874
6875fn upload_column_bank_rank(
6876    engine: &Engine,
6877    bank: E4m3ExpertBank<'_>,
6878    tp: usize,
6879    rank: usize,
6880) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
6881    let _main = engine.gpu.enter_main()?;
6882    let packed = pack_column_bank_rank(bank, tp, rank)?;
6883    Ok(ResidentE4m3ExpertBankRank {
6884        codes: engine.htod_bytes(&packed.codes)?,
6885        scales: engine.htod(&packed.scales)?,
6886        expert_range: packed.expert_range,
6887        out_features: packed.out_features,
6888        in_features: packed.in_features,
6889        code_stride: packed.code_stride,
6890        scale_stride: packed.scale_stride,
6891        k_blocks: packed.k_blocks,
6892    })
6893}
6894
6895fn pack_column_bank_rank(
6896    bank: E4m3ExpertBank<'_>,
6897    tp: usize,
6898    rank: usize,
6899) -> Result<PackedE4m3ExpertBankRank, String> {
6900    bank.validate()?;
6901    validate_column_bank_shape(bank, tp)?;
6902    if rank >= tp {
6903        return Err(format!("TP rank {rank} outside 0..{tp}"));
6904    }
6905    let local_out = bank.out_features / tp;
6906    let full_code_stride = bank.out_features * bank.in_features;
6907    let local_code_stride = local_out * bank.in_features;
6908    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
6909    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
6910    let local_scale_rows = local_out / FP8_BLOCK;
6911    let local_scale_stride = local_scale_rows * scale_cols;
6912    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
6913    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
6914    let row_start = rank * local_out;
6915    let scale_row_start = rank * local_scale_rows;
6916    for expert in 0..bank.expert_count {
6917        let code_start = expert * full_code_stride + row_start * bank.in_features;
6918        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
6919        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
6920        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
6921    }
6922    Ok(PackedE4m3ExpertBankRank {
6923        codes,
6924        scales,
6925        expert_range: 0..bank.expert_count,
6926        out_features: local_out,
6927        in_features: bank.in_features,
6928        code_stride: local_code_stride,
6929        scale_stride: local_scale_stride,
6930        k_blocks: None,
6931    })
6932}
6933
6934fn upload_row_bank_rank(
6935    engine: &Engine,
6936    bank: E4m3ExpertBank<'_>,
6937    tp: usize,
6938    rank: usize,
6939) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
6940    let _main = engine.gpu.enter_main()?;
6941    let packed = pack_row_bank_rank(bank, tp, rank)?;
6942    Ok(ResidentE4m3ExpertBankRank {
6943        codes: engine.htod_bytes(&packed.codes)?,
6944        scales: engine.htod(&packed.scales)?,
6945        expert_range: packed.expert_range,
6946        out_features: packed.out_features,
6947        in_features: packed.in_features,
6948        code_stride: packed.code_stride,
6949        scale_stride: packed.scale_stride,
6950        k_blocks: packed.k_blocks,
6951    })
6952}
6953
6954fn pack_row_bank_rank(
6955    bank: E4m3ExpertBank<'_>,
6956    tp: usize,
6957    rank: usize,
6958) -> Result<PackedE4m3ExpertBankRank, String> {
6959    bank.validate()?;
6960    validate_row_bank_shape(bank, tp)?;
6961    if rank >= tp {
6962        return Err(format!("TP rank {rank} outside 0..{tp}"));
6963    }
6964    let local_in = bank.in_features / tp;
6965    let full_code_stride = bank.out_features * bank.in_features;
6966    let local_code_stride = bank.out_features * local_in;
6967    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
6968    let local_scale_cols = local_in / FP8_BLOCK;
6969    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
6970    let full_scale_stride = scale_rows * full_scale_cols;
6971    let local_scale_stride = scale_rows * local_scale_cols;
6972    let global_block_start = rank * local_scale_cols;
6973    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
6974    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
6975    for expert in 0..bank.expert_count {
6976        let expert_code_start = expert * full_code_stride;
6977        let expert_scale_start = expert * full_scale_stride;
6978        for local_block in 0..local_scale_cols {
6979            let global_block = global_block_start + local_block;
6980            let column_start = global_block * FP8_BLOCK;
6981            for row in 0..bank.out_features {
6982                let start = expert_code_start + row * bank.in_features + column_start;
6983                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
6984            }
6985            for row in 0..scale_rows {
6986                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
6987            }
6988        }
6989    }
6990    Ok(PackedE4m3ExpertBankRank {
6991        codes,
6992        scales,
6993        expert_range: 0..bank.expert_count,
6994        out_features: bank.out_features,
6995        in_features: local_in,
6996        code_stride: local_code_stride,
6997        scale_stride: local_scale_stride,
6998        k_blocks: Some(local_scale_cols),
6999    })
7000}
7001
7002fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
7003    if engines.len() != ranks.len() {
7004        return Err(format!(
7005            "resident TP rank count {} != runtime rank count {}",
7006            ranks.len(),
7007            engines.len()
7008        ));
7009    }
7010    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7011        let device = engine.ctx().ordinal();
7012        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
7013            return Err(format!(
7014                "resident TP rank {rank} is not owned by runtime device {device}"
7015            ));
7016        }
7017    }
7018    Ok(())
7019}
7020
7021fn validate_tp_bank_residency(
7022    engines: &[Engine],
7023    experts: &ResidentTpExpertBank,
7024) -> Result<(), String> {
7025    if engines.len() != experts.gate.len()
7026        || engines.len() != experts.up.len()
7027        || engines.len() != experts.down.len()
7028    {
7029        return Err(format!(
7030            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
7031            experts.gate.len(),
7032            experts.up.len(),
7033            experts.down.len(),
7034            engines.len()
7035        ));
7036    }
7037    for (rank, engine) in engines.iter().enumerate() {
7038        let device = engine.ctx().ordinal();
7039        for (projection, bank) in [
7040            ("gate", &experts.gate[rank]),
7041            ("up", &experts.up[rank]),
7042            ("down", &experts.down[rank]),
7043        ] {
7044            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7045                return Err(format!(
7046                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
7047                     {device}"
7048                ));
7049            }
7050        }
7051    }
7052    Ok(())
7053}
7054
7055fn validate_ep_residency(
7056    engines: &[Engine],
7057    experts: &ResidentExpertParallel,
7058) -> Result<(), String> {
7059    if engines.len() != experts.ranks.len() {
7060        return Err(format!(
7061            "resident EP rank count {} != runtime rank count {}",
7062            experts.ranks.len(),
7063            engines.len()
7064        ));
7065    }
7066    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
7067        let device = engine.ctx().ordinal();
7068        for (projection, bank) in [
7069            ("gate", &resident.gate),
7070            ("up", &resident.up),
7071            ("down", &resident.down),
7072        ] {
7073            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7074                return Err(format!(
7075                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
7076                     {device}"
7077                ));
7078            }
7079        }
7080    }
7081    Ok(())
7082}
7083
7084fn run_rank(
7085    engine: &Engine,
7086    matrix: E4m3BlockMatrix<'_>,
7087    activations: &[f32],
7088    tokens: usize,
7089) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7090    let _main = engine.gpu.enter_main()?;
7091    let codes = engine.htod_bytes(matrix.codes)?;
7092    let scales = engine.htod(matrix.scales)?;
7093    let activations = engine.htod(activations)?;
7094    let output = engine.qmatvec_mmq_fp8_blk(
7095        &codes,
7096        &scales,
7097        &activations,
7098        tokens,
7099        matrix.in_features,
7100        matrix.out_features,
7101    )?;
7102    engine.dtoh(&output)
7103}
7104
7105fn run_resident_rank(
7106    engine: &Engine,
7107    matrix: &ResidentE4m3Rank,
7108    activations: &[f32],
7109    tokens: usize,
7110) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7111    let _main = engine.gpu.enter_main()?;
7112    let activations = engine.htod(activations)?;
7113    let output = engine.qmatvec_mmq_fp8_blk(
7114        &matrix.codes,
7115        &matrix.scales,
7116        &activations,
7117        tokens,
7118        matrix.in_features,
7119        matrix.out_features,
7120    )?;
7121    engine.dtoh(&output)
7122}
7123
7124fn run_resident_bf16_rank(
7125    engine: &Engine,
7126    matrix: &ResidentBf16Rank,
7127    activations: &[f32],
7128    tokens: usize,
7129    canonical_chunk_rows: Option<usize>,
7130) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7131    let _main = engine.gpu.enter_main()?;
7132    let activations = engine.htod(activations)?;
7133    let output = run_resident_bf16_rank_device(
7134        engine,
7135        matrix,
7136        &activations,
7137        tokens,
7138        canonical_chunk_rows,
7139        false,
7140    )?;
7141    engine.dtoh(&output)
7142}
7143
7144fn run_resident_bf16_rank_device(
7145    engine: &Engine,
7146    matrix: &ResidentBf16Rank,
7147    activations: &CudaSlice<f32>,
7148    tokens: usize,
7149    canonical_chunk_rows: Option<usize>,
7150    strided_chunk_output: bool,
7151) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7152    let _main = engine.gpu.enter_main()?;
7153    if activations.ordinal() != engine.ctx().ordinal() {
7154        return Err(format!(
7155            "resident BF16 activation device {} != rank device {}",
7156            activations.ordinal(),
7157            engine.ctx().ordinal()
7158        )
7159        .into());
7160    }
7161    if activations.len() != tokens * matrix.in_features {
7162        return Err(format!(
7163            "resident BF16 activation count {} != {tokens}x{}",
7164            activations.len(),
7165            matrix.in_features
7166        )
7167        .into());
7168    }
7169    match (&matrix.weight, canonical_chunk_rows) {
7170        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
7171            .linear_bf16_resident_canonical_rows(
7172                activations,
7173                bytes,
7174                tokens,
7175                matrix.in_features,
7176                matrix.out_features,
7177                rows,
7178            ),
7179        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
7180            activations,
7181            bytes,
7182            tokens,
7183            matrix.in_features,
7184            matrix.out_features,
7185        ),
7186        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
7187            .linear_f32_resident_canonical_rows_strided(
7188                activations,
7189                values,
7190                tokens,
7191                matrix.in_features,
7192                matrix.out_features,
7193                rows,
7194            ),
7195        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
7196            activations,
7197            values,
7198            tokens,
7199            matrix.in_features,
7200            matrix.out_features,
7201            rows,
7202        ),
7203        (ResidentBf16Weight::F32(values), None) => engine.linear(
7204            activations,
7205            values,
7206            tokens,
7207            matrix.in_features,
7208            matrix.out_features,
7209        ),
7210    }
7211}
7212
7213fn validate_resident_bf16_ranks(
7214    engines: &[Engine],
7215    ranks: &[ResidentBf16Rank],
7216) -> Result<(), String> {
7217    if engines.len() != ranks.len() {
7218        return Err(format!(
7219            "resident BF16 TP rank count {} != runtime rank count {}",
7220            ranks.len(),
7221            engines.len(),
7222        ));
7223    }
7224    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7225        let device = engine.ctx().ordinal();
7226        if matrix.weight.ordinal() != device {
7227            return Err(format!(
7228                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
7229            ));
7230        }
7231    }
7232    Ok(())
7233}
7234
7235fn validate_step_bf16_row_residency(
7236    engines: &[Engine],
7237    matrix: &ResidentStepBf16RowParallel,
7238) -> Result<(), String> {
7239    if engines.len() != matrix.ranks.len() {
7240        return Err(format!(
7241            "resident Step BF16 row rank count {} != runtime rank count {}",
7242            matrix.ranks.len(),
7243            engines.len(),
7244        ));
7245    }
7246    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
7247    if matrix.canonical_chunk_cols != canonical_cols {
7248        return Err(format!(
7249            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
7250            matrix.canonical_chunk_cols
7251        ));
7252    }
7253    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
7254    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
7255        if blocks.len() != blocks_per_rank {
7256            return Err(format!(
7257                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
7258                blocks.len()
7259            ));
7260        }
7261        let device = engine.ctx().ordinal();
7262        for (block, resident) in blocks.iter().enumerate() {
7263            if resident.weight.ordinal() != device
7264                || resident.in_features != canonical_cols
7265                || resident.out_features != matrix.out_features
7266            {
7267                return Err(format!(
7268                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
7269                     device or geometry"
7270                ));
7271            }
7272        }
7273    }
7274    Ok(())
7275}
7276
7277fn validate_replicated_device_rows(
7278    engines: &[Engine],
7279    rows: &ResidentReplicatedDeviceRows,
7280) -> Result<(), String> {
7281    let rank_lengths = rows
7282        .ranks
7283        .iter()
7284        .map(|rank_rows| rank_rows.len())
7285        .collect::<Vec<_>>();
7286    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
7287    if rows
7288        .ranks
7289        .iter()
7290        .zip(engines)
7291        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
7292    {
7293        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
7294    }
7295    Ok(())
7296}
7297
7298fn replicated_device_row_values(
7299    tokens: usize,
7300    width: usize,
7301    expected_ranks: usize,
7302    rank_lengths: &[usize],
7303) -> Result<usize, String> {
7304    let values = tokens
7305        .checked_mul(width)
7306        .ok_or("replicated device row size overflow")?;
7307    if tokens == 0
7308        || width == 0
7309        || expected_ranks == 0
7310        || rank_lengths.len() != expected_ranks
7311        || rank_lengths.iter().any(|&rank_len| rank_len != values)
7312    {
7313        return Err(format!(
7314            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
7315            tokens,
7316            width,
7317            rank_lengths.len(),
7318            expected_ranks
7319        ));
7320    }
7321    Ok(values)
7322}
7323
7324fn replicated_device_row_source_values(
7325    tokens: usize,
7326    width: usize,
7327    source_len: usize,
7328    source_device: usize,
7329    root_device: usize,
7330) -> Result<usize, String> {
7331    let values = tokens
7332        .checked_mul(width)
7333        .ok_or("replicated device row size overflow")?;
7334    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
7335        return Err(format!(
7336            "replicated device row source has inconsistent geometry/device \
7337             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
7338        ));
7339    }
7340    Ok(values)
7341}
7342
7343fn bf16_column_shard(
7344    matrix: Bf16Matrix<'_>,
7345    tp: usize,
7346    rank: usize,
7347) -> Result<Bf16Matrix<'_>, String> {
7348    matrix.validate()?;
7349    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
7350        return Err(format!(
7351            "invalid BF16 column shard out={} TP={tp} rank={rank}",
7352            matrix.out_features
7353        ));
7354    }
7355    let local_out = matrix.out_features / tp;
7356    let row_bytes = matrix.in_features * 2;
7357    let start = rank * local_out * row_bytes;
7358    Ok(Bf16Matrix {
7359        bytes: &matrix.bytes[start..start + local_out * row_bytes],
7360        out_features: local_out,
7361        in_features: matrix.in_features,
7362    })
7363}
7364
7365fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
7366    matrix.validate()?;
7367    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
7368        return Err(format!(
7369            "invalid BF16 row shard in={} TP={tp} rank={rank}",
7370            matrix.in_features
7371        ));
7372    }
7373    let local_in = matrix.in_features / tp;
7374    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
7375    for row in 0..matrix.out_features {
7376        let start = (row * matrix.in_features + rank * local_in) * 2;
7377        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
7378    }
7379    Ok(bytes)
7380}
7381
7382fn bf16_row_block(
7383    matrix: Bf16Matrix<'_>,
7384    col_start: usize,
7385    block_cols: usize,
7386) -> Result<Vec<u8>, String> {
7387    matrix.validate()?;
7388    let col_end = col_start
7389        .checked_add(block_cols)
7390        .ok_or("BF16 row block column overflow")?;
7391    if block_cols == 0 || col_end > matrix.in_features {
7392        return Err(format!(
7393            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
7394            matrix.in_features
7395        ));
7396    }
7397    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
7398    for row in 0..matrix.out_features {
7399        let start = (row * matrix.in_features + col_start) * 2;
7400        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
7401    }
7402    Ok(bytes)
7403}
7404
7405fn run_resident_bank_expert(
7406    engine: &Engine,
7407    bank: &ResidentE4m3ExpertBankRank,
7408    local_expert: usize,
7409    activations: &[f32],
7410    tokens: usize,
7411) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7412    let _main = engine.gpu.enter_main()?;
7413    if bank.k_blocks.is_some() {
7414        return Err("block-major TP row bank requires canonical block execution".into());
7415    }
7416    let local_count = bank.expert_range.end - bank.expert_range.start;
7417    if local_expert >= local_count {
7418        return Err(format!(
7419            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
7420            bank.expert_range
7421        )
7422        .into());
7423    }
7424    validate_activations(activations, tokens, bank.in_features)?;
7425    let activations = engine.htod(activations)?;
7426    let weight = bank
7427        .codes
7428        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
7429    let scales = bank
7430        .scales
7431        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
7432    let input = activations.slice(0..activations.len());
7433    let output = engine.qmatvec_mmq_fp8_blk_view(
7434        &weight,
7435        &scales,
7436        &input,
7437        tokens,
7438        bank.in_features,
7439        bank.out_features,
7440    )?;
7441    engine.dtoh(&output)
7442}
7443
7444fn run_resident_bank_expert_block(
7445    engine: &Engine,
7446    bank: &ResidentE4m3ExpertBankRank,
7447    local_expert: usize,
7448    block: usize,
7449    activations: &[f32],
7450) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7451    let _main = engine.gpu.enter_main()?;
7452    let local_count = bank.expert_range.end - bank.expert_range.start;
7453    if local_expert >= local_count {
7454        return Err(format!(
7455            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7456            bank.expert_range
7457        )
7458        .into());
7459    }
7460    let blocks = bank
7461        .k_blocks
7462        .ok_or("TP row bank is not packed in native K-block order")?;
7463    if block >= blocks {
7464        return Err(format!("TP row block {block} outside 0..{blocks}").into());
7465    }
7466    validate_activations(activations, 1, FP8_BLOCK)?;
7467    let block_code_stride = bank.out_features * FP8_BLOCK;
7468    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
7469    if bank.in_features != blocks * FP8_BLOCK
7470        || bank.code_stride != blocks * block_code_stride
7471        || bank.scale_stride != blocks * block_scale_stride
7472    {
7473        return Err("TP row bank block-major geometry is inconsistent".into());
7474    }
7475
7476    let expert_code_start = local_expert * bank.code_stride;
7477    let expert_scale_start = local_expert * bank.scale_stride;
7478    let weight = bank.codes.slice(
7479        expert_code_start + block * block_code_stride
7480            ..expert_code_start + (block + 1) * block_code_stride,
7481    );
7482    let scales = bank.scales.slice(
7483        expert_scale_start + block * block_scale_stride
7484            ..expert_scale_start + (block + 1) * block_scale_stride,
7485    );
7486    let activations = engine.htod(activations)?;
7487    let input = activations.slice(0..activations.len());
7488    let output = engine.qmatvec_mmq_fp8_blk_view(
7489        &weight,
7490        &scales,
7491        &input,
7492        1,
7493        FP8_BLOCK,
7494        bank.out_features,
7495    )?;
7496    engine.dtoh(&output)
7497}
7498
7499fn run_resident_bank_expert_device(
7500    engine: &Engine,
7501    bank: &ResidentE4m3ExpertBankRank,
7502    local_expert: usize,
7503    activations: &CudaSlice<f32>,
7504    tokens: usize,
7505) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7506    let _main = engine.gpu.enter_main()?;
7507    if bank.k_blocks.is_some() {
7508        return Err("block-major TP row bank requires canonical block execution".into());
7509    }
7510    let local_count = bank.expert_range.end - bank.expert_range.start;
7511    if local_expert >= local_count {
7512        return Err(format!(
7513            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7514            bank.expert_range
7515        )
7516        .into());
7517    }
7518    let expected = tokens
7519        .checked_mul(bank.in_features)
7520        .ok_or("native TP activation size overflow")?;
7521    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
7522        return Err(format!(
7523            "native TP activation len/device {}/{} != expected {expected}/{}",
7524            activations.len(),
7525            activations.ordinal(),
7526            engine.ctx().ordinal()
7527        )
7528        .into());
7529    }
7530    let weight = bank
7531        .codes
7532        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
7533    let scales = bank
7534        .scales
7535        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
7536    let input = activations.slice(0..activations.len());
7537    engine.qmatvec_mmq_fp8_blk_view(
7538        &weight,
7539        &scales,
7540        &input,
7541        tokens,
7542        bank.in_features,
7543        bank.out_features,
7544    )
7545}
7546
7547fn run_resident_bank_expert_block_device(
7548    engine: &Engine,
7549    bank: &ResidentE4m3ExpertBankRank,
7550    local_expert: usize,
7551    block: usize,
7552    activations: &cudarc::driver::CudaView<'_, f32>,
7553) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7554    let _main = engine.gpu.enter_main()?;
7555    let local_count = bank.expert_range.end - bank.expert_range.start;
7556    if local_expert >= local_count {
7557        return Err(format!(
7558            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7559            bank.expert_range
7560        )
7561        .into());
7562    }
7563    let blocks = bank
7564        .k_blocks
7565        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
7566    if block >= blocks {
7567        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
7568    }
7569    let activation_device = activations.stream().context().ordinal();
7570    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
7571        return Err(format!(
7572            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
7573            activations.len(),
7574            activation_device,
7575            engine.ctx().ordinal()
7576        )
7577        .into());
7578    }
7579    let block_code_stride = bank.out_features * FP8_BLOCK;
7580    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
7581    if bank.in_features != blocks * FP8_BLOCK
7582        || bank.code_stride != blocks * block_code_stride
7583        || bank.scale_stride != blocks * block_scale_stride
7584    {
7585        return Err("native TP row bank block-major geometry is inconsistent".into());
7586    }
7587    let expert_code_start = local_expert * bank.code_stride;
7588    let expert_scale_start = local_expert * bank.scale_stride;
7589    let weight = bank.codes.slice(
7590        expert_code_start + block * block_code_stride
7591            ..expert_code_start + (block + 1) * block_code_stride,
7592    );
7593    let scales = bank.scales.slice(
7594        expert_scale_start + block * block_scale_stride
7595            ..expert_scale_start + (block + 1) * block_scale_stride,
7596    );
7597    engine.qmatvec_mmq_fp8_blk_view(
7598        &weight,
7599        &scales,
7600        activations,
7601        1,
7602        FP8_BLOCK,
7603        bank.out_features,
7604    )
7605}
7606
7607fn configure_native_p2p(
7608    ranks: &[Engine],
7609    devices: &[usize],
7610) -> Result<(), Box<dyn std::error::Error>> {
7611    if ranks.len() != devices.len() || ranks.len() < 2 {
7612        return Err("native TP P2P setup requires matching multi-rank devices".into());
7613    }
7614    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
7615        if engine.ctx().ordinal() != device {
7616            return Err(format!(
7617                "native TP rank {rank} context device {} != requested device {device}",
7618                engine.ctx().ordinal()
7619            )
7620            .into());
7621        }
7622    }
7623
7624    for src in 0..ranks.len() {
7625        for dst in 0..ranks.len() {
7626            if src == dst {
7627                continue;
7628            }
7629            let mut can_access = 0;
7630            unsafe {
7631                cudarc::driver::sys::cuDeviceCanAccessPeer(
7632                    &mut can_access,
7633                    ranks[src].ctx().cu_device(),
7634                    ranks[dst].ctx().cu_device(),
7635                )
7636                .result()?;
7637            }
7638            if can_access == 0 {
7639                return Err(format!(
7640                    "native TP requires P2P, but dev{} cannot access dev{}",
7641                    devices[src], devices[dst]
7642                )
7643                .into());
7644            }
7645            ranks[src].ctx().bind_to_thread()?;
7646            let rc =
7647                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
7648            use cudarc::driver::sys::cudaError_enum as E;
7649            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
7650                return Err(format!(
7651                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
7652                    devices[src], devices[dst]
7653                )
7654                .into());
7655            }
7656        }
7657    }
7658
7659    for &owner in devices {
7660        for &accessor in devices {
7661            if owner == accessor {
7662                continue;
7663            }
7664            let device = cudarc::driver::result::device::get(owner as i32)?;
7665            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
7666            unsafe {
7667                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
7668            }
7669            let desc = cudarc::driver::sys::CUmemAccessDesc {
7670                location: cudarc::driver::sys::CUmemLocation {
7671                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
7672                    id: accessor as i32,
7673                },
7674                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
7675            };
7676            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
7677            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
7678                return Err(format!(
7679                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
7680                     {rc:?}"
7681                )
7682                .into());
7683            }
7684        }
7685    }
7686
7687    for src in 0..ranks.len() {
7688        for dst in 0..ranks.len() {
7689            if src == dst {
7690                continue;
7691            }
7692            let expected = (0..NATIVE_P2P_PROBE_WORDS)
7693                .map(|index| {
7694                    (index as u32)
7695                        .wrapping_mul(0x9e37_79b9)
7696                        .wrapping_add(((src as u32) << 16) | dst as u32)
7697                })
7698                .collect::<Vec<_>>();
7699            let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
7700            let source = ranks[src].htod_u32_v(&expected)?;
7701            let mut destination = ranks[dst].htod_u32_v(&poison)?;
7702            ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
7703            let actual = ranks[dst].dtoh_u32(&destination)?;
7704            if actual != expected {
7705                let mismatches = actual
7706                    .iter()
7707                    .zip(&expected)
7708                    .filter(|(actual, expected)| actual != expected)
7709                    .count();
7710                return Err(format!(
7711                    "native TP peer probe dev{}->dev{} failed: {mismatches}/{} words differ",
7712                    devices[src],
7713                    devices[dst],
7714                    expected.len()
7715                )
7716                .into());
7717            }
7718        }
7719    }
7720    ranks[0].ctx().bind_to_thread()?;
7721    eprintln!(
7722        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
7723         directions={} bytes={} mismatches=0",
7724        ranks.len() * (ranks.len() - 1),
7725        NATIVE_P2P_PROBE_WORDS * std::mem::size_of::<u32>(),
7726    );
7727    Ok(())
7728}
7729
7730fn validate_activations(
7731    activations: &[f32],
7732    tokens: usize,
7733    in_features: usize,
7734) -> Result<(), String> {
7735    let expected = tokens
7736        .checked_mul(in_features)
7737        .ok_or_else(|| "activation size overflow".to_string())?;
7738    if activations.len() != expected {
7739        return Err(format!(
7740            "activation count {} != {tokens}x{in_features} ({expected})",
7741            activations.len()
7742        ));
7743    }
7744    if !activations.iter().all(|value| value.is_finite()) {
7745        return Err("activations contain a non-finite value".to_string());
7746    }
7747    Ok(())
7748}
7749
7750fn column_shard(
7751    matrix: E4m3BlockMatrix<'_>,
7752    tp: usize,
7753    rank: usize,
7754) -> Result<E4m3BlockMatrix<'_>, String> {
7755    let local_out = matrix.out_features / tp;
7756    let row_start = rank * local_out;
7757    let code_start = row_start * matrix.in_features;
7758    let code_end = code_start + local_out * matrix.in_features;
7759    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
7760    let local_scale_rows = local_out / FP8_BLOCK;
7761    let scale_start = rank * local_scale_rows * scale_cols;
7762    let scale_end = scale_start + local_scale_rows * scale_cols;
7763    Ok(E4m3BlockMatrix {
7764        codes: &matrix.codes[code_start..code_end],
7765        scales: &matrix.scales[scale_start..scale_end],
7766        out_features: local_out,
7767        in_features: matrix.in_features,
7768    })
7769}
7770
7771fn row_shard(
7772    matrix: E4m3BlockMatrix<'_>,
7773    tp: usize,
7774    rank: usize,
7775) -> Result<(Vec<u8>, Vec<f32>), String> {
7776    let local_in = matrix.in_features / tp;
7777    let col_start = rank * local_in;
7778    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
7779    for row in 0..matrix.out_features {
7780        let start = row * matrix.in_features + col_start;
7781        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
7782    }
7783
7784    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
7785    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
7786    let local_scale_cols = local_in / FP8_BLOCK;
7787    let scale_col_start = rank * local_scale_cols;
7788    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
7789    for row in 0..scale_rows {
7790        let start = row * scale_cols + scale_col_start;
7791        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
7792    }
7793    Ok((codes, scales))
7794}
7795
7796fn activation_shard(
7797    activations: &[f32],
7798    tokens: usize,
7799    in_features: usize,
7800    tp: usize,
7801    rank: usize,
7802) -> Vec<f32> {
7803    let local_in = in_features / tp;
7804    let col_start = rank * local_in;
7805    let mut shard = Vec::with_capacity(tokens * local_in);
7806    for token in 0..tokens {
7807        let start = token * in_features + col_start;
7808        shard.extend_from_slice(&activations[start..start + local_in]);
7809    }
7810    shard
7811}
7812
7813// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
7814//
7815// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
7816// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
7817// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
7818// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
7819// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
7820// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
7821//
7822// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
7823// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
7824// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
7825// TP1-vs-TP2 bit gate. Every entry point below follows this order.
7826//
7827// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
7828// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
7829// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
7830
7831/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
7832#[derive(Clone, Copy)]
7833pub struct Nvfp4BlockMatrix<'a> {
7834    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
7835    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
7836    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
7837    pub out_features: usize,
7838    pub in_features: usize,
7839}
7840
7841impl Nvfp4BlockMatrix<'_> {
7842    pub fn validate(&self) -> Result<(), String> {
7843        if self.in_features == 0 || self.out_features == 0 {
7844            return Err("NVFP4 matrix has a zero dimension".to_string());
7845        }
7846        if self.in_features % 64 != 0 {
7847            return Err(format!(
7848                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
7849                self.in_features
7850            ));
7851        }
7852        if self.codes.len() != self.out_features * self.in_features / 2 {
7853            return Err(format!(
7854                "NVFP4 code bytes {} != {}x{}/2",
7855                self.codes.len(),
7856                self.out_features,
7857                self.in_features
7858            ));
7859        }
7860        if self.scales.len() != self.out_features * self.in_features / 16 {
7861            return Err(format!(
7862                "NVFP4 scale bytes {} != {}x{}/16",
7863                self.scales.len(),
7864                self.out_features,
7865                self.in_features
7866            ));
7867        }
7868        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
7869            return Err(format!(
7870                "NVFP4 macro scale {} is not finite-positive",
7871                self.macro_scale
7872            ));
7873        }
7874        Ok(())
7875    }
7876}
7877
7878/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
7879#[derive(Clone, Copy)]
7880pub struct Nvfp4ExpertBank<'a> {
7881    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
7882    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
7883    pub macros: &'a [f32], // [expert_count] weight_scale_2
7884    pub expert_count: usize,
7885    pub out_features: usize,
7886    pub in_features: usize,
7887}
7888
7889impl Nvfp4ExpertBank<'_> {
7890    pub fn validate(&self) -> Result<(), String> {
7891        if self.expert_count == 0 {
7892            return Err("NVFP4 expert bank is empty".to_string());
7893        }
7894        if self.macros.len() != self.expert_count {
7895            return Err(format!(
7896                "NVFP4 bank macros {} != expert count {}",
7897                self.macros.len(),
7898                self.expert_count
7899            ));
7900        }
7901        self.expert(0).map(|_| ())
7902    }
7903
7904    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
7905        if expert >= self.expert_count {
7906            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
7907        }
7908        let code_stride = self.out_features * self.in_features / 2;
7909        let scale_stride = self.out_features * self.in_features / 16;
7910        if self.codes.len() != self.expert_count * code_stride
7911            || self.scales.len() != self.expert_count * scale_stride
7912        {
7913            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
7914        }
7915        let matrix = Nvfp4BlockMatrix {
7916            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
7917            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
7918            macro_scale: self.macros[expert],
7919            out_features: self.out_features,
7920            in_features: self.in_features,
7921        };
7922        matrix.validate()?;
7923        Ok(matrix)
7924    }
7925}
7926
7927/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
7928pub struct ResidentNvfp4Rank {
7929    blocks: crate::CudaSlice<u8>,
7930    macro_scale: f32,
7931    out_features: usize,
7932    in_features: usize,
7933    row_bytes: usize,
7934}
7935
7936pub struct ResidentNvfp4ColumnParallel {
7937    ranks: Vec<ResidentNvfp4Rank>,
7938    pub out_features: usize,
7939    pub in_features: usize,
7940}
7941
7942pub struct ResidentNvfp4RowParallel {
7943    ranks: Vec<ResidentNvfp4Rank>,
7944    pub out_features: usize,
7945    pub in_features: usize,
7946}
7947
7948pub struct ResidentTpNvfp4Expert {
7949    gate: ResidentNvfp4ColumnParallel,
7950    up: ResidentNvfp4ColumnParallel,
7951    down: ResidentNvfp4RowParallel,
7952    pub input_width: usize,
7953    pub expert_width: usize,
7954}
7955
7956/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
7957/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
7958/// later perf rung, mirroring the FP8 bank's history).
7959pub struct ResidentNvfp4ColumnBankRank {
7960    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
7961    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
7962    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
7963    bank: crate::CudaSlice<u8>,
7964    expert_bytes: usize,
7965    local_out: usize,
7966    in_features: usize,
7967    row_bytes: usize,
7968}
7969
7970impl ResidentNvfp4ColumnBankRank {
7971    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
7972        self.bank
7973            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
7974    }
7975}
7976
7977/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
7978/// as exactly this many input-column windows summed in shard order, at every world size: a
7979/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
7980/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
7981/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
7982pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
7983
7984pub struct ResidentNvfp4RowBankRank {
7985    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
7986    bank: crate::CudaSlice<u8>,
7987    expert_bytes: usize,
7988    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
7989    out_features: usize,
7990    local_in: usize,
7991    row_bytes: usize,
7992}
7993
7994impl ResidentNvfp4RowBankRank {
7995    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
7996        self.bank
7997            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
7998    }
7999}
8000
8001impl ResidentNvfp4TensorParallel {
8002    pub(crate) fn device_workspace_handle(
8003        &self,
8004    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
8005        &self.device_workspace
8006    }
8007}
8008
8009pub struct ResidentNvfp4TensorParallel {
8010    gate: Vec<ResidentNvfp4ColumnBankRank>,
8011    up: Vec<ResidentNvfp4ColumnBankRank>,
8012    down: Vec<ResidentNvfp4RowBankRank>,
8013    macros_gate: Vec<f32>,
8014    macros_up: Vec<f32>,
8015    macros_down: Vec<f32>,
8016    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
8017    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
8018    /// into the route-weight axpy scalar.
8019    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
8020    macros_up_dev: Vec<crate::CudaSlice<f32>>,
8021    macros_down_dev: Vec<crate::CudaSlice<f32>>,
8022    pub expert_count: usize,
8023    pub input_width: usize,
8024    pub expert_width: usize,
8025    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
8026    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
8027    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
8028    /// Lazily-built spec-verify t=2 workspace (MEMRA_TCOL_FFN): the two-column routed
8029    /// sweep's slabs and events, kept apart from the serving workspace so the verify walk
8030    /// never perturbs serving state.
8031    t2_workspace: std::sync::Mutex<Option<Nvfp4T2Workspace>>,
8032}
8033
8034/// Persistent buffers for the two-column (spec verify) NVFP4 device-routed program: every
8035/// slab is the t=1 workspace shape doubled along the pair axis, plus per-column
8036/// accumulators. One per expert bank, reused every (round, layer) call.
8037pub struct Nvfp4T2Workspace {
8038    input2: Vec<crate::CudaSlice<f32>>,
8039    in_q2: Vec<crate::CudaSlice<i8>>,
8040    in_d2: Vec<crate::CudaSlice<f32>>,
8041    sel2: Vec<crate::CudaSlice<i32>>,
8042    route_w2: Vec<crate::CudaSlice<f32>>,
8043    gate_out2: Vec<crate::CudaSlice<f32>>,
8044    up_out2: Vec<crate::CudaSlice<f32>>,
8045    act_q2: Vec<crate::CudaSlice<i8>>,
8046    act_d2: Vec<crate::CudaSlice<f32>>,
8047    partial2: Vec<crate::CudaSlice<f32>>,
8048    /// Per-rank per-column combine accumulators ([width] each).
8049    acc_a: Vec<crate::CudaSlice<f32>>,
8050    acc_b: Vec<crate::CudaSlice<f32>>,
8051    /// Root-side pulls of rank1's accumulators and the joined columns.
8052    peer_a: crate::CudaSlice<f32>,
8053    peer_b: crate::CudaSlice<f32>,
8054    omix_a: crate::CudaSlice<f32>,
8055    omix_b: crate::CudaSlice<f32>,
8056    ev_entry: CudaEvent,
8057    ev_rank: Vec<CudaEvent>,
8058    ev_root: CudaEvent,
8059    n_sel: usize,
8060    e_device: usize,
8061}
8062
8063/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
8064/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
8065/// (token, layer) call so the decode loop performs zero output allocations.
8066/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
8067/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
8068/// conservatively) and the persistent e-context input staging its copies read.
8069struct RoutesGraph {
8070    exec: cudarc::driver::sys::CUgraphExec,
8071    parent: cudarc::driver::sys::CUgraph,
8072    _children: Vec<cudarc::driver::CudaGraph>,
8073}
8074// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
8075// context-agnostic process handles.
8076unsafe impl Send for RoutesGraph {}
8077
8078impl Drop for RoutesGraph {
8079    fn drop(&mut self) {
8080        unsafe {
8081            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
8082            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
8083        }
8084    }
8085}
8086
8087impl Nvfp4DeviceRoutesWorkspace {
8088    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
8089        self.in_stage_e.as_ref()
8090    }
8091    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8092        self.in_stage_e.as_mut()
8093    }
8094    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8095        self.out_stage_e.as_mut()
8096    }
8097    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
8098    pub(crate) fn arm_stages(
8099        &mut self,
8100        e: &Engine,
8101        width: usize,
8102        n_sel: usize,
8103    ) -> Result<(), Box<dyn std::error::Error>> {
8104        let _main = e.gpu.enter_main()?;
8105        if self.in_stage_e.is_none() {
8106            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8107            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8108        }
8109        if self.dev_route_e.is_none() {
8110            self.dev_route_e = Some((
8111                e.htod_i32(&vec![0i32; n_sel])?,
8112                e.htod(&vec![0.0f32; n_sel])?,
8113            ));
8114        }
8115        Ok(())
8116    }
8117
8118    /// Split-borrow: the routes input (shared) + output (mut) stages together.
8119    pub(crate) fn in_and_out_stages_mut(
8120        &mut self,
8121    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
8122        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
8123            (Some(input), Some(output)) => Some((input, output)),
8124            _ => None,
8125        }
8126    }
8127    pub(crate) fn dev_route_e_mut(
8128        &mut self,
8129    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
8130        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
8131    }
8132}
8133
8134pub struct Nvfp4DeviceRoutesWorkspace {
8135    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
8136    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
8137    gate_out: Vec<crate::CudaSlice<f32>>,
8138    up_out: Vec<crate::CudaSlice<f32>>,
8139    act_q: Vec<crate::CudaSlice<i8>>,
8140    act_d: Vec<crate::CudaSlice<f32>>,
8141    sel: Vec<crate::CudaSlice<i32>>,
8142    partial: Vec<crate::CudaSlice<f32>>,
8143    accumulator: Vec<crate::CudaSlice<f32>>,
8144    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
8145    combine_w: Vec<crate::CudaSlice<f32>>,
8146    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
8147    /// in-kernel via sel + macros_down_dev).
8148    route_w: Vec<crate::CudaSlice<f32>>,
8149    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
8150    /// per-call allocation).
8151    in_q: Vec<crate::CudaSlice<i8>>,
8152    in_d: Vec<crate::CudaSlice<f32>>,
8153    /// e-context staging for the device router outputs (persistent — rank streams peer-read
8154    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
8155    /// never-free discipline).
8156    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
8157    /// Prestage door state: input pull + quantize already issued for this layer's call
8158    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
8159    prestaged: bool,
8160    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
8161    /// the routed run skips rank1's sel pull. Reset per call.
8162    rank1_routed: bool,
8163    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
8164    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
8165    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
8166    fence_flags_raw: u64,
8167    fence_ticket: u32,
8168    /// Prestage input fence, recorded on e after the input's producer.
8169    ev_input: Option<(CudaEvent, usize)>,
8170    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
8171    /// captured copies read/write), and the per-layer stitched parent.
8172    in_stage_e: Option<crate::CudaSlice<f32>>,
8173    out_stage_e: Option<crate::CudaSlice<f32>>,
8174    routes_graph: Option<RoutesGraph>,
8175    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
8176    raw_dev_route_e: Option<(u64, u64)>,
8177    raw_combine: Option<(u64, u64, u64, u64)>,
8178    raw_input: Vec<u64>,
8179    raw_sel: Vec<u64>,
8180    raw_route_w: Vec<u64>,
8181    remote: crate::CudaSlice<f32>,
8182    combined: crate::CudaSlice<f32>,
8183    n_sel: usize,
8184    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
8185    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
8186    /// BoundarySlot discipline, same as the v2 attention workspace.
8187    input: Vec<crate::CudaSlice<f32>>,
8188    ev_rank: Vec<CudaEvent>,
8189    ev_done: Option<CudaEvent>,
8190    ev_entry: Option<(CudaEvent, usize)>,
8191}
8192
8193/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
8194struct ResidentNvfp4EpRank {
8195    gate: Vec<crate::CudaSlice<u8>>,
8196    up: Vec<crate::CudaSlice<u8>>,
8197    down: Vec<crate::CudaSlice<u8>>,
8198    #[allow(dead_code)]
8199    expert_range: Range<usize>,
8200}
8201
8202pub struct ResidentNvfp4ExpertParallel {
8203    ranks: Vec<ResidentNvfp4EpRank>,
8204    macros_gate: Vec<f32>,
8205    macros_up: Vec<f32>,
8206    macros_down: Vec<f32>,
8207    pub expert_count: usize,
8208    pub input_width: usize,
8209    pub expert_width: usize,
8210    gate_row_bytes: usize,
8211    down_row_bytes: usize,
8212}
8213
8214fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8215    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
8216        matrix.codes,
8217        matrix.scales,
8218        matrix.out_features,
8219        matrix.in_features,
8220    )
8221}
8222
8223fn nvfp4_row_bytes(in_features: usize) -> usize {
8224    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
8225}
8226
8227/// MEMRA_NVFP4_BANK_V2=1: store the contiguous expert banks in the slot-major layout the
8228/// coalesced `*_v2` kernels read (see qmatvec.cu). Pure byte permutation — value-exact.
8229/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
8230/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
8231/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
8232/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
8233/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
8234pub(crate) fn fuse_rope_append_on() -> bool {
8235    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8236    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
8237}
8238
8239pub(crate) fn no_local_shadow_on() -> bool {
8240    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8241    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
8242}
8243
8244pub(crate) fn nvfp4_bank_v2_on() -> bool {
8245    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8246    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_BANK_V2").as_deref() == Ok("1"))
8247}
8248
8249/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
8250/// into the slot-major v2 row layout: per row, slot g's 16 qs bytes at g*16, then the two
8251/// UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count unchanged.
8252fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
8253    let row_bytes = nvfp4_row_bytes(in_features);
8254    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
8255    let n_slots = in_features / 32;
8256    let mut out = Vec::with_capacity(v1.len());
8257    for row in 0..out_features {
8258        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
8259        for g in 0..n_slots {
8260            let (sblk, h) = (g / 2, g % 2);
8261            let b = &r[sblk * 36..sblk * 36 + 36];
8262            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
8263        }
8264        for g in 0..n_slots {
8265            let (sblk, h) = (g / 2, g % 2);
8266            let b = &r[sblk * 36..sblk * 36 + 36];
8267            out.push(b[2 * h]);
8268            out.push(b[2 * h + 1]);
8269        }
8270    }
8271    out
8272}
8273
8274/// Repack + (optionally) v2-permute one expert shard for the contiguous banks.
8275fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8276    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
8277    let v1 = nvfp4_repack_matrix(matrix);
8278    if nvfp4_bank_v2_on() {
8279        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
8280    } else {
8281        v1
8282    }
8283}
8284
8285/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
8286/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
8287fn nvfp4_column_shard<'a>(
8288    matrix: Nvfp4BlockMatrix<'a>,
8289    tp: usize,
8290    rank: usize,
8291) -> Result<Nvfp4BlockMatrix<'a>, String> {
8292    if matrix.out_features % tp != 0 {
8293        return Err(format!(
8294            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
8295            matrix.out_features
8296        ));
8297    }
8298    let local_out = matrix.out_features / tp;
8299    let code_row = matrix.in_features / 2;
8300    let scale_row = matrix.in_features / 16;
8301    Ok(Nvfp4BlockMatrix {
8302        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
8303        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
8304        macro_scale: matrix.macro_scale,
8305        out_features: local_out,
8306        in_features: matrix.in_features,
8307    })
8308}
8309
8310/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
8311/// row contributes one contiguous byte window, gathered across rows.
8312fn nvfp4_row_shard(
8313    matrix: Nvfp4BlockMatrix<'_>,
8314    tp: usize,
8315    rank: usize,
8316) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
8317    if matrix.in_features % tp != 0 {
8318        return Err(format!(
8319            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
8320            matrix.in_features
8321        ));
8322    }
8323    let local_in = matrix.in_features / tp;
8324    if local_in % 64 != 0 {
8325        return Err(format!(
8326            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
8327        ));
8328    }
8329    let code_row = matrix.in_features / 2;
8330    let scale_row = matrix.in_features / 16;
8331    let local_code = local_in / 2;
8332    let local_scale = local_in / 16;
8333    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
8334    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
8335    for row in 0..matrix.out_features {
8336        let code_start = row * code_row + rank * local_code;
8337        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
8338        let scale_start = row * scale_row + rank * local_scale;
8339        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
8340    }
8341    Ok((codes, scales, local_in))
8342}
8343
8344/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
8345/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
8346/// point (see the section header).
8347fn run_rank_nvfp4(
8348    engine: &Engine,
8349    matrix: Nvfp4BlockMatrix<'_>,
8350    activations: &[f32],
8351    tokens: usize,
8352) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8353    matrix.validate()?;
8354    validate_activations(activations, tokens, matrix.in_features)?;
8355    let _main = engine.gpu.enter_main()?;
8356    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
8357    let activations = engine.htod(activations)?;
8358    let output = engine.qmatvec_nvfp4_fast(
8359        &blocks.slice(0..blocks.len()),
8360        &activations,
8361        tokens,
8362        matrix.in_features,
8363        matrix.out_features,
8364        nvfp4_row_bytes(matrix.in_features),
8365    )?;
8366    engine.dtoh(&output)
8367}
8368
8369fn upload_rank_nvfp4(
8370    engine: &Engine,
8371    matrix: Nvfp4BlockMatrix<'_>,
8372) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
8373    matrix.validate()?;
8374    let _main = engine.gpu.enter_main()?;
8375    Ok(ResidentNvfp4Rank {
8376        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
8377        macro_scale: matrix.macro_scale,
8378        out_features: matrix.out_features,
8379        in_features: matrix.in_features,
8380        row_bytes: nvfp4_row_bytes(matrix.in_features),
8381    })
8382}
8383
8384fn run_resident_rank_nvfp4(
8385    engine: &Engine,
8386    rank: &ResidentNvfp4Rank,
8387    activations: &[f32],
8388    tokens: usize,
8389) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8390    validate_activations(activations, tokens, rank.in_features)?;
8391    let _main = engine.gpu.enter_main()?;
8392    let activations = engine.htod(activations)?;
8393    let output = engine.qmatvec_nvfp4_fast(
8394        &rank.blocks.slice(0..rank.blocks.len()),
8395        &activations,
8396        tokens,
8397        rank.in_features,
8398        rank.out_features,
8399        rank.row_bytes,
8400    )?;
8401    engine.dtoh(&output)
8402}
8403
8404fn apply_macro(values: &mut [f32], macro_scale: f32) {
8405    for value in values.iter_mut() {
8406        *value *= macro_scale;
8407    }
8408}
8409
8410impl TpE4m3HostBounce {
8411    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
8412    pub fn full_nvfp4(
8413        &self,
8414        matrix: Nvfp4BlockMatrix<'_>,
8415        activations: &[f32],
8416        tokens: usize,
8417    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8418        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
8419        apply_macro(&mut output, matrix.macro_scale);
8420        Ok(output)
8421    }
8422
8423    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
8424    /// order, macro applied ONCE post-gather.
8425    pub fn column_parallel_nvfp4(
8426        &self,
8427        matrix: Nvfp4BlockMatrix<'_>,
8428        activations: &[f32],
8429        tokens: usize,
8430    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
8431        matrix.validate()?;
8432        validate_activations(activations, tokens, matrix.in_features)?;
8433        let tp = self.ranks.len();
8434        let local_out = matrix.out_features / tp;
8435        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
8436        let mut rank_outputs = Vec::with_capacity(tp);
8437        for (rank_index, rank) in self.ranks.iter().enumerate() {
8438            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
8439            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
8440            let row_start = rank_index * local_out;
8441            for token in 0..tokens {
8442                gathered[token * matrix.out_features + row_start
8443                    ..token * matrix.out_features + row_start + local_out]
8444                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
8445            }
8446            rank_outputs.push(output);
8447        }
8448        apply_macro(&mut gathered, matrix.macro_scale);
8449        Ok(ColumnParallelResult {
8450            gathered,
8451            rank_outputs,
8452        })
8453    }
8454
8455    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
8456    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
8457    pub fn row_parallel_nvfp4(
8458        &self,
8459        matrix: Nvfp4BlockMatrix<'_>,
8460        activations: &[f32],
8461        tokens: usize,
8462    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
8463        matrix.validate()?;
8464        validate_activations(activations, tokens, matrix.in_features)?;
8465        let tp = self.ranks.len();
8466        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
8467        let mut rank_partials = Vec::with_capacity(tp);
8468        for (rank_index, rank) in self.ranks.iter().enumerate() {
8469            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
8470            let local_activations =
8471                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
8472            let shard = Nvfp4BlockMatrix {
8473                codes: &codes,
8474                scales: &scales,
8475                macro_scale: matrix.macro_scale,
8476                out_features: matrix.out_features,
8477                in_features: local_in,
8478            };
8479            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
8480            for (sum, value) in reduced.iter_mut().zip(&partial) {
8481                *sum += *value;
8482            }
8483            rank_partials.push(partial);
8484        }
8485        apply_macro(&mut reduced, matrix.macro_scale);
8486        Ok(RowParallelResult {
8487            reduced,
8488            rank_partials,
8489        })
8490    }
8491
8492    pub fn upload_expert_nvfp4(
8493        &self,
8494        gate: Nvfp4BlockMatrix<'_>,
8495        up: Nvfp4BlockMatrix<'_>,
8496        down: Nvfp4BlockMatrix<'_>,
8497    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
8498        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8499            return Err("NVFP4 TP expert gate/up dimensions differ".into());
8500        }
8501        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8502            return Err(format!(
8503                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
8504                down.out_features, down.in_features, gate.out_features, gate.in_features
8505            )
8506            .into());
8507        }
8508        let tp = self.ranks.len();
8509        let mut gate_ranks = Vec::with_capacity(tp);
8510        let mut up_ranks = Vec::with_capacity(tp);
8511        let mut down_ranks = Vec::with_capacity(tp);
8512        for (rank_index, engine) in self.ranks.iter().enumerate() {
8513            gate_ranks.push(upload_rank_nvfp4(
8514                engine,
8515                nvfp4_column_shard(gate, tp, rank_index)?,
8516            )?);
8517            up_ranks.push(upload_rank_nvfp4(
8518                engine,
8519                nvfp4_column_shard(up, tp, rank_index)?,
8520            )?);
8521            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
8522            down_ranks.push(upload_rank_nvfp4(
8523                engine,
8524                Nvfp4BlockMatrix {
8525                    codes: &codes,
8526                    scales: &scales,
8527                    macro_scale: down.macro_scale,
8528                    out_features: down.out_features,
8529                    in_features: local_in,
8530                },
8531            )?);
8532        }
8533        Ok(ResidentTpNvfp4Expert {
8534            gate: ResidentNvfp4ColumnParallel {
8535                ranks: gate_ranks,
8536                out_features: gate.out_features,
8537                in_features: gate.in_features,
8538            },
8539            up: ResidentNvfp4ColumnParallel {
8540                ranks: up_ranks,
8541                out_features: up.out_features,
8542                in_features: up.in_features,
8543            },
8544            down: ResidentNvfp4RowParallel {
8545                ranks: down_ranks,
8546                out_features: down.out_features,
8547                in_features: down.in_features,
8548            },
8549            input_width: gate.in_features,
8550            expert_width: gate.out_features,
8551        })
8552    }
8553
8554    fn column_parallel_resident_nvfp4(
8555        &self,
8556        matrix: &ResidentNvfp4ColumnParallel,
8557        activations: &[f32],
8558        tokens: usize,
8559    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8560        validate_activations(activations, tokens, matrix.in_features)?;
8561        let local_out = matrix.out_features / self.ranks.len();
8562        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
8563        let mut macro_scale = None;
8564        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
8565            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
8566            let row_start = rank_index * local_out;
8567            for token in 0..tokens {
8568                gathered[token * matrix.out_features + row_start
8569                    ..token * matrix.out_features + row_start + local_out]
8570                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
8571            }
8572            macro_scale = Some(shard.macro_scale);
8573        }
8574        apply_macro(
8575            &mut gathered,
8576            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
8577        );
8578        Ok(gathered)
8579    }
8580
8581    fn row_parallel_resident_nvfp4(
8582        &self,
8583        matrix: &ResidentNvfp4RowParallel,
8584        activations: &[f32],
8585        tokens: usize,
8586    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8587        validate_activations(activations, tokens, matrix.in_features)?;
8588        let tp = self.ranks.len();
8589        let local_in = matrix.in_features / tp;
8590        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
8591        let mut macro_scale = None;
8592        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
8593            if shard.in_features != local_in {
8594                return Err(format!(
8595                    "NVFP4 resident row shard in_features {} != expected {local_in}",
8596                    shard.in_features
8597                )
8598                .into());
8599            }
8600            let local_activations =
8601                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
8602            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
8603            for (sum, value) in reduced.iter_mut().zip(&partial) {
8604                *sum += *value;
8605            }
8606            macro_scale = Some(shard.macro_scale);
8607        }
8608        apply_macro(
8609            &mut reduced,
8610            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
8611        );
8612        Ok(reduced)
8613    }
8614
8615    pub fn run_expert_nvfp4(
8616        &self,
8617        expert: &ResidentTpNvfp4Expert,
8618        input: &[f32],
8619        tokens: usize,
8620    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8621        validate_activations(input, tokens, expert.input_width)?;
8622        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
8623        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
8624        let activated: Vec<f32> = gate
8625            .iter()
8626            .zip(&up)
8627            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
8628            .collect();
8629        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
8630        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
8631    }
8632
8633    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
8634    pub fn upload_tensor_parallel_nvfp4(
8635        &self,
8636        gate: Nvfp4ExpertBank<'_>,
8637        up: Nvfp4ExpertBank<'_>,
8638        down: Nvfp4ExpertBank<'_>,
8639    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
8640        gate.validate()?;
8641        up.validate()?;
8642        down.validate()?;
8643        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
8644            return Err("NVFP4 TP gate/up/down expert counts differ".into());
8645        }
8646        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8647            return Err("NVFP4 TP gate/up dimensions differ".into());
8648        }
8649        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8650            return Err(format!(
8651                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
8652                down.out_features, down.in_features, gate.out_features, gate.in_features
8653            )
8654            .into());
8655        }
8656        let tp = self.ranks.len();
8657        if gate.out_features % tp != 0 {
8658            return Err(format!(
8659                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
8660                gate.out_features
8661            )
8662            .into());
8663        }
8664        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
8665            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
8666        {
8667            return Err(format!(
8668                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
8669                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
8670                down.in_features
8671            )
8672            .into());
8673        }
8674        if tp > NVFP4_CANONICAL_ROW_SHARDS {
8675            return Err(format!(
8676                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
8677                 ({NVFP4_CANONICAL_ROW_SHARDS})"
8678            )
8679            .into());
8680        }
8681
8682        let mut gate_ranks = Vec::with_capacity(tp);
8683        let mut up_ranks = Vec::with_capacity(tp);
8684        let mut macros_gate_dev = Vec::with_capacity(tp);
8685        let mut macros_up_dev = Vec::with_capacity(tp);
8686        let mut macros_down_dev = Vec::with_capacity(tp);
8687        for (rank_index, engine) in self.ranks.iter().enumerate() {
8688            let _main = engine.gpu.enter_main()?;
8689            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
8690            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
8691            // are unchanged (same repack).
8692            let mut gate_host: Vec<u8> = Vec::new();
8693            let mut up_host: Vec<u8> = Vec::new();
8694            for expert in 0..gate.expert_count {
8695                let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
8696                gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard));
8697                let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
8698                up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard));
8699            }
8700            let gate_expert_bytes = gate_host.len() / gate.expert_count;
8701            let up_expert_bytes = up_host.len() / up.expert_count;
8702            gate_ranks.push(ResidentNvfp4ColumnBankRank {
8703                bank: engine.htod_bytes(&gate_host)?,
8704                expert_bytes: gate_expert_bytes,
8705                local_out: gate.out_features / tp,
8706                in_features: gate.in_features,
8707                row_bytes: nvfp4_row_bytes(gate.in_features),
8708            });
8709            up_ranks.push(ResidentNvfp4ColumnBankRank {
8710                bank: engine.htod_bytes(&up_host)?,
8711                expert_bytes: up_expert_bytes,
8712                local_out: up.out_features / tp,
8713                in_features: up.in_features,
8714                row_bytes: nvfp4_row_bytes(up.in_features),
8715            });
8716            macros_gate_dev.push(engine.htod(gate.macros)?);
8717            macros_up_dev.push(engine.htod(up.macros)?);
8718            macros_down_dev.push(engine.htod(down.macros)?);
8719        }
8720        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
8721        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
8722        // execution and reduction order stay identical.
8723        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
8724        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
8725            let device_rank = shard_index % tp;
8726            let engine = &self.ranks[device_rank];
8727            let _main = engine.gpu.enter_main()?;
8728            let mut down_host: Vec<u8> = Vec::new();
8729            for expert in 0..down.expert_count {
8730                let down_matrix = down.expert(expert)?;
8731                let (codes, scales, local_in) =
8732                    nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
8733                down_host.extend_from_slice(&nvfp4_repack_bank_matrix(Nvfp4BlockMatrix {
8734                    codes: &codes,
8735                    scales: &scales,
8736                    macro_scale: down_matrix.macro_scale,
8737                    out_features: down_matrix.out_features,
8738                    in_features: local_in,
8739                }));
8740            }
8741            let down_expert_bytes = down_host.len() / down.expert_count;
8742            down_ranks.push(ResidentNvfp4RowBankRank {
8743                bank: engine.htod_bytes(&down_host)?,
8744                expert_bytes: down_expert_bytes,
8745                device_rank,
8746                out_features: down.out_features,
8747                local_in: down.in_features / NVFP4_CANONICAL_ROW_SHARDS,
8748                row_bytes: nvfp4_row_bytes(down.in_features / NVFP4_CANONICAL_ROW_SHARDS),
8749            });
8750        }
8751        Ok(ResidentNvfp4TensorParallel {
8752            gate: gate_ranks,
8753            up: up_ranks,
8754            down: down_ranks,
8755            macros_gate: gate.macros.to_vec(),
8756            macros_up: up.macros.to_vec(),
8757            macros_down: down.macros.to_vec(),
8758            macros_gate_dev,
8759            macros_up_dev,
8760            macros_down_dev,
8761            expert_count: gate.expert_count,
8762            input_width: gate.in_features,
8763            expert_width: gate.out_features,
8764            device_workspace: std::sync::Mutex::new(None),
8765            t2_workspace: std::sync::Mutex::new(None),
8766        })
8767    }
8768
8769    fn run_column_bank_expert_nvfp4(
8770        &self,
8771        ranks: &[ResidentNvfp4ColumnBankRank],
8772        macros: &[f32],
8773        expert: usize,
8774        input: &[f32],
8775    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8776        let local_out = ranks
8777            .first()
8778            .ok_or("NVFP4 TP column bank has no ranks")?
8779            .local_out;
8780        let mut gathered = vec![0.0f32; local_out * ranks.len()];
8781        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
8782            let _main = engine.gpu.enter_main()?;
8783            let activations = engine.htod(input)?;
8784            let output = if nvfp4_bank_v2_on() {
8785                engine.qmatvec_nvfp4_fast_v2(
8786                    &bank.expert(expert),
8787                    &activations,
8788                    1,
8789                    bank.in_features,
8790                    bank.local_out,
8791                    bank.row_bytes,
8792                )?
8793            } else {
8794                engine.qmatvec_nvfp4_fast(
8795                    &bank.expert(expert),
8796                    &activations,
8797                    1,
8798                    bank.in_features,
8799                    bank.local_out,
8800                    bank.row_bytes,
8801                )?
8802            };
8803            let output = engine.dtoh(&output)?;
8804            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
8805        }
8806        apply_macro(&mut gathered, macros[expert]);
8807        Ok(gathered)
8808    }
8809
8810    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
8811    /// executes on its owning rank engine), so the reduction parenthesization is identical at
8812    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
8813    fn run_row_bank_expert_nvfp4(
8814        &self,
8815        shards: &[ResidentNvfp4RowBankRank],
8816        macros: &[f32],
8817        expert: usize,
8818        input: &[f32],
8819    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8820        let out_features = shards
8821            .first()
8822            .ok_or("NVFP4 TP row bank has no canonical shards")?
8823            .out_features;
8824        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
8825        let mut reduced = vec![0.0f32; out_features];
8826        for (shard_index, shard) in shards.iter().enumerate() {
8827            let engine = self
8828                .ranks
8829                .get(shard.device_rank)
8830                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
8831            let _main = engine.gpu.enter_main()?;
8832            let local_activations =
8833                activation_shard(input, 1, in_features, shards.len(), shard_index);
8834            let activations = engine.htod(&local_activations)?;
8835            let output = if nvfp4_bank_v2_on() {
8836                engine.qmatvec_nvfp4_fast_v2(
8837                    &shard.expert(expert),
8838                    &activations,
8839                    1,
8840                    shard.local_in,
8841                    shard.out_features,
8842                    shard.row_bytes,
8843                )?
8844            } else {
8845                engine.qmatvec_nvfp4_fast(
8846                    &shard.expert(expert),
8847                    &activations,
8848                    1,
8849                    shard.local_in,
8850                    shard.out_features,
8851                    shard.row_bytes,
8852                )?
8853            };
8854            let partial = engine.dtoh(&output)?;
8855            for (sum, value) in reduced.iter_mut().zip(&partial) {
8856                *sum += *value;
8857            }
8858        }
8859        apply_macro(&mut reduced, macros[expert]);
8860        Ok(reduced)
8861    }
8862
8863    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
8864    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
8865    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
8866    pub fn upload_expert_parallel_nvfp4(
8867        &self,
8868        gate: Nvfp4ExpertBank<'_>,
8869        up: Nvfp4ExpertBank<'_>,
8870        down: Nvfp4ExpertBank<'_>,
8871    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
8872        gate.validate()?;
8873        up.validate()?;
8874        down.validate()?;
8875        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
8876            return Err("NVFP4 EP gate/up/down expert counts differ".into());
8877        }
8878        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8879            return Err("NVFP4 EP gate/up dimensions differ".into());
8880        }
8881        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8882            return Err(format!(
8883                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
8884                down.out_features, down.in_features, gate.out_features, gate.in_features
8885            )
8886            .into());
8887        }
8888        let world = self.ranks.len();
8889        if gate.expert_count % world != 0 {
8890            return Err(format!(
8891                "NVFP4 EP expert count {} is not divisible by {world} ranks",
8892                gate.expert_count
8893            )
8894            .into());
8895        }
8896        let experts_per_rank = gate.expert_count / world;
8897        let mut ranks = Vec::with_capacity(world);
8898        for (rank_index, engine) in self.ranks.iter().enumerate() {
8899            let _main = engine.gpu.enter_main()?;
8900            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
8901            let mut gate_experts = Vec::with_capacity(experts_per_rank);
8902            let mut up_experts = Vec::with_capacity(experts_per_rank);
8903            let mut down_experts = Vec::with_capacity(experts_per_rank);
8904            for expert in expert_range.clone() {
8905                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
8906                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
8907                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
8908            }
8909            ranks.push(ResidentNvfp4EpRank {
8910                gate: gate_experts,
8911                up: up_experts,
8912                down: down_experts,
8913                expert_range,
8914            });
8915        }
8916        Ok(ResidentNvfp4ExpertParallel {
8917            ranks,
8918            macros_gate: gate.macros.to_vec(),
8919            macros_up: up.macros.to_vec(),
8920            macros_down: down.macros.to_vec(),
8921            expert_count: gate.expert_count,
8922            input_width: gate.in_features,
8923            expert_width: gate.out_features,
8924            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
8925            down_row_bytes: nvfp4_row_bytes(down.in_features),
8926        })
8927    }
8928
8929    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
8930    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
8931    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
8932    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
8933    /// contract. Exactness-first; no throughput claim.
8934    #[allow(clippy::too_many_arguments)]
8935    pub fn run_routed_experts_nvfp4(
8936        &self,
8937        experts: &ResidentNvfp4ExpertParallel,
8938        input: &[f32],
8939        tokens: usize,
8940        selected: &[usize],
8941        route_weights: &[f32],
8942        experts_per_token: usize,
8943        activation_limit: Option<f32>,
8944    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8945        validate_activations(input, tokens, experts.input_width)?;
8946        let pairs = tokens
8947            .checked_mul(experts_per_token)
8948            .ok_or("NVFP4 EP route count overflow")?;
8949        if selected.len() != pairs || route_weights.len() != pairs {
8950            return Err(format!(
8951                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
8952                 {experts_per_token} ({pairs})",
8953                selected.len(),
8954                route_weights.len(),
8955            )
8956            .into());
8957        }
8958        if !route_weights.iter().all(|weight| weight.is_finite()) {
8959            return Err("NVFP4 EP route weights contain a non-finite value".into());
8960        }
8961        let experts_per_rank = experts.expert_count / experts.ranks.len();
8962        let mut output = vec![0.0f32; tokens * experts.input_width];
8963        for token in 0..tokens {
8964            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
8965            for slot in 0..experts_per_token {
8966                let pair = token * experts_per_token + slot;
8967                let expert = selected[pair];
8968                if expert >= experts.expert_count {
8969                    return Err(format!(
8970                        "NVFP4 EP selected expert {expert} outside 0..{}",
8971                        experts.expert_count
8972                    )
8973                    .into());
8974                }
8975                let owner = expert / experts_per_rank;
8976                let local = expert - owner * experts_per_rank;
8977                let rank = &experts.ranks[owner];
8978                let engine = &self.ranks[owner];
8979                let _main = engine.gpu.enter_main()?;
8980                let device_input = engine.htod(input_row)?;
8981                let gate_out = engine.qmatvec_nvfp4_fast(
8982                    &rank.gate[local].slice(0..rank.gate[local].len()),
8983                    &device_input,
8984                    1,
8985                    experts.input_width,
8986                    experts.expert_width,
8987                    experts.gate_row_bytes,
8988                )?;
8989                let up_out = engine.qmatvec_nvfp4_fast(
8990                    &rank.up[local].slice(0..rank.up[local].len()),
8991                    &device_input,
8992                    1,
8993                    experts.input_width,
8994                    experts.expert_width,
8995                    experts.gate_row_bytes,
8996                )?;
8997                let mut gate_host = engine.dtoh(&gate_out)?;
8998                let mut up_host = engine.dtoh(&up_out)?;
8999                apply_macro(&mut gate_host, experts.macros_gate[expert]);
9000                apply_macro(&mut up_host, experts.macros_up[expert]);
9001                let activated: Vec<f32> = gate_host
9002                    .iter()
9003                    .zip(&up_host)
9004                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
9005                    .collect();
9006                let device_activated = engine.htod(&activated)?;
9007                let down_out = engine.qmatvec_nvfp4_fast(
9008                    &rank.down[local].slice(0..rank.down[local].len()),
9009                    &device_activated,
9010                    1,
9011                    experts.expert_width,
9012                    experts.input_width,
9013                    experts.down_row_bytes,
9014                )?;
9015                let mut down_host = engine.dtoh(&down_out)?;
9016                apply_macro(&mut down_host, experts.macros_down[expert]);
9017                let weight = route_weights[pair];
9018                for (sum, value) in output
9019                    [token * experts.input_width..(token + 1) * experts.input_width]
9020                    .iter_mut()
9021                    .zip(down_host)
9022                {
9023                    *sum += weight * value;
9024                }
9025            }
9026        }
9027        Ok(output)
9028    }
9029
9030    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
9031    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
9032    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
9033    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
9034    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
9035    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
9036    ///
9037    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
9038    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
9039    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
9040    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
9041    /// host-canonical oracle, and with repeat determinism against itself.
9042    /// Clamped layers refuse (they stay on the EP program).
9043    pub fn run_tensor_parallel_routes_nvfp4_device(
9044        &self,
9045        experts: &ResidentNvfp4TensorParallel,
9046        input: &[f32],
9047        selected: &[usize],
9048        route_weights: &[f32],
9049        experts_per_token: usize,
9050        activation_limit: Option<f32>,
9051    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9052        validate_activations(input, 1, experts.input_width)?;
9053        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9054            return Err(format!(
9055                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
9056                selected.len(),
9057                route_weights.len(),
9058            )
9059            .into());
9060        }
9061        if !route_weights.iter().all(|weight| weight.is_finite()) {
9062            return Err("NVFP4 device route weights contain a non-finite value".into());
9063        }
9064        let world = self.ranks.len();
9065        if world != NVFP4_CANONICAL_ROW_SHARDS {
9066            return Err(format!(
9067                "NVFP4 device routes require world == canonical shard grid \
9068                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
9069            )
9070            .into());
9071        }
9072        let local_out = experts.expert_width / world;
9073
9074        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
9075        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
9076        // everything else without Nsight.
9077        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9078        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9079        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9080        let started = timing.then(std::time::Instant::now);
9081
9082        let n_sel = experts_per_token;
9083        let mut workspace_guard = experts
9084            .device_workspace
9085            .lock()
9086            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9087        if workspace_guard.is_none() {
9088            let mut gate_out = Vec::with_capacity(world);
9089            let mut up_out = Vec::with_capacity(world);
9090            let mut act_q = Vec::with_capacity(world);
9091            let mut act_d = Vec::with_capacity(world);
9092            let mut sel = Vec::with_capacity(world);
9093            let mut partial = Vec::with_capacity(world);
9094            let mut accumulator = Vec::with_capacity(world);
9095            let mut combine_w = Vec::with_capacity(world);
9096            let mut route_w = Vec::with_capacity(world);
9097            let mut in_q = Vec::with_capacity(world);
9098            let mut in_d = Vec::with_capacity(world);
9099            let mut input = Vec::with_capacity(world);
9100            let mut ev_rank = Vec::with_capacity(world);
9101            let moe_direct = moe_direct_on();
9102            for (rank, engine) in self.ranks.iter().enumerate() {
9103                let _main = engine.gpu.enter_main()?;
9104                gate_out.push(engine.uninit(n_sel * local_out)?);
9105                up_out.push(engine.uninit(n_sel * local_out)?);
9106                act_q.push(engine.uninit_i8(n_sel * local_out)?);
9107                act_d.push(engine.uninit(n_sel * local_out / 32)?);
9108                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
9109                partial.push(engine.uninit(n_sel * experts.input_width)?);
9110                // Direct join: peer accumulators live on ROOT (single P2P store pass).
9111                if moe_direct && rank != 0 {
9112                    let root = &self.ranks[0];
9113                    let _root_main = root.gpu.enter_main()?;
9114                    accumulator.push(root.zeros(experts.input_width)?);
9115                } else {
9116                    accumulator.push(engine.zeros(experts.input_width)?);
9117                }
9118                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9119                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9120                in_q.push(engine.uninit_i8(experts.input_width)?);
9121                in_d.push(engine.uninit(experts.input_width / 32)?);
9122                input.push(engine.uninit(experts.input_width)?);
9123                ev_rank.push(engine.ctx().new_event(None)?);
9124            }
9125            let root = &self.ranks[0];
9126            let _main = root.gpu.enter_main()?;
9127            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
9128                prestaged: false,
9129                rank1_routed: false,
9130                ev_input: None,
9131                fence_flags_raw: 0,
9132                fence_ticket: 0,
9133                gate_out,
9134                up_out,
9135                act_q,
9136                act_d,
9137                sel,
9138                partial,
9139                accumulator,
9140                combine_w,
9141                route_w,
9142                in_q,
9143                in_d,
9144                dev_route_e: None,
9145                in_stage_e: None,
9146                out_stage_e: None,
9147                routes_graph: None,
9148                raw_dev_route_e: None,
9149                raw_combine: None,
9150                raw_input: Vec::new(),
9151                raw_sel: Vec::new(),
9152                raw_route_w: Vec::new(),
9153                remote: root.uninit(experts.input_width)?,
9154                combined: root.uninit(experts.input_width)?,
9155                n_sel,
9156                input,
9157                ev_rank,
9158                ev_done: Some(root.ctx().new_event(None)?),
9159                ev_entry: None,
9160            });
9161        }
9162        let workspace = workspace_guard
9163            .as_mut()
9164            .expect("NVFP4 device routes workspace initialized above");
9165        if workspace.n_sel != n_sel {
9166            return Err(format!(
9167                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
9168                workspace.n_sel
9169            )
9170            .into());
9171        }
9172        for &expert in selected {
9173            if expert >= experts.expert_count {
9174                return Err(format!(
9175                    "NVFP4 device selected expert {expert} outside 0..{}",
9176                    experts.expert_count
9177                )
9178                .into());
9179            }
9180        }
9181        let sel_i32 = selected
9182            .iter()
9183            .map(|&expert| expert as i32)
9184            .collect::<Vec<_>>();
9185
9186        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
9187        // down) covers every selected expert via the selection array and the contiguous bank —
9188        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
9189        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
9190        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
9191        // accumulation order — the program's values are unchanged.
9192        for (rank_index, engine) in self.ranks.iter().enumerate() {
9193            let _main = engine.gpu.enter_main()?;
9194            let device_input = engine.htod(input)?;
9195            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
9196            engine.quantize_q8_1_into(
9197                &device_input,
9198                1,
9199                experts.input_width,
9200                &mut in_q[rank_index],
9201                &mut in_d[rank_index],
9202            )?;
9203            // device_input frees on this rank's stream after the quantize — same-stream order.
9204        }
9205        self.nvfp4_routes_batched_sweeps(
9206            experts,
9207            workspace,
9208            selected,
9209            route_weights,
9210            &sel_i32,
9211            local_out,
9212            n_sel,
9213            activation_limit,
9214            false,
9215        )?;
9216
9217        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
9218        // reduce in canonical shard order, read back once.
9219        let root = &self.ranks[0];
9220        for engine in &self.ranks[1..] {
9221            let _main = engine.gpu.enter_main()?;
9222            engine.stream().synchronize()?;
9223        }
9224        let _main = root.gpu.enter_main()?;
9225        root.stream()
9226            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
9227        root.add(
9228            &workspace.accumulator[0],
9229            &workspace.remote,
9230            &mut workspace.combined,
9231            experts.input_width,
9232        )?;
9233        let output = root.dtoh(&workspace.combined)?;
9234        if let Some(started) = started {
9235            use std::sync::atomic::Ordering;
9236            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9237                + started.elapsed().as_nanos() as u64;
9238            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9239            if calls % 430 == 0 {
9240                eprintln!(
9241                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9242                    ns as f64 / 1.0e6,
9243                    ns as f64 / calls as f64 / 1.0e3,
9244                );
9245            }
9246        }
9247        Ok(output)
9248    }
9249
9250    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
9251    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
9252    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
9253    /// owning rank's stream; callers own input acquisition and the combine.
9254    #[allow(clippy::too_many_arguments)]
9255    fn nvfp4_routes_batched_sweeps(
9256        &self,
9257        experts: &ResidentNvfp4TensorParallel,
9258        workspace: &mut Nvfp4DeviceRoutesWorkspace,
9259        selected: &[usize],
9260        route_weights: &[f32],
9261        sel_i32: &[i32],
9262        local_out: usize,
9263        n_sel: usize,
9264        activation_limit: Option<f32>,
9265        device_routed: bool,
9266    ) -> Result<(), Box<dyn std::error::Error>> {
9267        for rank_index in 0..self.ranks.len() {
9268            self.nvfp4_routes_batched_sweeps_rank(
9269                experts,
9270                workspace,
9271                selected,
9272                route_weights,
9273                sel_i32,
9274                local_out,
9275                n_sel,
9276                activation_limit,
9277                device_routed,
9278                rank_index,
9279            )?;
9280        }
9281        Ok(())
9282    }
9283
9284    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
9285    /// the graph door can capture each rank's segment on its own stream.
9286    #[allow(clippy::too_many_arguments)]
9287    fn nvfp4_routes_batched_sweeps_rank(
9288        &self,
9289        experts: &ResidentNvfp4TensorParallel,
9290        workspace: &mut Nvfp4DeviceRoutesWorkspace,
9291        selected: &[usize],
9292        route_weights: &[f32],
9293        sel_i32: &[i32],
9294        local_out: usize,
9295        n_sel: usize,
9296        activation_limit: Option<f32>,
9297        device_routed: bool,
9298        rank_index: usize,
9299    ) -> Result<(), Box<dyn std::error::Error>> {
9300        {
9301            let engine = &self.ranks[rank_index];
9302            let _main = engine.gpu.enter_main()?;
9303            if !device_routed {
9304                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
9305                // Folded combine weights (route_weight x down macro) — one 40-byte upload
9306                // replaces the accumulator reset + n_sel sequential axpy launches below.
9307                let folded = (0..n_sel)
9308                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
9309                    .collect::<Vec<_>>();
9310                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
9311                engine.stream().memcpy_htod(&folded, &mut view)?;
9312            }
9313            let gate_bank = &experts.gate[rank_index];
9314            let up_bank = &experts.up[rank_index];
9315            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
9316            // FUSION #2a (v2 banks): the two sweeps share sel/aq/ad and identical geometry
9317            // — one launch, per-row bit-identical, double the grid fill.
9318            let gu_fused = nvfp4_bank_v2_on()
9319                && gate_bank.in_features == up_bank.in_features
9320                && gate_bank.local_out == up_bank.local_out
9321                && gate_bank.row_bytes == up_bank.row_bytes
9322                && gate_bank.expert_bytes == up_bank.expert_bytes;
9323            if gu_fused {
9324                let Nvfp4DeviceRoutesWorkspace {
9325                    sel,
9326                    gate_out,
9327                    up_out,
9328                    in_q,
9329                    in_d,
9330                    ..
9331                } = &mut *workspace;
9332                engine.qmatvec_nvfp4_sel_gu_into(
9333                    &gate_bank.bank,
9334                    &up_bank.bank,
9335                    &sel[rank_index],
9336                    &in_q[rank_index],
9337                    &in_d[rank_index],
9338                    &mut gate_out[rank_index],
9339                    &mut up_out[rank_index],
9340                    n_sel,
9341                    gate_bank.in_features,
9342                    gate_bank.local_out,
9343                    gate_bank.row_bytes,
9344                    gate_bank.expert_bytes,
9345                )?;
9346            } else {
9347                engine.qmatvec_nvfp4_sel_into(
9348                    &gate_bank.bank,
9349                    &workspace.sel[rank_index],
9350                    aq,
9351                    ad,
9352                    &mut workspace.gate_out[rank_index],
9353                    n_sel,
9354                    gate_bank.in_features,
9355                    gate_bank.local_out,
9356                    gate_bank.row_bytes,
9357                    gate_bank.expert_bytes,
9358                    0,
9359                    0,
9360                )?;
9361                engine.qmatvec_nvfp4_sel_into(
9362                    &up_bank.bank,
9363                    &workspace.sel[rank_index],
9364                    aq,
9365                    ad,
9366                    &mut workspace.up_out[rank_index],
9367                    n_sel,
9368                    up_bank.in_features,
9369                    up_bank.local_out,
9370                    up_bank.row_bytes,
9371                    up_bank.expert_bytes,
9372                    0,
9373                    0,
9374                )?;
9375            }
9376            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
9377            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
9378            // input-column window (the geometry gift; see the method doc).
9379            {
9380                let Nvfp4DeviceRoutesWorkspace {
9381                    gate_out,
9382                    up_out,
9383                    sel,
9384                    act_q,
9385                    act_d,
9386                    ..
9387                } = &mut *workspace;
9388                engine.silu_mul_scaled_q8_1_sel_into(
9389                    &gate_out[rank_index],
9390                    &up_out[rank_index],
9391                    &experts.macros_gate_dev[rank_index],
9392                    &experts.macros_up_dev[rank_index],
9393                    &sel[rank_index],
9394                    activation_limit,
9395                    &mut act_q[rank_index],
9396                    &mut act_d[rank_index],
9397                    local_out,
9398                    n_sel,
9399                )?;
9400            }
9401            let shard = &experts.down[rank_index];
9402            if shard.device_rank != rank_index || shard.local_in != local_out {
9403                return Err(
9404                    "NVFP4 device routes: down canonical shard placement drifted from \
9405                     the gate/up column split"
9406                        .into(),
9407                );
9408            }
9409            {
9410                let Nvfp4DeviceRoutesWorkspace {
9411                    sel,
9412                    act_q,
9413                    act_d,
9414                    partial,
9415                    ..
9416                } = &mut *workspace;
9417                engine.qmatvec_nvfp4_sel_into(
9418                    &shard.bank,
9419                    &sel[rank_index],
9420                    &act_q[rank_index],
9421                    &act_d[rank_index],
9422                    &mut partial[rank_index],
9423                    n_sel,
9424                    shard.local_in,
9425                    shard.out_features,
9426                    shard.row_bytes,
9427                    shard.expert_bytes,
9428                    local_out,
9429                    local_out / 32,
9430                )?;
9431            }
9432            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
9433            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
9434            // fold the down macro in-kernel from the device selection.
9435            {
9436                let Nvfp4DeviceRoutesWorkspace {
9437                    partial,
9438                    combine_w,
9439                    route_w,
9440                    sel,
9441                    accumulator,
9442                    ..
9443                } = &mut *workspace;
9444                if device_routed {
9445                    engine.axpy_rows_seq_md_into(
9446                        &partial[rank_index],
9447                        &route_w[rank_index],
9448                        &experts.macros_down_dev[rank_index],
9449                        &sel[rank_index],
9450                        &mut accumulator[rank_index],
9451                        experts.input_width,
9452                        n_sel,
9453                    )?;
9454                } else {
9455                    engine.axpy_rows_seq_into(
9456                        &partial[rank_index],
9457                        &combine_w[rank_index],
9458                        &mut accumulator[rank_index],
9459                        experts.input_width,
9460                        n_sel,
9461                    )?;
9462                }
9463            }
9464        }
9465        Ok(())
9466    }
9467
9468    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
9469    /// a device row on the model engine `e` and the combined output returns as a fresh
9470    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
9471    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
9472    /// the input's producer; each rank waits it before its peer read; the root reduce waits
9473    /// every rank's done event; `e` waits the root's done event before copying out. The
9474    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
9475    pub fn run_tensor_parallel_routes_nvfp4_device_io(
9476        &self,
9477        experts: &ResidentNvfp4TensorParallel,
9478        e: &Engine,
9479        input_dev: &crate::CudaSlice<f32>,
9480        selected: &[usize],
9481        route_weights: &[f32],
9482        experts_per_token: usize,
9483        activation_limit: Option<f32>,
9484    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
9485        if input_dev.len() != experts.input_width {
9486            return Err(format!(
9487                "NVFP4 device-io routes input {} != width {}",
9488                input_dev.len(),
9489                experts.input_width
9490            )
9491            .into());
9492        }
9493        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9494            return Err(format!(
9495                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
9496                selected.len(),
9497                route_weights.len(),
9498            )
9499            .into());
9500        }
9501        if !route_weights.iter().all(|weight| weight.is_finite()) {
9502            return Err("NVFP4 device route weights contain a non-finite value".into());
9503        }
9504        let world = self.ranks.len();
9505        if world != NVFP4_CANONICAL_ROW_SHARDS {
9506            return Err(format!(
9507                "NVFP4 device routes require world == canonical shard grid \
9508                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
9509            )
9510            .into());
9511        }
9512        let local_out = experts.expert_width / world;
9513        let n_sel = experts_per_token;
9514
9515        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9516        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9517        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9518        let started = timing.then(std::time::Instant::now);
9519
9520        let mut workspace_guard = experts
9521            .device_workspace
9522            .lock()
9523            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9524        if workspace_guard.is_none() {
9525            drop(workspace_guard);
9526            // Build through the host-IO ensure path exactly once: run it with a zero input.
9527            // Cheaper than duplicating the init; the first real call overwrites everything.
9528            let zero = vec![0.0f32; experts.input_width];
9529            let zero_sel = vec![0usize; n_sel];
9530            let zero_w = vec![0.0f32; n_sel];
9531            let _ = self.run_tensor_parallel_routes_nvfp4_device(
9532                experts,
9533                &zero,
9534                &zero_sel,
9535                &zero_w,
9536                n_sel,
9537                activation_limit,
9538            )?;
9539            workspace_guard = experts
9540                .device_workspace
9541                .lock()
9542                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9543        }
9544        let workspace = workspace_guard
9545            .as_mut()
9546            .expect("NVFP4 device routes workspace initialized above");
9547        if workspace.n_sel != n_sel {
9548            return Err(format!(
9549                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
9550                workspace.n_sel
9551            )
9552            .into());
9553        }
9554        for &expert in selected {
9555            if expert >= experts.expert_count {
9556                return Err(format!(
9557                    "NVFP4 device selected expert {expert} outside 0..{}",
9558                    experts.expert_count
9559                )
9560                .into());
9561            }
9562        }
9563        let sel_i32 = selected
9564            .iter()
9565            .map(|&expert| expert as i32)
9566            .collect::<Vec<_>>();
9567
9568        // Entry fence: e's stream position covers the input's producer AND every consumer of
9569        // the previous layer's output (queued on e's stream before this call), guarding the
9570        // workspace reuse exactly like the v2 attention driver.
9571        if let Some((_, device)) = workspace.ev_entry.as_ref() {
9572            if *device != e.ctx().ordinal() {
9573                return Err("NVFP4 device-io routes engine changed".into());
9574            }
9575        } else {
9576            let _main = e.gpu.enter_main()?;
9577            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
9578        }
9579        {
9580            let _main = e.gpu.enter_main()?;
9581            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
9582            ev_entry.record(&e.stream())?;
9583        }
9584        for (rank_index, engine) in self.ranks.iter().enumerate() {
9585            let _main = engine.gpu.enter_main()?;
9586            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
9587            engine.stream().wait(ev_entry)?;
9588            {
9589                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
9590                engine
9591                    .stream()
9592                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
9593            }
9594            {
9595                let Nvfp4DeviceRoutesWorkspace {
9596                    input, in_q, in_d, ..
9597                } = &mut *workspace;
9598                engine.quantize_q8_1_into(
9599                    &input[rank_index],
9600                    1,
9601                    experts.input_width,
9602                    &mut in_q[rank_index],
9603                    &mut in_d[rank_index],
9604                )?;
9605            }
9606        }
9607        self.nvfp4_routes_batched_sweeps(
9608            experts,
9609            workspace,
9610            selected,
9611            route_weights,
9612            &sel_i32,
9613            local_out,
9614            n_sel,
9615            activation_limit,
9616            false,
9617        )?;
9618
9619        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
9620        // the root stream in canonical shard order, and e copies the combined row out behind
9621        // the root's done event.
9622        // rank0 == root: its own stream order already covers its sweep; only the PEER
9623        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
9624        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
9625            let _main = engine.gpu.enter_main()?;
9626            workspace.ev_rank[rank_index].record(&engine.stream())?;
9627        }
9628        if moe_direct_on() && self.ranks.len() == 2 {
9629            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
9630            // rank0's is root-stream-ordered. One root event + rank1's own event order
9631            // the model engine's single add — same operand order as root's add
9632            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
9633            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
9634            // hazard class does not apply).
9635            {
9636                let root = &self.ranks[0];
9637                let _main = root.gpu.enter_main()?;
9638                workspace
9639                    .ev_done
9640                    .as_ref()
9641                    .expect("device routes done event")
9642                    .record(&root.stream())?;
9643            }
9644            let _main = e.gpu.enter_main()?;
9645            e.stream().wait(
9646                workspace
9647                    .ev_done
9648                    .as_ref()
9649                    .expect("device routes done event"),
9650            )?;
9651            for ev in workspace.ev_rank.iter().skip(1) {
9652                e.stream().wait(ev)?;
9653            }
9654            let mut output = e.uninit(experts.input_width)?;
9655            e.add(
9656                &workspace.accumulator[0],
9657                &workspace.accumulator[1],
9658                &mut output,
9659                experts.input_width,
9660            )?;
9661            let output = output;
9662            if let Some(started) = started {
9663                use std::sync::atomic::Ordering;
9664                let ns = TIMING_NS
9665                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9666                    + started.elapsed().as_nanos() as u64;
9667                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9668                if calls % 430 == 0 {
9669                    eprintln!(
9670                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9671                        ns as f64 / 1.0e6,
9672                        ns as f64 / calls as f64 / 1.0e3,
9673                    );
9674                }
9675            }
9676            return Ok(output);
9677        }
9678        {
9679            let root = &self.ranks[0];
9680            let _main = root.gpu.enter_main()?;
9681            for ev in workspace.ev_rank.iter().skip(1) {
9682                root.stream().wait(ev)?;
9683            }
9684            root.stream()
9685                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
9686            {
9687                let Nvfp4DeviceRoutesWorkspace {
9688                    accumulator,
9689                    remote,
9690                    combined,
9691                    ..
9692                } = &mut *workspace;
9693                root.add(&accumulator[0], remote, combined, experts.input_width)?;
9694            }
9695            workspace
9696                .ev_done
9697                .as_ref()
9698                .expect("device routes done event")
9699                .record(&root.stream())?;
9700        }
9701        let output = {
9702            let _main = e.gpu.enter_main()?;
9703            e.stream().wait(
9704                workspace
9705                    .ev_done
9706                    .as_ref()
9707                    .expect("device routes done event"),
9708            )?;
9709            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
9710            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
9711            let mut output = e.uninit(experts.input_width)?;
9712            e.stream().memcpy_dtod(
9713                &workspace.combined.slice(0..experts.input_width),
9714                &mut output.slice_mut(0..experts.input_width),
9715            )?;
9716            output
9717        };
9718        if let Some(started) = started {
9719            use std::sync::atomic::Ordering;
9720            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9721                + started.elapsed().as_nanos() as u64;
9722            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9723            if calls % 430 == 0 {
9724                eprintln!(
9725                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9726                    ns as f64 / 1.0e6,
9727                    ns as f64 / calls as f64 / 1.0e3,
9728                );
9729            }
9730        }
9731        Ok(output)
9732    }
9733
9734    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
9735    /// route weights arrive as the device router's e-context outputs — the per-layer host
9736    /// logits readback disappears. The fresh router outputs are staged into persistent
9737    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
9738    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
9739    #[allow(clippy::too_many_arguments)]
9740    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
9741    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
9742    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
9743    /// the routed run then does its own staging as before.
9744    pub fn nvfp4_routes_prestage(
9745        &self,
9746        experts: &ResidentNvfp4TensorParallel,
9747        e: &Engine,
9748        input_dev: &crate::CudaSlice<f32>,
9749    ) -> Result<bool, Box<dyn std::error::Error>> {
9750        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
9751    }
9752
9753    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
9754    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
9755    /// deterministic kernels on identical input bits produce identical sel/w, so the
9756    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
9757    /// routed run then skips rank1's sel pull.
9758    pub fn nvfp4_routes_prestage_with(
9759        &self,
9760        experts: &ResidentNvfp4TensorParallel,
9761        e: &Engine,
9762        input_dev: &crate::CudaSlice<f32>,
9763        rank1_router: impl FnOnce(
9764            &Engine,
9765            &crate::CudaSlice<f32>,
9766            &mut crate::CudaSlice<i32>,
9767            &mut crate::CudaSlice<f32>,
9768        ) -> Result<bool, Box<dyn std::error::Error>>,
9769    ) -> Result<bool, Box<dyn std::error::Error>> {
9770        if !routes_prestage_on() || step_tp_graph_enabled()? {
9771            return Ok(false);
9772        }
9773        if input_dev.len() != experts.input_width {
9774            return Err("NVFP4 prestage input width mismatch".into());
9775        }
9776        let mut workspace_guard = experts
9777            .device_workspace
9778            .lock()
9779            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9780        let Some(workspace) = workspace_guard.as_mut() else {
9781            return Ok(false);
9782        };
9783        if workspace.ev_input.is_none() {
9784            let _main = e.gpu.enter_main()?;
9785            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
9786        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
9787            return Err("NVFP4 prestage engine changed".into());
9788        }
9789        {
9790            let _main = e.gpu.enter_main()?;
9791            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
9792            ev.record(&e.stream())?;
9793        }
9794        for (rank_index, engine) in self.ranks.iter().enumerate() {
9795            let _main = engine.gpu.enter_main()?;
9796            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
9797            engine.stream().wait(ev)?;
9798            {
9799                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
9800                engine
9801                    .stream()
9802                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
9803            }
9804            {
9805                let Nvfp4DeviceRoutesWorkspace {
9806                    input, in_q, in_d, ..
9807                } = &mut *workspace;
9808                engine.quantize_q8_1_into(
9809                    &input[rank_index],
9810                    1,
9811                    experts.input_width,
9812                    &mut in_q[rank_index],
9813                    &mut in_d[rank_index],
9814                )?;
9815            }
9816        }
9817        if self.ranks.len() == 2 {
9818            let rank1 = &self.ranks[1];
9819            let _r1 = rank1.gpu.enter_main()?;
9820            let Nvfp4DeviceRoutesWorkspace {
9821                input,
9822                sel,
9823                route_w,
9824                ..
9825            } = &mut *workspace;
9826            let (in1, rest_sel) = (&input[1], &mut sel[1]);
9827            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
9828                workspace.rank1_routed = true;
9829            }
9830        }
9831        workspace.prestaged = true;
9832        Ok(true)
9833    }
9834
9835    /// TWO-COLUMN device-routed expert program (spec verify, MEMRA_TCOL_FFN): one gu_tcol
9836    /// sweep over 2*n_sel_col pairs (pair t reads activation row t/n_sel_col — weights the
9837    /// two columns share dedup through L2), the UNCHANGED silu/down kernels at n_sel=16
9838    /// (both already index per pair), and one offset-axpy combine per column (the exact
9839    /// t=1 sequential chain over that column's 8 pairs). No serving doors: no graph, no
9840    /// prestage, no shexp folding — plain evented ordering. Returns [2, input_width] on e.
9841    ///
9842    /// EXACTNESS: every kernel body is the t=1 program per (pair,row) or per element; the
9843    /// per-column combine order equals the t=1 combine; the cross-rank join adds the same
9844    /// operand values elementwise. Gated by the greedy tape like every verify arm.
9845    #[allow(clippy::too_many_arguments)]
9846    pub fn run_tensor_parallel_routes_nvfp4_device_routed_t2(
9847        &self,
9848        experts: &ResidentNvfp4TensorParallel,
9849        e: &Engine,
9850        z2: &crate::CudaSlice<f32>,
9851        sel_d: &crate::CudaSlice<i32>,
9852        w_d: &crate::CudaSlice<f32>,
9853        n_sel_col: usize,
9854        activation_limit: Option<f32>,
9855    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
9856        let world = self.ranks.len();
9857        if world != NVFP4_CANONICAL_ROW_SHARDS {
9858            return Err("NVFP4 t2 routes require the canonical 2-shard grid".into());
9859        }
9860        let width = experts.input_width;
9861        let n_sel = 2 * n_sel_col;
9862        if z2.len() < 2 * width || sel_d.len() < n_sel || w_d.len() < n_sel {
9863            return Err("NVFP4 t2 routes geometry".into());
9864        }
9865        if !nvfp4_bank_v2_on() {
9866            return Err("NVFP4 t2 routes require the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
9867        }
9868        let local_out = experts.expert_width / world;
9869        let mut guard = experts
9870            .t2_workspace
9871            .lock()
9872            .map_err(|_| "NVFP4 t2 workspace lock is poisoned")?;
9873        if guard.as_ref().is_none_or(|ws| ws.n_sel != n_sel) {
9874            let mut input2 = Vec::new();
9875            let mut in_q2 = Vec::new();
9876            let mut in_d2 = Vec::new();
9877            let mut sel2 = Vec::new();
9878            let mut route_w2 = Vec::new();
9879            let mut gate_out2 = Vec::new();
9880            let mut up_out2 = Vec::new();
9881            let mut act_q2 = Vec::new();
9882            let mut act_d2 = Vec::new();
9883            let mut partial2 = Vec::new();
9884            let mut acc_a = Vec::new();
9885            let mut acc_b = Vec::new();
9886            let mut ev_rank = Vec::new();
9887            for engine in &self.ranks {
9888                let _m = engine.gpu.enter_main()?;
9889                input2.push(engine.uninit(2 * width)?);
9890                in_q2.push(engine.alloc_i8_uninit(2 * width)?);
9891                in_d2.push(engine.uninit(2 * (width / 32))?);
9892                sel2.push(engine.htod_i32(&vec![0i32; n_sel])?);
9893                route_w2.push(engine.uninit(n_sel)?);
9894                gate_out2.push(engine.uninit(n_sel * local_out)?);
9895                up_out2.push(engine.uninit(n_sel * local_out)?);
9896                act_q2.push(engine.alloc_i8_uninit(n_sel * local_out)?);
9897                act_d2.push(engine.uninit(n_sel * (local_out / 32))?);
9898                partial2.push(engine.uninit(n_sel * width)?);
9899                acc_a.push(engine.uninit(width)?);
9900                acc_b.push(engine.uninit(width)?);
9901                ev_rank.push(engine.ctx().new_event(None)?);
9902            }
9903            let root = &self.ranks[0];
9904            let (peer_a, peer_b, omix_a, omix_b, ev_root) = {
9905                let _m = root.gpu.enter_main()?;
9906                (
9907                    root.uninit(width)?,
9908                    root.uninit(width)?,
9909                    root.uninit(width)?,
9910                    root.uninit(width)?,
9911                    root.ctx().new_event(None)?,
9912                )
9913            };
9914            let ev_entry = {
9915                let _m = e.gpu.enter_main()?;
9916                e.ctx().new_event(None)?
9917            };
9918            *guard = Some(Nvfp4T2Workspace {
9919                input2,
9920                in_q2,
9921                in_d2,
9922                sel2,
9923                route_w2,
9924                gate_out2,
9925                up_out2,
9926                act_q2,
9927                act_d2,
9928                partial2,
9929                acc_a,
9930                acc_b,
9931                peer_a,
9932                peer_b,
9933                omix_a,
9934                omix_b,
9935                ev_entry,
9936                ev_rank,
9937                ev_root,
9938                n_sel,
9939                e_device: e.ctx().ordinal(),
9940            });
9941        }
9942        let ws = guard.as_mut().expect("armed above");
9943        if ws.e_device != e.ctx().ordinal() {
9944            return Err("NVFP4 t2 routes engine changed".into());
9945        }
9946        {
9947            let _main = e.gpu.enter_main()?;
9948            ws.ev_entry.record(&e.stream())?;
9949        }
9950        for rank in 0..world {
9951            let engine = &self.ranks[rank];
9952            let _main = engine.gpu.enter_main()?;
9953            engine.stream().wait(&ws.ev_entry)?;
9954            {
9955                let mut dst = ws.input2[rank].slice_mut(0..2 * width);
9956                engine
9957                    .stream()
9958                    .memcpy_dtod(&z2.slice(0..2 * width), &mut dst)?;
9959            }
9960            {
9961                let mut dst = ws.sel2[rank].slice_mut(0..n_sel);
9962                engine
9963                    .stream()
9964                    .memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
9965            }
9966            {
9967                let mut dst = ws.route_w2[rank].slice_mut(0..n_sel);
9968                engine
9969                    .stream()
9970                    .memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
9971            }
9972            {
9973                let Nvfp4T2Workspace {
9974                    input2,
9975                    in_q2,
9976                    in_d2,
9977                    ..
9978                } = &mut *ws;
9979                engine.quantize_q8_1_into(
9980                    &input2[rank],
9981                    2,
9982                    width,
9983                    &mut in_q2[rank],
9984                    &mut in_d2[rank],
9985                )?;
9986            }
9987            let gate_bank = &experts.gate[rank];
9988            let up_bank = &experts.up[rank];
9989            if gate_bank.in_features != up_bank.in_features
9990                || gate_bank.local_out != up_bank.local_out
9991                || gate_bank.row_bytes != up_bank.row_bytes
9992                || gate_bank.expert_bytes != up_bank.expert_bytes
9993            {
9994                return Err("NVFP4 t2 routes need matched gate/up bank geometry".into());
9995            }
9996            {
9997                let Nvfp4T2Workspace {
9998                    sel2,
9999                    in_q2,
10000                    in_d2,
10001                    gate_out2,
10002                    up_out2,
10003                    ..
10004                } = &mut *ws;
10005                engine.qmatvec_nvfp4_sel_gu_tcol_into(
10006                    &gate_bank.bank,
10007                    &up_bank.bank,
10008                    &sel2[rank],
10009                    &in_q2[rank],
10010                    &in_d2[rank],
10011                    &mut gate_out2[rank],
10012                    &mut up_out2[rank],
10013                    n_sel,
10014                    n_sel_col,
10015                    gate_bank.in_features,
10016                    gate_bank.local_out,
10017                    gate_bank.row_bytes,
10018                    gate_bank.expert_bytes,
10019                    width,
10020                    width / 32,
10021                )?;
10022            }
10023            {
10024                let Nvfp4T2Workspace {
10025                    gate_out2,
10026                    up_out2,
10027                    sel2,
10028                    act_q2,
10029                    act_d2,
10030                    ..
10031                } = &mut *ws;
10032                engine.silu_mul_scaled_q8_1_sel_into(
10033                    &gate_out2[rank],
10034                    &up_out2[rank],
10035                    &experts.macros_gate_dev[rank],
10036                    &experts.macros_up_dev[rank],
10037                    &sel2[rank],
10038                    activation_limit,
10039                    &mut act_q2[rank],
10040                    &mut act_d2[rank],
10041                    local_out,
10042                    n_sel,
10043                )?;
10044            }
10045            let shard = &experts.down[rank];
10046            if shard.device_rank != rank || shard.local_in != local_out {
10047                return Err("NVFP4 t2 routes: down shard placement drifted".into());
10048            }
10049            {
10050                let Nvfp4T2Workspace {
10051                    sel2,
10052                    act_q2,
10053                    act_d2,
10054                    partial2,
10055                    ..
10056                } = &mut *ws;
10057                engine.qmatvec_nvfp4_sel_into(
10058                    &shard.bank,
10059                    &sel2[rank],
10060                    &act_q2[rank],
10061                    &act_d2[rank],
10062                    &mut partial2[rank],
10063                    n_sel,
10064                    shard.local_in,
10065                    shard.out_features,
10066                    shard.row_bytes,
10067                    shard.expert_bytes,
10068                    local_out,
10069                    local_out / 32,
10070                )?;
10071            }
10072            {
10073                let Nvfp4T2Workspace {
10074                    partial2,
10075                    route_w2,
10076                    sel2,
10077                    acc_a,
10078                    acc_b,
10079                    ..
10080                } = &mut *ws;
10081                engine.axpy_rows_seq_md_off_into(
10082                    &partial2[rank],
10083                    &route_w2[rank],
10084                    &experts.macros_down_dev[rank],
10085                    &sel2[rank],
10086                    &mut acc_a[rank],
10087                    width,
10088                    n_sel_col,
10089                    0,
10090                )?;
10091                engine.axpy_rows_seq_md_off_into(
10092                    &partial2[rank],
10093                    &route_w2[rank],
10094                    &experts.macros_down_dev[rank],
10095                    &sel2[rank],
10096                    &mut acc_b[rank],
10097                    width,
10098                    n_sel_col,
10099                    n_sel_col,
10100                )?;
10101            }
10102            if rank != 0 {
10103                ws.ev_rank[rank].record(&engine.stream())?;
10104            }
10105        }
10106        let root = &self.ranks[0];
10107        {
10108            let _main = root.gpu.enter_main()?;
10109            for ev in ws.ev_rank.iter().skip(1) {
10110                root.stream().wait(ev)?;
10111            }
10112            {
10113                let Nvfp4T2Workspace {
10114                    acc_a,
10115                    acc_b,
10116                    peer_a,
10117                    peer_b,
10118                    omix_a,
10119                    omix_b,
10120                    ..
10121                } = &mut *ws;
10122                {
10123                    let mut dst = peer_a.slice_mut(0..width);
10124                    root.stream()
10125                        .memcpy_dtod(&acc_a[1].slice(0..width), &mut dst)?;
10126                }
10127                {
10128                    let mut dst = peer_b.slice_mut(0..width);
10129                    root.stream()
10130                        .memcpy_dtod(&acc_b[1].slice(0..width), &mut dst)?;
10131                }
10132                root.add(&acc_a[0], peer_a, omix_a, width)?;
10133                root.add(&acc_b[0], peer_b, omix_b, width)?;
10134            }
10135            ws.ev_root.record(&root.stream())?;
10136        }
10137        let _main = e.gpu.enter_main()?;
10138        e.stream().wait(&ws.ev_root)?;
10139        let mut out = e.uninit(2 * width)?;
10140        e.stream()
10141            .memcpy_dtod(&ws.omix_a.slice(0..width), &mut out.slice_mut(0..width))?;
10142        e.stream().memcpy_dtod(
10143            &ws.omix_b.slice(0..width),
10144            &mut out.slice_mut(width..2 * width),
10145        )?;
10146        Ok(out)
10147    }
10148
10149    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
10150        &self,
10151        experts: &ResidentNvfp4TensorParallel,
10152        e: &Engine,
10153        input_dev: &crate::CudaSlice<f32>,
10154        sel_d: &crate::CudaSlice<i32>,
10155        w_d: &crate::CudaSlice<f32>,
10156        experts_per_token: usize,
10157        activation_limit: Option<f32>,
10158    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10159        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
10160            experts,
10161            e,
10162            input_dev,
10163            sel_d,
10164            w_d,
10165            experts_per_token,
10166            activation_limit,
10167            || Ok(()),
10168        )
10169    }
10170
10171    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
10172    /// runs on the host right before the join wait is enqueued on e's stream — work it
10173    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
10174    /// sweep, instead of after the join. Value-neutral by construction (the hook only
10175    /// reorders independent host issue).
10176    #[allow(clippy::too_many_arguments)]
10177    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
10178        &self,
10179        experts: &ResidentNvfp4TensorParallel,
10180        e: &Engine,
10181        input_dev: &crate::CudaSlice<f32>,
10182        sel_d: &crate::CudaSlice<i32>,
10183        w_d: &crate::CudaSlice<f32>,
10184        experts_per_token: usize,
10185        activation_limit: Option<f32>,
10186        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
10187    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10188        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
10189            experts,
10190            e,
10191            input_dev,
10192            sel_d,
10193            w_d,
10194            experts_per_token,
10195            activation_limit,
10196            pre_join,
10197            None,
10198        )
10199    }
10200
10201    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
10202    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
10203    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
10204    /// its apply launch. Raw UVA pointers so no lock is held across the call.
10205    #[allow(clippy::too_many_arguments)]
10206    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
10207        &self,
10208        experts: &ResidentNvfp4TensorParallel,
10209        e: &Engine,
10210        input_dev: &crate::CudaSlice<f32>,
10211        sel_d: &crate::CudaSlice<i32>,
10212        w_d: &crate::CudaSlice<f32>,
10213        experts_per_token: usize,
10214        activation_limit: Option<f32>,
10215        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
10216        post_add: Option<(u64, u64)>,
10217    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10218        if input_dev.len() != experts.input_width {
10219            return Err(format!(
10220                "NVFP4 device-routed input {} != width {}",
10221                input_dev.len(),
10222                experts.input_width
10223            )
10224            .into());
10225        }
10226        let n_sel = experts_per_token;
10227        if sel_d.len() < n_sel || w_d.len() < n_sel {
10228            return Err(format!(
10229                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
10230                sel_d.len(),
10231                w_d.len()
10232            )
10233            .into());
10234        }
10235        let world = self.ranks.len();
10236        if world != NVFP4_CANONICAL_ROW_SHARDS {
10237            return Err(format!(
10238                "NVFP4 device routes require world == canonical shard grid \
10239                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10240            )
10241            .into());
10242        }
10243        let local_out = experts.expert_width / world;
10244
10245        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10246        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10247        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10248        let started = timing.then(std::time::Instant::now);
10249
10250        let mut workspace_guard = experts
10251            .device_workspace
10252            .lock()
10253            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10254        if workspace_guard.is_none() {
10255            drop(workspace_guard);
10256            let zero = vec![0.0f32; experts.input_width];
10257            let zero_sel = vec![0usize; n_sel];
10258            let zero_w = vec![0.0f32; n_sel];
10259            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10260                experts,
10261                &zero,
10262                &zero_sel,
10263                &zero_w,
10264                n_sel,
10265                activation_limit,
10266            )?;
10267            workspace_guard = experts
10268                .device_workspace
10269                .lock()
10270                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10271        }
10272        let workspace = workspace_guard
10273            .as_mut()
10274            .expect("NVFP4 device routes workspace initialized above");
10275        if workspace.n_sel != n_sel {
10276            return Err(format!(
10277                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10278                workspace.n_sel
10279            )
10280            .into());
10281        }
10282
10283        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
10284        // stitched multi-device parent launched on e's stream — no events, no per-token node
10285        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
10286        // the children replay exactly the same kernel/copy sequence.
10287        if step_tp_graph_enabled()? {
10288            if workspace.dev_route_e.is_none() {
10289                let _main = e.gpu.enter_main()?;
10290                workspace.dev_route_e = Some((
10291                    e.htod_i32(&vec![0i32; n_sel])?,
10292                    e.htod(&vec![0.0f32; n_sel])?,
10293                ));
10294            }
10295            if workspace.in_stage_e.is_none() {
10296                let _main = e.gpu.enter_main()?;
10297                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
10298                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
10299            }
10300            if workspace.routes_graph.is_none() {
10301                let graph = self.nvfp4_routes_build_graph(
10302                    experts,
10303                    workspace,
10304                    local_out,
10305                    n_sel,
10306                    activation_limit,
10307                )?;
10308                workspace.routes_graph = Some(graph);
10309                eprintln!(
10310                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
10311                     children=3 updates=none performance_claim=false"
10312                );
10313            }
10314            let output = {
10315                let _main = e.gpu.enter_main()?;
10316                {
10317                    let (sel_e, w_e) = workspace
10318                        .dev_route_e
10319                        .as_mut()
10320                        .expect("device route staging set above");
10321                    {
10322                        let mut dst = sel_e.slice_mut(0..n_sel);
10323                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10324                    }
10325                    {
10326                        let mut dst = w_e.slice_mut(0..n_sel);
10327                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10328                    }
10329                }
10330                {
10331                    let in_stage = workspace
10332                        .in_stage_e
10333                        .as_mut()
10334                        .expect("graph staging set above");
10335                    let mut dst = in_stage.slice_mut(0..experts.input_width);
10336                    e.stream()
10337                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
10338                }
10339                unsafe {
10340                    let r = cudarc::driver::sys::cuGraphLaunch(
10341                        workspace
10342                            .routes_graph
10343                            .as_ref()
10344                            .expect("routes graph built above")
10345                            .exec,
10346                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
10347                    );
10348                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
10349                        return Err(format!("routes graph launch: {r:?}").into());
10350                    }
10351                }
10352                let mut output = e.uninit(experts.input_width)?;
10353                {
10354                    let out_stage = workspace
10355                        .out_stage_e
10356                        .as_ref()
10357                        .expect("graph staging set above");
10358                    e.stream().memcpy_dtod(
10359                        &out_stage.slice(0..experts.input_width),
10360                        &mut output.slice_mut(0..experts.input_width),
10361                    )?;
10362                }
10363                output
10364            };
10365            if let Some(started) = started {
10366                use std::sync::atomic::Ordering;
10367                let ns = TIMING_NS
10368                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10369                    + started.elapsed().as_nanos() as u64;
10370                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10371                if calls % 430 == 0 {
10372                    eprintln!(
10373                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10374                        ns as f64 / 1.0e6,
10375                        ns as f64 / calls as f64 / 1.0e3,
10376                    );
10377                }
10378            }
10379            return Ok(output);
10380        }
10381
10382        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
10383        // copied into the persistent e-context pair, then the event is recorded — the caller's
10384        // sel_d/w_d can free on e's stream with no cross-stream reader.
10385        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10386            if *device != e.ctx().ordinal() {
10387                return Err("NVFP4 device-routed routes engine changed".into());
10388            }
10389        } else {
10390            let _main = e.gpu.enter_main()?;
10391            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10392        }
10393        if workspace.dev_route_e.is_none() {
10394            let _main = e.gpu.enter_main()?;
10395            workspace.dev_route_e = Some((
10396                e.htod_i32(&vec![0i32; n_sel])?,
10397                e.htod(&vec![0.0f32; n_sel])?,
10398            ));
10399        }
10400        {
10401            let _main = e.gpu.enter_main()?;
10402            let (sel_e, w_e) = workspace
10403                .dev_route_e
10404                .as_mut()
10405                .expect("device route staging set above");
10406            {
10407                let mut dst = sel_e.slice_mut(0..n_sel);
10408                e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10409            }
10410            {
10411                let mut dst = w_e.slice_mut(0..n_sel);
10412                e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10413            }
10414            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10415            ev_entry.record(&e.stream())?;
10416        }
10417        // Prestage door: input pull + quantize were already issued on the rank streams
10418        // (before the router) — the rank stream order suffices, skip them here.
10419        let prestaged = std::mem::take(&mut workspace.prestaged);
10420        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
10421        for (rank_index, engine) in self.ranks.iter().enumerate() {
10422            let _main = engine.gpu.enter_main()?;
10423            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10424            engine.stream().wait(ev_entry)?;
10425            if !prestaged {
10426                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10427                engine
10428                    .stream()
10429                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10430            }
10431            if !(rank1_routed && rank_index == 1) {
10432                let (sel_e, w_e) = workspace
10433                    .dev_route_e
10434                    .as_ref()
10435                    .expect("device route staging set above");
10436                {
10437                    let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
10438                    engine
10439                        .stream()
10440                        .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
10441                }
10442                {
10443                    let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
10444                    engine
10445                        .stream()
10446                        .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
10447                }
10448            }
10449            if !prestaged {
10450                let Nvfp4DeviceRoutesWorkspace {
10451                    input, in_q, in_d, ..
10452                } = &mut *workspace;
10453                engine.quantize_q8_1_into(
10454                    &input[rank_index],
10455                    1,
10456                    experts.input_width,
10457                    &mut in_q[rank_index],
10458                    &mut in_d[rank_index],
10459                )?;
10460            }
10461        }
10462        self.nvfp4_routes_batched_sweeps(
10463            experts,
10464            workspace,
10465            &[],
10466            &[],
10467            &[],
10468            local_out,
10469            n_sel,
10470            activation_limit,
10471            true,
10472        )?;
10473
10474        // rank0 == root: its own stream order already covers its sweep; only the PEER
10475        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
10476        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
10477            let _main = engine.gpu.enter_main()?;
10478            workspace.ev_rank[rank_index].record(&engine.stream())?;
10479        }
10480        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
10481        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
10482        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
10483        let mut ticket = 0u32;
10484        if memops {
10485            use cudarc::driver::sys;
10486            if workspace.fence_flags_raw == 0 {
10487                let root = &self.ranks[0];
10488                let _main = root.gpu.enter_main()?;
10489                let mut ptr: sys::CUdeviceptr = 0;
10490                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
10491                if r != sys::CUresult::CUDA_SUCCESS {
10492                    return Err(format!("fence flag alloc: {r:?}").into());
10493                }
10494                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
10495                if r != sys::CUresult::CUDA_SUCCESS {
10496                    return Err(format!("fence flag memset: {r:?}").into());
10497                }
10498                workspace.fence_flags_raw = ptr as u64;
10499            }
10500            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
10501            ticket = workspace.fence_ticket;
10502            let base = workspace.fence_flags_raw;
10503            // rank1's fence stays an EVENT: stream memops on PEER memory are rejected
10504            // over PCIe P2P (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23). Only the
10505            // same-device root->e hop rides the doorbell.
10506            {
10507                let root = &self.ranks[0];
10508                let _main = root.gpu.enter_main()?;
10509                let r = unsafe {
10510                    sys::cuStreamWriteValue32_v2(
10511                        root.stream().cu_stream() as sys::CUstream,
10512                        (base + 4) as sys::CUdeviceptr,
10513                        ticket,
10514                        0,
10515                    )
10516                };
10517                if r != sys::CUresult::CUDA_SUCCESS {
10518                    return Err(format!("fence write root: {r:?}").into());
10519                }
10520            }
10521        }
10522        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
10523        // kernels queued here execute while the peer rank drains its sweep.
10524        pre_join()?;
10525
10526        if moe_direct_on() && self.ranks.len() == 2 {
10527            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
10528            // rank0's is root-stream-ordered. One root event + rank1's own event order
10529            // the model engine's single add — same operand order as root's add
10530            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
10531            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
10532            // hazard class does not apply).
10533            let _main = e.gpu.enter_main()?;
10534            if memops {
10535                use cudarc::driver::sys;
10536                let base = workspace.fence_flags_raw;
10537                let r = unsafe {
10538                    sys::cuStreamWaitValue32_v2(
10539                        e.stream().cu_stream() as sys::CUstream,
10540                        (base + 4) as sys::CUdeviceptr,
10541                        ticket,
10542                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
10543                    )
10544                };
10545                if r != sys::CUresult::CUDA_SUCCESS {
10546                    return Err(format!("fence wait: {r:?}").into());
10547                }
10548                for ev in workspace.ev_rank.iter().skip(1) {
10549                    e.stream().wait(ev)?;
10550                }
10551            } else {
10552                {
10553                    let root = &self.ranks[0];
10554                    let _rmain = root.gpu.enter_main()?;
10555                    workspace
10556                        .ev_done
10557                        .as_ref()
10558                        .expect("device routes done event")
10559                        .record(&root.stream())?;
10560                }
10561                e.stream().wait(
10562                    workspace
10563                        .ev_done
10564                        .as_ref()
10565                        .expect("device routes done event"),
10566                )?;
10567                for ev in workspace.ev_rank.iter().skip(1) {
10568                    e.stream().wait(ev)?;
10569                }
10570            }
10571            let mut output = e.uninit(experts.input_width)?;
10572            if let Some((sh_raw, scale_raw)) = post_add {
10573                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
10574                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
10575                e.add3_raw(
10576                    &workspace.accumulator[0],
10577                    &workspace.accumulator[1],
10578                    sh_raw,
10579                    scale_raw,
10580                    &mut output,
10581                    experts.input_width,
10582                )?;
10583            } else {
10584                e.add(
10585                    &workspace.accumulator[0],
10586                    &workspace.accumulator[1],
10587                    &mut output,
10588                    experts.input_width,
10589                )?;
10590            }
10591            let output = output;
10592            if let Some(started) = started {
10593                use std::sync::atomic::Ordering;
10594                let ns = TIMING_NS
10595                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10596                    + started.elapsed().as_nanos() as u64;
10597                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10598                if calls % 430 == 0 {
10599                    eprintln!(
10600                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10601                        ns as f64 / 1.0e6,
10602                        ns as f64 / calls as f64 / 1.0e3,
10603                    );
10604                }
10605            }
10606            return Ok(output);
10607        }
10608        {
10609            let root = &self.ranks[0];
10610            let _main = root.gpu.enter_main()?;
10611            for ev in workspace.ev_rank.iter().skip(1) {
10612                root.stream().wait(ev)?;
10613            }
10614            root.stream()
10615                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10616            {
10617                let Nvfp4DeviceRoutesWorkspace {
10618                    accumulator,
10619                    remote,
10620                    combined,
10621                    ..
10622                } = &mut *workspace;
10623                root.add(&accumulator[0], remote, combined, experts.input_width)?;
10624            }
10625            workspace
10626                .ev_done
10627                .as_ref()
10628                .expect("device routes done event")
10629                .record(&root.stream())?;
10630        }
10631        let output = {
10632            let _main = e.gpu.enter_main()?;
10633            e.stream().wait(
10634                workspace
10635                    .ev_done
10636                    .as_ref()
10637                    .expect("device routes done event"),
10638            )?;
10639            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
10640            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
10641            let mut output = e.uninit(experts.input_width)?;
10642            e.stream().memcpy_dtod(
10643                &workspace.combined.slice(0..experts.input_width),
10644                &mut output.slice_mut(0..experts.input_width),
10645            )?;
10646            output
10647        };
10648        if let Some(started) = started {
10649            use std::sync::atomic::Ordering;
10650            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10651                + started.elapsed().as_nanos() as u64;
10652            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10653            if calls % 430 == 0 {
10654                eprintln!(
10655                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10656                    ns as f64 / 1.0e6,
10657                    ns as f64 / calls as f64 / 1.0e3,
10658                );
10659            }
10660        }
10661        Ok(output)
10662    }
10663
10664    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
10665    /// caller wraps it with rank-event waits + the done record; the token graph captures it
10666    /// verbatim (parent edges provide the ordering).
10667    pub(crate) fn decode_v2_finish_root_fused(
10668        &self,
10669        ws: &mut StepTpDecodeV2Ws,
10670    ) -> Result<(), Box<dyn std::error::Error>> {
10671        let root = &self.ranks[0];
10672        let _main = root.gpu.enter_main()?;
10673        if ws.raw_peer_partial != 0 {
10674            // Capture-safe raw seams (arming happened in the stage flow).
10675            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
10676        } else {
10677            root.stream()
10678                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
10679        }
10680        {
10681            let StepTpDecodeV2Ws {
10682                o_partials,
10683                peer_partial,
10684                reduce_a,
10685                o_out,
10686                ..
10687            } = &mut *ws;
10688            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
10689        }
10690        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
10691        if shadows {
10692            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
10693            // raw when armed.
10694            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
10695            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
10696            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
10697            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
10698        }
10699        if shadows && ws.raw_peer_partial != 0 {
10700            raw_copy_bytes(
10701                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
10702                ws.raw_k1,
10703                ws.local_kv_dim * 4,
10704                root,
10705            )?;
10706            raw_copy_bytes(
10707                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
10708                ws.raw_v1,
10709                ws.local_kv_dim * 4,
10710                root,
10711            )?;
10712        } else if shadows {
10713            let start = ws.local_kv_dim;
10714            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
10715            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
10716            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
10717            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
10718        }
10719        if ws.raw_mixed_stage_e != 0 {
10720            // Token-graph mirrors: the e-glue children read same-context copies of the
10721            // root-produced rows.
10722            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
10723            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
10724            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
10725            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
10726        }
10727        Ok(())
10728    }
10729
10730    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
10731    /// reduce_a's own pointer.
10732    pub(crate) fn decode_v2_arm_token_mirrors(
10733        &self,
10734        ws: &mut StepTpDecodeV2Ws,
10735        mixed_stage_e: u64,
10736        shadow_stage_e: (u64, u64),
10737    ) -> Result<(), Box<dyn std::error::Error>> {
10738        use cudarc::driver::DevicePtr;
10739        let root = &self.ranks[0];
10740        let _main = root.gpu.enter_main()?;
10741        let stream = root.stream();
10742        let (a, _g) = ws.reduce_a.device_ptr(&stream);
10743        ws.raw_reduce_a = a as u64;
10744        ws.raw_mixed_stage_e = mixed_stage_e;
10745        ws.raw_shadow_stage_e = shadow_stage_e;
10746        Ok(())
10747    }
10748
10749    /// Build one layer's stitched routes graph: per-rank children captured on their own
10750    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
10751    /// capture-illegal there), a root combine child, and a multi-device parent with
10752    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
10753    /// nodes touch is persistent workspace/staging.
10754    fn nvfp4_routes_build_graph(
10755        &self,
10756        experts: &ResidentNvfp4TensorParallel,
10757        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10758        local_out: usize,
10759        n_sel: usize,
10760        activation_limit: Option<f32>,
10761    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
10762        use cudarc::driver::DevicePtr;
10763        use cudarc::driver::sys;
10764        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
10765            if r == sys::CUresult::CUDA_SUCCESS {
10766                Ok(())
10767            } else {
10768                Err(format!("{what}: {r:?}").into())
10769            }
10770        }
10771        let world = self.ranks.len();
10772        if world != 2 {
10773            return Err("routes graph door is built for the TP2 pair".into());
10774        }
10775        let width = experts.input_width;
10776
10777        // Raw pointers cached before capture (each read with its owner's stream).
10778        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
10779            let stream = engine.stream();
10780            let (ptr, _g) = buf.device_ptr(&stream);
10781            ptr as u64
10782        };
10783        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
10784            let stream = engine.stream();
10785            let (ptr, _g) = buf.device_ptr(&stream);
10786            ptr as u64
10787        };
10788        let (sel_e, w_e) = workspace
10789            .dev_route_e
10790            .as_ref()
10791            .expect("device route staging set before graph build");
10792        let root_engine = &self.ranks[0];
10793        let p_in_stage = ptr_f32(
10794            workspace.in_stage_e.as_ref().expect("graph staging"),
10795            root_engine,
10796        );
10797        let p_out_stage = ptr_f32(
10798            workspace.out_stage_e.as_ref().expect("graph staging"),
10799            root_engine,
10800        );
10801        let p_sel_e = ptr_i32(sel_e, root_engine);
10802        let p_w_e = ptr_f32(w_e, root_engine);
10803        let p_input: Vec<u64> = (0..world)
10804            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
10805            .collect();
10806        let p_sel: Vec<u64> = (0..world)
10807            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
10808            .collect();
10809        let p_route_w: Vec<u64> = (0..world)
10810            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
10811            .collect();
10812        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
10813        let p_remote = ptr_f32(&workspace.remote, root_engine);
10814        let p_combined = ptr_f32(&workspace.combined, root_engine);
10815
10816        let raw_copy = |dst: u64,
10817                        src: u64,
10818                        bytes: usize,
10819                        engine: &Engine|
10820         -> Result<(), Box<dyn std::error::Error>> {
10821            unsafe {
10822                cu_try(
10823                    sys::cuMemcpyAsync(
10824                        dst as sys::CUdeviceptr,
10825                        src as sys::CUdeviceptr,
10826                        bytes,
10827                        engine.stream().cu_stream() as sys::CUstream,
10828                    ),
10829                    "routes graph cuMemcpyAsync",
10830                )
10831            }
10832        };
10833
10834        let mut children = Vec::with_capacity(3);
10835        for rank in 0..world {
10836            let engine = &self.ranks[rank];
10837            let _main = engine.gpu.enter_main()?;
10838            let (child, _retained) = engine.capture_graph_retained(|_| {
10839                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
10840                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
10841                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
10842                {
10843                    let Nvfp4DeviceRoutesWorkspace {
10844                        input, in_q, in_d, ..
10845                    } = &mut *workspace;
10846                    engine.quantize_q8_1_into(
10847                        &input[rank],
10848                        1,
10849                        width,
10850                        &mut in_q[rank],
10851                        &mut in_d[rank],
10852                    )?;
10853                }
10854                self.nvfp4_routes_batched_sweeps_rank(
10855                    experts,
10856                    workspace,
10857                    &[],
10858                    &[],
10859                    &[],
10860                    local_out,
10861                    n_sel,
10862                    activation_limit,
10863                    true,
10864                    rank,
10865                )?;
10866                Ok(())
10867            })?;
10868            children.push(child);
10869        }
10870        {
10871            let root = &self.ranks[0];
10872            let _main = root.gpu.enter_main()?;
10873            let (child, _retained) = root.capture_graph_retained(|_| {
10874                raw_copy(p_remote, p_acc1, width * 4, root)?;
10875                {
10876                    let Nvfp4DeviceRoutesWorkspace {
10877                        accumulator,
10878                        remote,
10879                        combined,
10880                        ..
10881                    } = &mut *workspace;
10882                    root.add(&accumulator[0], remote, combined, width)?;
10883                }
10884                raw_copy(p_out_stage, p_combined, width * 4, root)?;
10885                Ok(())
10886            })?;
10887            children.push(child);
10888        }
10889
10890        let mut parent: sys::CUgraph = std::ptr::null_mut();
10891        unsafe {
10892            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
10893        }
10894        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
10895        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
10896        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
10897        unsafe {
10898            cu_try(
10899                sys::cuGraphAddChildGraphNode(
10900                    &mut n0,
10901                    parent,
10902                    std::ptr::null(),
10903                    0,
10904                    children[0].cu_graph(),
10905                ),
10906                "routes child r0",
10907            )?;
10908            cu_try(
10909                sys::cuGraphAddChildGraphNode(
10910                    &mut n1,
10911                    parent,
10912                    std::ptr::null(),
10913                    0,
10914                    children[1].cu_graph(),
10915                ),
10916                "routes child r1",
10917            )?;
10918            let deps = [n0, n1];
10919            cu_try(
10920                sys::cuGraphAddChildGraphNode(
10921                    &mut n2,
10922                    parent,
10923                    deps.as_ptr(),
10924                    2,
10925                    children[2].cu_graph(),
10926                ),
10927                "routes child root",
10928            )?;
10929        }
10930        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
10931        unsafe {
10932            cu_try(
10933                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
10934                "routes instantiate",
10935            )?;
10936        }
10937        Ok(RoutesGraph {
10938            exec,
10939            parent,
10940            _children: children,
10941        })
10942    }
10943
10944    /// One rank's routes section for the token graph (event-free): staged input copy (raw
10945    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
10946    /// Eager device_routed wraps it with the entry-event wait.
10947    #[allow(clippy::too_many_arguments)]
10948    pub(crate) fn routes_rank_section(
10949        &self,
10950        experts: &ResidentNvfp4TensorParallel,
10951        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10952        raw_input_src: u64,
10953        local_out: usize,
10954        n_sel: usize,
10955        activation_limit: Option<f32>,
10956        rank_index: usize,
10957    ) -> Result<(), Box<dyn std::error::Error>> {
10958        let engine = &self.ranks[rank_index];
10959        {
10960            let _main = engine.gpu.enter_main()?;
10961            // sel/route_w land via raw copies from the e staging (fixed addresses).
10962            let (sel_e_ptr, w_e_ptr) = workspace
10963                .raw_dev_route_e
10964                .ok_or("routes rank section requires armed staging pointers")?;
10965            raw_copy_bytes(
10966                workspace.raw_input[rank_index],
10967                raw_input_src,
10968                experts.input_width * 4,
10969                engine,
10970            )?;
10971            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
10972            raw_copy_bytes(
10973                workspace.raw_route_w[rank_index],
10974                w_e_ptr,
10975                n_sel * 4,
10976                engine,
10977            )?;
10978            {
10979                let Nvfp4DeviceRoutesWorkspace {
10980                    input, in_q, in_d, ..
10981                } = &mut *workspace;
10982                engine.quantize_q8_1_into(
10983                    &input[rank_index],
10984                    1,
10985                    experts.input_width,
10986                    &mut in_q[rank_index],
10987                    &mut in_d[rank_index],
10988                )?;
10989            }
10990        }
10991        self.nvfp4_routes_batched_sweeps_rank(
10992            experts,
10993            workspace,
10994            &[],
10995            &[],
10996            &[],
10997            local_out,
10998            n_sel,
10999            activation_limit,
11000            true,
11001            rank_index,
11002        )
11003    }
11004
11005    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
11006    /// add, combined row raw-copied into the fixed e-context out stage.
11007    pub(crate) fn routes_root_section(
11008        &self,
11009        experts: &ResidentNvfp4TensorParallel,
11010        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11011    ) -> Result<(), Box<dyn std::error::Error>> {
11012        let root = &self.ranks[0];
11013        let _main = root.gpu.enter_main()?;
11014        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
11015            .raw_combine
11016            .ok_or("routes root section requires armed combine pointers")?;
11017        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
11018        {
11019            let Nvfp4DeviceRoutesWorkspace {
11020                accumulator,
11021                remote,
11022                combined,
11023                ..
11024            } = &mut *workspace;
11025            root.add(&accumulator[0], remote, combined, experts.input_width)?;
11026        }
11027        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
11028        Ok(())
11029    }
11030
11031    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
11032    /// combine set. Requires dev_route_e + in/out stages already allocated.
11033    pub(crate) fn routes_arm_raw(
11034        &self,
11035        experts: &ResidentNvfp4TensorParallel,
11036        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11037    ) -> Result<(), Box<dyn std::error::Error>> {
11038        use cudarc::driver::DevicePtr;
11039        if workspace.raw_dev_route_e.is_some() {
11040            return Ok(());
11041        }
11042        let _ = experts;
11043        let (sel_e, w_e) = workspace
11044            .dev_route_e
11045            .as_ref()
11046            .ok_or("routes staging not armed")?;
11047        let root = &self.ranks[0];
11048        {
11049            let _main = root.gpu.enter_main()?;
11050            let stream = root.stream();
11051            let (a, _g) = sel_e.device_ptr(&stream);
11052            let (b, _g) = w_e.device_ptr(&stream);
11053            workspace.raw_dev_route_e = Some((a as u64, b as u64));
11054            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
11055            let (d, _g) = workspace.remote.device_ptr(&stream);
11056            let (f, _g) = workspace.combined.device_ptr(&stream);
11057            let out_stage = workspace
11058                .out_stage_e
11059                .as_ref()
11060                .ok_or("routes out stage not armed")?;
11061            let (g_, _g) = out_stage.device_ptr(&stream);
11062            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
11063        }
11064        for rank in 0..self.ranks.len() {
11065            let engine = &self.ranks[rank];
11066            let _main = engine.gpu.enter_main()?;
11067            let stream = engine.stream();
11068            let (a, _g) = workspace.input[rank].device_ptr(&stream);
11069            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
11070            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
11071            workspace.raw_input.push(a as u64);
11072            workspace.raw_sel.push(b as u64);
11073            workspace.raw_route_w.push(c as u64);
11074        }
11075        Ok(())
11076    }
11077
11078    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
11079    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
11080    /// throughput claim.
11081    pub fn run_tensor_parallel_routes_nvfp4(
11082        &self,
11083        experts: &ResidentNvfp4TensorParallel,
11084        input: &[f32],
11085        tokens: usize,
11086        selected: &[usize],
11087        route_weights: &[f32],
11088        experts_per_token: usize,
11089        activation_limit: Option<f32>,
11090    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
11091        validate_activations(input, tokens, experts.input_width)?;
11092        let pairs = tokens
11093            .checked_mul(experts_per_token)
11094            .ok_or("NVFP4 TP route count overflow")?;
11095        if selected.len() != pairs || route_weights.len() != pairs {
11096            return Err(format!(
11097                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
11098                 {experts_per_token} ({pairs})",
11099                selected.len(),
11100                route_weights.len(),
11101            )
11102            .into());
11103        }
11104        if !route_weights.iter().all(|weight| weight.is_finite()) {
11105            return Err("NVFP4 TP route weights contain a non-finite value".into());
11106        }
11107
11108        let mut output = vec![0.0f32; tokens * experts.input_width];
11109        for token in 0..tokens {
11110            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
11111            for slot in 0..experts_per_token {
11112                let pair = token * experts_per_token + slot;
11113                let expert = selected[pair];
11114                if expert >= experts.expert_count {
11115                    return Err(format!(
11116                        "NVFP4 TP selected expert {expert} outside 0..{}",
11117                        experts.expert_count
11118                    )
11119                    .into());
11120                }
11121                let gate = self.run_column_bank_expert_nvfp4(
11122                    &experts.gate,
11123                    &experts.macros_gate,
11124                    expert,
11125                    input_row,
11126                )?;
11127                let up = self.run_column_bank_expert_nvfp4(
11128                    &experts.up,
11129                    &experts.macros_up,
11130                    expert,
11131                    input_row,
11132                )?;
11133                let activated: Vec<f32> = gate
11134                    .iter()
11135                    .zip(&up)
11136                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
11137                    .collect();
11138                debug_assert_eq!(activated.len(), experts.expert_width);
11139                let down = self.run_row_bank_expert_nvfp4(
11140                    &experts.down,
11141                    &experts.macros_down,
11142                    expert,
11143                    &activated,
11144                )?;
11145                let weight = route_weights[pair];
11146                for (sum, value) in output
11147                    [token * experts.input_width..(token + 1) * experts.input_width]
11148                    .iter_mut()
11149                    .zip(down)
11150                {
11151                    *sum += weight * value;
11152                }
11153            }
11154        }
11155        Ok(output)
11156    }
11157}
11158
11159#[cfg(test)]
11160mod tests {
11161    use super::*;
11162
11163    #[test]
11164    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
11165        let limit = Some(7.0);
11166        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
11167        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
11168        assert!(
11169            step_expert_activation_host(-20.0, 9.0, limit).abs()
11170                < step_expert_activation_host(-20.0, 9.0, None).abs()
11171        );
11172        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
11173        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
11174        assert!(validate_step_expert_activation_limit(limit).is_ok());
11175    }
11176
11177    #[test]
11178    fn moe_residual_host_preserves_official_add_order() {
11179        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
11180        assert_eq!(output, [0.0]);
11181        assert_eq!(
11182            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
11183            "MoE residual lengths residual=1 routed=2 shared=1"
11184        );
11185    }
11186
11187    #[test]
11188    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
11189        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
11190        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
11191        assert_eq!(owners.len(), 4);
11192        for (rank, owner) in owners.iter().enumerate() {
11193            assert_eq!(owner.rank, rank);
11194            assert_eq!(owner.selected, vec![0, 36]);
11195            assert_eq!(owner.token_rows, vec![0, 0]);
11196            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
11197        }
11198    }
11199
11200    #[test]
11201    fn expert_owner_routes_validate_geometry_and_selected_experts() {
11202        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
11203        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
11204        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
11205        assert!(error.contains("outside 0..288"));
11206    }
11207
11208    #[test]
11209    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
11210        let selected = [
11211            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
11212        ];
11213        assert_eq!(
11214            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
11215            16
11216        );
11217        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
11218        assert_eq!(
11219            owners
11220                .iter()
11221                .map(|owner| owner.selected.len())
11222                .collect::<Vec<_>>(),
11223            vec![2, 4, 6, 4]
11224        );
11225        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
11226        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
11227        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
11228    }
11229
11230    #[test]
11231    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
11232        let owner0 = [0usize, 3];
11233        let owner1 = [1usize, 2];
11234        let owners = [owner0.as_slice(), owner1.as_slice()];
11235        assert_eq!(
11236            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
11237                .unwrap(),
11238            WeightedRouteCombineShape {
11239                pairs: 4,
11240                max_pairs: 12,
11241            }
11242        );
11243        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
11244        assert!(
11245            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
11246                .is_err()
11247        );
11248        assert!(
11249            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
11250                .is_err()
11251        );
11252        assert!(
11253            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
11254                .is_err()
11255        );
11256    }
11257
11258    #[test]
11259    fn native_p2p_door_is_strict_and_default_off() {
11260        assert!(!parse_step_tp_native_p2p(None).unwrap());
11261        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
11262        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
11263        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
11264        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
11265        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
11266    }
11267
11268    #[test]
11269    fn bulk_p2p_door_is_strict_and_default_off() {
11270        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
11271        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
11272        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
11273        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
11274        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
11275        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
11276    }
11277
11278    #[test]
11279    fn ep_device_arithmetic_door_is_strict_and_default_off() {
11280        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
11281        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
11282        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
11283        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
11284        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
11285        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
11286    }
11287
11288    #[test]
11289    fn f32_mirror_door_is_strict_and_default_off() {
11290        assert!(!parse_step_tp_f32_mirror(None).unwrap());
11291        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
11292        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
11293        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
11294        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
11295        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
11296    }
11297
11298    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
11299        let codes = (0..out_features * in_features)
11300            .map(|index| (index % 251) as u8)
11301            .collect();
11302        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
11303            .map(|index| index as f32 + 1.0)
11304            .collect();
11305        (codes, scales)
11306    }
11307
11308    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
11309        (0..out_features * in_features)
11310            .flat_map(|value| (value as u16).to_le_bytes())
11311            .collect()
11312    }
11313
11314    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
11315        bytes
11316            .chunks_exact(2)
11317            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
11318            .collect()
11319    }
11320
11321    #[test]
11322    fn bf16_matrix_rejects_wrong_byte_count() {
11323        let bytes = vec![0u8; 4 * 4 * 2 - 1];
11324        let matrix = Bf16Matrix {
11325            bytes: &bytes,
11326            out_features: 4,
11327            in_features: 4,
11328        };
11329        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
11330    }
11331
11332    #[test]
11333    fn replicated_device_rows_require_exact_rank_local_shapes() {
11334        assert_eq!(
11335            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
11336            12_288
11337        );
11338        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
11339        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
11340        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
11341        assert!(
11342            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
11343        );
11344        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
11345    }
11346
11347    #[test]
11348    fn replicated_device_row_refresh_requires_exact_root_source() {
11349        assert_eq!(
11350            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
11351            12_288
11352        );
11353        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
11354        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
11355        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
11356        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
11357        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
11358    }
11359
11360    #[test]
11361    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
11362        for tp in [1, 2, 4, 8] {
11363            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
11364            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
11365            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
11366            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
11367            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
11368        }
11369        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
11370        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
11371        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
11372        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
11373    }
11374
11375    #[test]
11376    fn cache_rows_split_by_token_then_rank() {
11377        let rows = (0u8..24).collect::<Vec<_>>();
11378        assert_eq!(
11379            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
11380            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
11381        );
11382        assert_eq!(
11383            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
11384            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
11385        );
11386        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
11387        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
11388    }
11389
11390    #[test]
11391    fn bf16_column_shard_preserves_contiguous_output_rows() {
11392        let bytes = bf16_matrix_bytes(4, 4);
11393        let matrix = Bf16Matrix {
11394            bytes: &bytes,
11395            out_features: 4,
11396            in_features: 4,
11397        };
11398        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
11399        assert_eq!(shard.out_features, 2);
11400        assert_eq!(shard.in_features, 4);
11401        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
11402    }
11403
11404    #[test]
11405    fn bf16_row_shard_preserves_each_input_column_window() {
11406        let bytes = bf16_matrix_bytes(3, 4);
11407        let matrix = Bf16Matrix {
11408            bytes: &bytes,
11409            out_features: 3,
11410            in_features: 4,
11411        };
11412        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
11413        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
11414    }
11415
11416    #[test]
11417    fn bf16_row_block_preserves_global_column_order() {
11418        let bytes = bf16_matrix_bytes(3, 8);
11419        let matrix = Bf16Matrix {
11420            bytes: &bytes,
11421            out_features: 3,
11422            in_features: 8,
11423        };
11424        let block = bf16_row_block(matrix, 2, 3).unwrap();
11425        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
11426    }
11427
11428    #[test]
11429    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
11430        let (codes, scales) = matrix(1280, 4096);
11431        let matrix = E4m3BlockMatrix {
11432            codes: &codes,
11433            scales: &scales,
11434            out_features: 1280,
11435            in_features: 4096,
11436        };
11437        let shard = column_shard(matrix, 2, 1).unwrap();
11438        assert_eq!(shard.out_features, 640);
11439        assert_eq!(shard.codes, &codes[640 * 4096..]);
11440        assert_eq!(shard.scales, &scales[5 * 32..]);
11441    }
11442
11443    #[test]
11444    fn row_shard_preserves_each_weight_and_scale_column_window() {
11445        let (codes, scales) = matrix(4096, 1280);
11446        let matrix = E4m3BlockMatrix {
11447            codes: &codes,
11448            scales: &scales,
11449            out_features: 4096,
11450            in_features: 1280,
11451        };
11452        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
11453        assert_eq!(shard_codes.len(), 4096 * 640);
11454        assert_eq!(&shard_codes[..640], &codes[640..1280]);
11455        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
11456        assert_eq!(shard_scales.len(), 32 * 5);
11457        assert_eq!(&shard_scales[..5], &scales[5..10]);
11458        assert_eq!(&shard_scales[5..10], &scales[15..20]);
11459    }
11460
11461    #[test]
11462    fn activation_shards_keep_token_rows_separate() {
11463        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
11464        assert_eq!(
11465            activation_shard(&activations, 2, 8, 2, 1),
11466            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
11467        );
11468    }
11469
11470    #[test]
11471    fn expert_bank_selects_expert_major_code_and_scale_planes() {
11472        let expert_count = 2;
11473        let out_features = 128;
11474        let in_features = 128;
11475        let code_stride = out_features * in_features;
11476        let codes: Vec<u8> = (0..expert_count * code_stride)
11477            .map(|index| (index % 251) as u8)
11478            .collect();
11479        let scales = vec![1.0f32, 2.0];
11480        let bank = E4m3ExpertBank {
11481            codes: &codes,
11482            scales: &scales,
11483            expert_count,
11484            out_features,
11485            in_features,
11486        };
11487        bank.validate().unwrap();
11488        let expert = bank.expert(1).unwrap();
11489        assert_eq!(expert.codes, &codes[code_stride..]);
11490        assert_eq!(expert.scales, &[2.0]);
11491    }
11492
11493    #[test]
11494    fn expert_bank_rejects_non_positive_scale() {
11495        let codes = vec![0u8; 128 * 128];
11496        let scales = vec![0.0f32];
11497        let bank = E4m3ExpertBank {
11498            codes: &codes,
11499            scales: &scales,
11500            expert_count: 1,
11501            out_features: 128,
11502            in_features: 128,
11503        };
11504        assert!(bank.validate().unwrap_err().contains("non-positive"));
11505    }
11506
11507    #[test]
11508    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
11509        let expert_count = 2;
11510        let out_features = 256;
11511        let in_features = 128;
11512        let code_stride = out_features * in_features;
11513        let scale_stride = 2;
11514        let codes = (0..expert_count * code_stride)
11515            .map(|index| (index % 251) as u8)
11516            .collect::<Vec<_>>();
11517        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
11518        let bank = E4m3ExpertBank {
11519            codes: &codes,
11520            scales: &scales,
11521            expert_count,
11522            out_features,
11523            in_features,
11524        };
11525
11526        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
11527        assert_eq!(rank.out_features, 128);
11528        assert_eq!(rank.in_features, 128);
11529        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
11530        assert_eq!(rank.scales, vec![11.0, 21.0]);
11531        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
11532        assert_eq!(
11533            &rank.codes[128 * 128..],
11534            &codes[code_stride + 128 * 128..2 * code_stride]
11535        );
11536        assert_eq!(scale_stride, scales.len() / expert_count);
11537    }
11538
11539    #[test]
11540    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
11541        let expert_count = 2;
11542        let out_features = 128;
11543        let in_features = 256;
11544        let code_stride = out_features * in_features;
11545        let codes = (0..expert_count * code_stride)
11546            .map(|index| (index % 251) as u8)
11547            .collect::<Vec<_>>();
11548        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
11549        let bank = E4m3ExpertBank {
11550            codes: &codes,
11551            scales: &scales,
11552            expert_count,
11553            out_features,
11554            in_features,
11555        };
11556
11557        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
11558        assert_eq!(rank.out_features, 128);
11559        assert_eq!(rank.in_features, 128);
11560        assert_eq!(rank.k_blocks, Some(1));
11561        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
11562        assert_eq!(rank.scales, vec![11.0, 21.0]);
11563        assert_eq!(&rank.codes[..128], &codes[128..256]);
11564        assert_eq!(
11565            &rank.codes[128 * 128..128 * 128 + 128],
11566            &codes[code_stride + 128..code_stride + 256]
11567        );
11568    }
11569
11570    #[test]
11571    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
11572        let expert_count = 2;
11573        let out_features = 256;
11574        let in_features = 512;
11575        let code_stride = out_features * in_features;
11576        let mut codes = vec![0u8; expert_count * code_stride];
11577        for expert in 0..expert_count {
11578            for row in 0..out_features {
11579                for block in 0..4 {
11580                    let value = (expert * 80 + block * 16 + row % 16) as u8;
11581                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
11582                    codes[start..start + FP8_BLOCK].fill(value);
11583                }
11584            }
11585        }
11586        let scales = vec![
11587            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,
11588            112.0, 113.0, 114.0,
11589        ];
11590        let bank = E4m3ExpertBank {
11591            codes: &codes,
11592            scales: &scales,
11593            expert_count,
11594            out_features,
11595            in_features,
11596        };
11597
11598        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
11599        assert_eq!(rank.out_features, out_features);
11600        assert_eq!(rank.in_features, 256);
11601        assert_eq!(rank.k_blocks, Some(2));
11602        assert_eq!(rank.code_stride, out_features * 256);
11603        assert_eq!(rank.scale_stride, 4);
11604        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
11605        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
11606
11607        let block_stride = out_features * FP8_BLOCK;
11608        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
11609        assert!(
11610            rank.codes[block_stride..block_stride + FP8_BLOCK]
11611                .iter()
11612                .all(|&code| code == 48)
11613        );
11614        assert!(
11615            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
11616                .iter()
11617                .all(|&code| code == 112)
11618        );
11619        assert!(
11620            rank.codes
11621                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
11622                .iter()
11623                .all(|&code| code == 128)
11624        );
11625    }
11626
11627    #[test]
11628    fn step_ep_layer_specs_are_literal_and_fail_closed() {
11629        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
11630        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
11631        assert_eq!(
11632            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
11633            vec![StepEpLayerSpec {
11634                layer: 24,
11635                devices: vec![1, 2],
11636            }]
11637        );
11638        assert_eq!(
11639            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
11640            vec![
11641                StepEpLayerSpec {
11642                    layer: 24,
11643                    devices: vec![1, 2],
11644                },
11645                StepEpLayerSpec {
11646                    layer: 25,
11647                    devices: vec![1, 2],
11648                },
11649                StepEpLayerSpec {
11650                    layer: 31,
11651                    devices: vec![0, 2],
11652                },
11653            ]
11654        );
11655        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
11656        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
11657        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
11658        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
11659        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
11660        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
11661        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
11662    }
11663
11664    #[test]
11665    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
11666        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
11667        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
11668        assert_eq!(
11669            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
11670            vec![
11671                StepTpLayerSpec {
11672                    layer: 24,
11673                    devices: vec![1, 2],
11674                },
11675                StepTpLayerSpec {
11676                    layer: 25,
11677                    devices: vec![1, 2],
11678                },
11679            ]
11680        );
11681        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
11682        assert!(error.contains("MEMRA_STEP_TP"));
11683        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
11684        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
11685
11686        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
11687        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
11688        assert_eq!(all.first().unwrap().layer, 0);
11689        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
11690        let devices = (0..8).collect::<Vec<_>>();
11691        assert!(all.iter().all(|spec| spec.devices == devices));
11692        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
11693    }
11694}
11695
11696// ===== Whole-token graph builder (increment B) ==================================================
11697//
11698// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
11699// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
11700// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
11701// device and records a child + its dependency edges. A token then assembles as ONE multi-device
11702// parent (children per section per layer), launched once per token — the launch-collapse the
11703// per-layer minis could not reach (routes-mini negative, 2026-08-21).
11704
11705/// One captured section: the child graph plus which parent node it became, and the CUDA
11706/// context it was captured under (exec memset updates need it).
11707struct TokenGraphChild {
11708    graph: cudarc::driver::CudaGraph,
11709    node: cudarc::driver::sys::CUgraphNode,
11710    ctx: cudarc::driver::sys::CUcontext,
11711}
11712
11713/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
11714/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
11715/// handles address the parent's CLONED child graphs (the M1-probed update path).
11716struct TokenGraphFaSite {
11717    ctx: cudarc::driver::sys::CUcontext,
11718    memset_o: cudarc::driver::sys::CUgraphNode,
11719    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
11720    fa: cudarc::driver::sys::CUgraphNode,
11721    combine: cudarc::driver::sys::CUgraphNode,
11722    window: usize,
11723    n_head: usize,
11724    n_head_kv: usize,
11725    head_dim: usize,
11726}
11727
11728pub struct TokenGraphBuilder {
11729    parent: cudarc::driver::sys::CUgraph,
11730    children: Vec<TokenGraphChild>,
11731    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
11732    /// several while a parallel group is open.
11733    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
11734    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
11735    /// non-group section (they never gate a parallel group merge — the SH1 shape).
11736    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
11737    /// Open parallel group: sections issued under the same group id fork from the SAME
11738    /// predecessor set and merge into the frontier together when the group closes.
11739    group: Option<(
11740        u32,
11741        Vec<cudarc::driver::sys::CUgraphNode>,
11742        Vec<cudarc::driver::sys::CUgraphNode>,
11743    )>,
11744}
11745
11746// SAFETY: single decode thread; graph handles are process handles.
11747unsafe impl Send for TokenGraphBuilder {}
11748
11749impl TokenGraphBuilder {
11750    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
11751        use cudarc::driver::sys;
11752        let mut parent: sys::CUgraph = std::ptr::null_mut();
11753        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
11754        if r != sys::CUresult::CUDA_SUCCESS {
11755            return Err(format!("token graph create: {r:?}").into());
11756        }
11757        Ok(Self {
11758            parent,
11759            children: Vec::new(),
11760            frontier: Vec::new(),
11761            pending_detached: Vec::new(),
11762            group: None,
11763        })
11764    }
11765
11766    fn push_child(
11767        &mut self,
11768        graph: cudarc::driver::CudaGraph,
11769        parallel_group: Option<u32>,
11770        detached: bool,
11771        absorb: bool,
11772        ctx: cudarc::driver::sys::CUcontext,
11773    ) -> Result<(), Box<dyn std::error::Error>> {
11774        use cudarc::driver::sys;
11775        // Resolve the dependency set: serial sections depend on the current frontier; a
11776        // parallel-group section depends on the frontier AS OF the group opening; a
11777        // DETACHED section forks like a group member but joins only the next serial section.
11778        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
11779            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
11780            (state, Some(group)) => {
11781                // opening a new group (closing any previous one first)
11782                if let Some((_, _, members)) = state.take() {
11783                    self.frontier = members;
11784                }
11785                let base = self.frontier.clone();
11786                *state = Some((group, base.clone(), Vec::new()));
11787                base
11788            }
11789            (state, None) if detached => match state.as_ref() {
11790                Some((_, base, _)) => base.clone(),
11791                None => self.frontier.clone(),
11792            },
11793            (state, None) => {
11794                if let Some((_, _, members)) = state.take() {
11795                    self.frontier = members;
11796                }
11797                let mut deps = self.frontier.clone();
11798                if absorb {
11799                    deps.append(&mut self.pending_detached);
11800                }
11801                deps
11802            }
11803        };
11804        let mut node: sys::CUgraphNode = std::ptr::null_mut();
11805        let r = unsafe {
11806            sys::cuGraphAddChildGraphNode(
11807                &mut node,
11808                self.parent,
11809                if deps.is_empty() {
11810                    std::ptr::null()
11811                } else {
11812                    deps.as_ptr()
11813                },
11814                deps.len(),
11815                graph.cu_graph(),
11816            )
11817        };
11818        if r != sys::CUresult::CUDA_SUCCESS {
11819            return Err(format!("token graph child: {r:?}").into());
11820        }
11821        match (&mut self.group, parallel_group, detached) {
11822            (_, None, true) => self.pending_detached.push(node),
11823            (Some((_, _, members)), Some(_), _) => members.push(node),
11824            _ => self.frontier = vec![node],
11825        }
11826        self.children.push(TokenGraphChild { graph, node, ctx });
11827        Ok(())
11828    }
11829
11830    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
11831        use cudarc::driver::sys;
11832        if let Some((_, _, members)) = self.group.take() {
11833            self.frontier = members;
11834        }
11835        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
11836        // the node handles the exec update path (M1) addresses.
11837        let mut fa_sites = Vec::new();
11838        for child in &self.children {
11839            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
11840                fa_sites.push(site);
11841            }
11842        }
11843        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
11844        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
11845        if r != sys::CUresult::CUDA_SUCCESS {
11846            return Err(format!("token graph instantiate: {r:?}").into());
11847        }
11848        Ok(TokenGraph {
11849            exec,
11850            parent: self.parent,
11851            _children: self.children,
11852            fa_sites,
11853        })
11854    }
11855}
11856
11857/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
11858/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
11859fn discover_fa_site(
11860    child_node: cudarc::driver::sys::CUgraphNode,
11861    ctx: cudarc::driver::sys::CUcontext,
11862) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
11863    use cudarc::driver::sys;
11864    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
11865        if r == sys::CUresult::CUDA_SUCCESS {
11866            Ok(())
11867        } else {
11868            Err(format!("{what}: {r:?}").into())
11869        }
11870    }
11871    let mut graph: sys::CUgraph = std::ptr::null_mut();
11872    unsafe {
11873        cu_try(
11874            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
11875            "fa-site child GetGraph",
11876        )?;
11877    }
11878    let mut count: usize = 0;
11879    unsafe {
11880        cu_try(
11881            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
11882            "fa-site GetNodes(count)",
11883        )?;
11884    }
11885    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
11886    unsafe {
11887        cu_try(
11888            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
11889            "fa-site GetNodes",
11890        )?;
11891    }
11892    nodes.truncate(count);
11893    let node_type =
11894        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
11895            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
11896            unsafe {
11897                cu_try(
11898                    sys::cuGraphNodeGetType(node, &mut ty),
11899                    "fa-site NodeGetType",
11900                )?;
11901            }
11902            Ok(ty)
11903        };
11904    let memsets: Vec<sys::CUgraphNode> = {
11905        let mut v = Vec::new();
11906        for &node in &nodes {
11907            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
11908                v.push(node);
11909            }
11910        }
11911        v
11912    };
11913    if memsets.len() != 3 {
11914        return Ok(None);
11915    }
11916    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
11917    let dependents =
11918        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
11919            let mut n: usize = 0;
11920            unsafe {
11921                cu_try(
11922                    sys::cuGraphNodeGetDependentNodes_v2(
11923                        node,
11924                        std::ptr::null_mut(),
11925                        std::ptr::null_mut(),
11926                        &mut n,
11927                    ),
11928                    "fa-site GetDependentNodes(count)",
11929                )?;
11930            }
11931            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
11932            unsafe {
11933                cu_try(
11934                    sys::cuGraphNodeGetDependentNodes_v2(
11935                        node,
11936                        v.as_mut_ptr(),
11937                        std::ptr::null_mut(),
11938                        &mut n,
11939                    ),
11940                    "fa-site GetDependentNodes",
11941                )?;
11942            }
11943            v.truncate(n);
11944            Ok(v)
11945        };
11946    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
11947    // ordered among themselves but interchangeable for width updates.
11948    let mut fa: Option<sys::CUgraphNode> = None;
11949    let mut last_memset: Option<sys::CUgraphNode> = None;
11950    for &ms in &memsets {
11951        for dep in dependents(ms)? {
11952            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
11953                fa = Some(dep);
11954                last_memset = Some(ms);
11955            }
11956        }
11957    }
11958    let (Some(fa), Some(_last)) = (fa, last_memset) else {
11959        return Ok(None);
11960    };
11961    let mut combine: Option<sys::CUgraphNode> = None;
11962    for dep in dependents(fa)? {
11963        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
11964            combine = Some(dep);
11965        }
11966    }
11967    let Some(combine) = combine else {
11968        return Ok(None);
11969    };
11970    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
11971    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
11972    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
11973    unsafe {
11974        cu_try(
11975            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
11976            "fa-site KernelNodeGetParams",
11977        )?;
11978    }
11979    let arg_i32 =
11980        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
11981    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
11982    // Identify the o-partial memset (hd x wider than the m/l pair).
11983    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
11984        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
11985        unsafe {
11986            cu_try(
11987                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
11988                "fa-site MemsetNodeGetParams",
11989            )?;
11990        }
11991        Ok(mp.width)
11992    };
11993    let mut widest = memsets[0];
11994    for &ms in &memsets[1..] {
11995        if width_of(ms)? > width_of(widest)? {
11996            widest = ms;
11997        }
11998    }
11999    let memset_m: Vec<sys::CUgraphNode> =
12000        memsets.iter().copied().filter(|&m| m != widest).collect();
12001    Ok(Some(TokenGraphFaSite {
12002        ctx,
12003        memset_o: widest,
12004        memset_m: [memset_m[0], memset_m[1]],
12005        fa,
12006        combine,
12007        window: win as usize,
12008        n_head: nh as usize,
12009        n_head_kv: nhkv as usize,
12010        head_dim: hd as usize,
12011    }))
12012}
12013
12014pub struct TokenGraph {
12015    exec: cudarc::driver::sys::CUgraphExec,
12016    parent: cudarc::driver::sys::CUgraph,
12017    _children: Vec<TokenGraphChild>,
12018    fa_sites: Vec<TokenGraphFaSite>,
12019}
12020
12021unsafe impl Send for TokenGraph {}
12022
12023impl TokenGraph {
12024    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
12025    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
12026    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
12027    /// move together so the exec always matches what a fresh build at `bucket` would bake.
12028    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
12029        use cudarc::driver::sys;
12030        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12031            if r == sys::CUresult::CUDA_SUCCESS {
12032                Ok(())
12033            } else {
12034                Err(format!("{what}: {r:?}").into())
12035            }
12036        }
12037        for site in &self.fa_sites {
12038            let layer_bucket = if site.window > 0 {
12039                bucket.min(site.window)
12040            } else {
12041                bucket
12042            };
12043            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
12044            let nsp = layer_bucket.div_ceil(sp).max(1);
12045            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
12046            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12047            unsafe {
12048                cu_try(
12049                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
12050                    "retarget fa GetParams",
12051                )?;
12052                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
12053                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
12054                params.gridDimY = nsp as u32;
12055                cu_try(
12056                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
12057                    "retarget fa SetParams",
12058                )?;
12059            }
12060            // combine: nsp (slot 6).
12061            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12062            unsafe {
12063                cu_try(
12064                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
12065                    "retarget combine GetParams",
12066                )?;
12067                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
12068                cu_try(
12069                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
12070                    "retarget combine SetParams",
12071                )?;
12072            }
12073            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
12074            let set_width =
12075                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
12076                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
12077                    unsafe {
12078                        cu_try(
12079                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
12080                            "retarget memset GetParams",
12081                        )?;
12082                    }
12083                    mp.width = width;
12084                    unsafe {
12085                        cu_try(
12086                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
12087                            "retarget memset SetParams",
12088                        )?;
12089                    }
12090                    Ok(())
12091                };
12092            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
12093            set_width(site.memset_m[0], site.n_head * nsp)?;
12094            set_width(site.memset_m[1], site.n_head * nsp)?;
12095        }
12096        Ok(())
12097    }
12098
12099    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
12100        use cudarc::driver::sys;
12101        let _main = e.gpu.enter_main()?;
12102        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
12103        if r != sys::CUresult::CUDA_SUCCESS {
12104            return Err(format!("token graph launch: {r:?}").into());
12105        }
12106        Ok(())
12107    }
12108}
12109
12110impl Drop for TokenGraph {
12111    fn drop(&mut self) {
12112        unsafe {
12113            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
12114            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
12115        }
12116    }
12117}
12118
12119std::thread_local! {
12120    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
12121        const { std::cell::RefCell::new(None) };
12122}
12123
12124/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
12125pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
12126    let builder = TokenGraphBuilder::new()?;
12127    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
12128    Ok(())
12129}
12130
12131/// Take the finished parent (ends build mode).
12132pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
12133    let builder = TOKEN_GRAPH_BUILDER
12134        .with(|cell| cell.borrow_mut().take())
12135        .ok_or("token graph build was not begun")?;
12136    builder.finish()
12137}
12138
12139/// True while the thread-local builder is armed.
12140pub fn token_graph_building() -> bool {
12141    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
12142}
12143
12144/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
12145/// stream capture on `engine`'s stream and records the child. Sections sharing a
12146/// `parallel_group` id fork from the same predecessor set and merge together. The closure
12147/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
12148pub fn graph_section<F>(
12149    engine: &Engine,
12150    parallel_group: Option<u32>,
12151    f: F,
12152) -> Result<(), Box<dyn std::error::Error>>
12153where
12154    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12155{
12156    graph_section_opts(engine, parallel_group, false, false, f)
12157}
12158
12159/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
12160pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
12161where
12162    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12163{
12164    graph_section_opts(engine, None, false, true, f)
12165}
12166
12167/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
12168/// group base) and is joined only by the next serial section — never gates a group merge.
12169pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
12170where
12171    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12172{
12173    graph_section_opts(engine, None, true, false, f)
12174}
12175
12176pub fn graph_section_opts<F>(
12177    engine: &Engine,
12178    parallel_group: Option<u32>,
12179    detached: bool,
12180    absorb: bool,
12181    f: F,
12182) -> Result<(), Box<dyn std::error::Error>>
12183where
12184    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12185{
12186    let building = token_graph_building();
12187    if !building {
12188        let mut f = f;
12189        return f();
12190    }
12191    let (child, ctx) = {
12192        let _main = engine.gpu.enter_main()?;
12193        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
12194        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
12195        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
12196            return Err(format!("graph section ctx query: {r:?}").into());
12197        }
12198        let mut f = f;
12199        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
12200        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
12201        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
12202        (child, ctx)
12203    };
12204    TOKEN_GRAPH_BUILDER.with(|cell| {
12205        cell.borrow_mut()
12206            .as_mut()
12207            .expect("builder checked above")
12208            .push_child(child, parallel_group, detached, absorb, ctx)
12209    })
12210}