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
175/// MEMRA_SEL_DOWN8=1: fuse the NVFP4 down sweep with the route-weight combine and run one
176/// warp per routed slot (the q8 `down8 w8` occupancy arm). Bit-identical; default OFF until
177/// receipted on this bank family.
178/// MEMRA_SEL_MIRROR=1: the per-rank routed-selection pull runs as ONE `moe_sel_w_mirror`
179/// launch instead of two 32-byte D2D copies, and when every consuming rank shares e's device
180/// the intermediate e-context staging pair is skipped entirely (the caller's sel/route_w rows
181/// are process-persistent, so the ranks read them directly). Bit-identical: same bytes, one
182/// fewer hop. Refused under the graph door, whose captured copies need the fixed staging
183/// addresses. Default OFF until receipted.
184/// MEMRA_FENCE_RANK1=1: the peer rank rings a doorbell in ROOT memory with a kernel store
185/// (`memra_ring_flag`) and the model engine waits it with a SAME-DEVICE stream memop, instead
186/// of waiting a cross-device event. Completes the half the memops receipt left open (peer
187/// memops are rejected; peer kernel stores are the direct-join mechanism). Ordering only —
188/// values are untouched. Requires MEMRA_FENCE_MEMOPS=1 (it owns the flag allocation).
189pub(crate) fn fence_rank1_on() -> bool {
190    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
191    *ON.get_or_init(|| std::env::var("MEMRA_FENCE_RANK1").as_deref() == Ok("1"))
192}
193
194pub(crate) fn sel_mirror_on() -> bool {
195    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
196    *ON.get_or_init(|| std::env::var("MEMRA_SEL_MIRROR").as_deref() == Ok("1"))
197}
198
199pub(crate) fn sel_down8_on() -> bool {
200    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
201    *ON.get_or_init(|| std::env::var("MEMRA_SEL_DOWN8").as_deref() == Ok("1"))
202}
203
204pub(crate) fn oproj_direct_on() -> bool {
205    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
206    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
207}
208
209pub(crate) fn raw_copy_bytes(
210    dst: u64,
211    src: u64,
212    bytes: usize,
213    engine: &Engine,
214) -> Result<(), Box<dyn std::error::Error>> {
215    use cudarc::driver::sys;
216    let r = unsafe {
217        sys::cuMemcpyAsync(
218            dst as sys::CUdeviceptr,
219            src as sys::CUdeviceptr,
220            bytes,
221            engine.stream().cu_stream() as sys::CUstream,
222        )
223    };
224    if r == sys::CUresult::CUDA_SUCCESS {
225        Ok(())
226    } else {
227        Err(format!("raw_copy_bytes: {r:?}").into())
228    }
229}
230
231pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
232    let silu = gate / (1.0 + (-gate).exp());
233    match limit {
234        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
235        None => silu * up,
236    }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq)]
240struct ExpertOwnerRoutes {
241    rank: usize,
242    selected: Vec<usize>,
243    token_rows: Vec<usize>,
244    global_pairs: Vec<usize>,
245}
246
247fn partition_expert_owner_routes(
248    expert_count: usize,
249    ranks: usize,
250    tokens: usize,
251    experts_per_token: usize,
252    selected: &[usize],
253) -> Result<Vec<ExpertOwnerRoutes>, String> {
254    if expert_count == 0
255        || ranks == 0
256        || tokens == 0
257        || experts_per_token == 0
258        || expert_count % ranks != 0
259    {
260        return Err(format!(
261            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
262             tokens={tokens} experts_per_token={experts_per_token}"
263        ));
264    }
265    let pairs = tokens
266        .checked_mul(experts_per_token)
267        .ok_or("expert-owner route count overflow")?;
268    if selected.len() != pairs {
269        return Err(format!(
270            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
271            selected.len()
272        ));
273    }
274    let per_rank = expert_count / ranks;
275    let mut owners = (0..ranks)
276        .map(|rank| ExpertOwnerRoutes {
277            rank,
278            selected: Vec::new(),
279            token_rows: Vec::new(),
280            global_pairs: Vec::new(),
281        })
282        .collect::<Vec<_>>();
283    for (pair, &expert) in selected.iter().enumerate() {
284        if expert >= expert_count {
285            return Err(format!(
286                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
287            ));
288        }
289        let rank = expert / per_rank;
290        owners[rank].selected.push(expert - rank * per_rank);
291        owners[rank].token_rows.push(pair / experts_per_token);
292        owners[rank].global_pairs.push(pair);
293    }
294    Ok(owners)
295}
296
297fn validate_step_grouped_owner_routes(
298    expert_count: usize,
299    tokens: usize,
300    selected: &[usize],
301) -> Result<usize, String> {
302    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
303        return Err(format!(
304            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
305             experts={expert_count} tokens={tokens}",
306            STEP_GROUPED_FP8_EXPERTS
307        ));
308    }
309    let pairs = tokens
310        .checked_mul(STEP_GROUPED_FP8_TOP_K)
311        .ok_or("official Step owner-grouped FP8 route count overflow")?;
312    if selected.len() != pairs {
313        return Err(format!(
314            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
315            selected.len(),
316            STEP_GROUPED_FP8_TOP_K,
317        ));
318    }
319    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
320        let mut unique = routes.to_vec();
321        unique.sort_unstable();
322        unique.dedup();
323        if unique.len() != STEP_GROUPED_FP8_TOP_K {
324            return Err(format!(
325                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
326                 {routes:?}"
327            ));
328        }
329    }
330    Ok(pairs)
331}
332
333#[derive(Debug, Clone, Copy, PartialEq, Eq)]
334struct WeightedRouteCombineShape {
335    pairs: usize,
336    max_pairs: usize,
337}
338
339fn validate_weighted_route_combine(
340    width: usize,
341    experts_per_token: usize,
342    max_tokens: usize,
343    tokens: usize,
344    owner_global_pairs: &[&[usize]],
345    route_weights: &[f32],
346) -> Result<WeightedRouteCombineShape, String> {
347    if width == 0
348        || experts_per_token == 0
349        || max_tokens == 0
350        || tokens == 0
351        || tokens > max_tokens
352        || width > i32::MAX as usize
353        || experts_per_token > i32::MAX as usize
354        || tokens > i32::MAX as usize
355    {
356        return Err(format!(
357            "invalid weighted route combine geometry width={width} experts_per_token=\
358             {experts_per_token} tokens={tokens}/{max_tokens}"
359        ));
360    }
361    let pairs = tokens
362        .checked_mul(experts_per_token)
363        .ok_or("weighted route combine pair count overflow")?;
364    let max_pairs = max_tokens
365        .checked_mul(experts_per_token)
366        .ok_or("weighted route combine capacity overflow")?;
367    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
368        return Err(format!(
369            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
370            route_weights.len()
371        ));
372    }
373    let mut seen = vec![false; pairs];
374    let mut observed = 0usize;
375    for pairs_for_owner in owner_global_pairs {
376        observed = observed
377            .checked_add(pairs_for_owner.len())
378            .ok_or("weighted route combine observed pair count overflow")?;
379        for &pair in *pairs_for_owner {
380            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
381                return Err(format!(
382                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
383                ));
384            }
385        }
386    }
387    if observed != pairs || seen.iter().any(|present| !present) {
388        return Err(format!(
389            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
390        ));
391    }
392    Ok(WeightedRouteCombineShape { pairs, max_pairs })
393}
394
395fn cache_rank_rows(
396    rows: &[u8],
397    tokens: usize,
398    local_token_bytes: usize,
399    ranks: usize,
400    rank: usize,
401) -> Result<Vec<u8>, String> {
402    if ranks == 0 || rank >= ranks {
403        return Err(format!(
404            "TP cache rank {rank} is outside a {ranks}-rank layout"
405        ));
406    }
407    let global_token_bytes = local_token_bytes
408        .checked_mul(ranks)
409        .ok_or("TP cache global token-byte overflow")?;
410    let expected = tokens
411        .checked_mul(global_token_bytes)
412        .ok_or("TP cache row-byte overflow")?;
413    if rows.len() != expected {
414        return Err(format!(
415            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
416            rows.len()
417        ));
418    }
419    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
420    for token in 0..tokens {
421        let start = token * global_token_bytes + rank * local_token_bytes;
422        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
423    }
424    Ok(shard)
425}
426
427fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
428    match value {
429        None | Some("") | Some("0") => Ok(false),
430        Some("1") => Ok(true),
431        Some(value) => Err(format!(
432            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
433        )),
434    }
435}
436
437pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
438    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
439}
440
441fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
442    match value {
443        None | Some("") | Some("0") => Ok(false),
444        Some("1") => Ok(true),
445        Some(value) => Err(format!(
446            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
447        )),
448    }
449}
450
451pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
452    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
453}
454
455fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
456    match value {
457        None | Some("") | Some("0") => Ok(false),
458        Some("1") => Ok(true),
459        Some(value) => Err(format!(
460            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
461        )),
462    }
463}
464
465fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
466    match value {
467        None | Some("") | Some("0") => Ok(false),
468        Some("1") => Ok(true),
469        Some(value) => Err(format!(
470            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
471        )),
472    }
473}
474
475/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
476/// host-canonical program remains the oracle until the device path carries its own gates.
477pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
478    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
479}
480
481pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
482    parse_step_ep_device_arithmetic(
483        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
484            .ok()
485            .as_deref(),
486    )
487}
488
489fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
490    match value {
491        None | Some("") | Some("0") => Ok(false),
492        Some("1") => Ok(true),
493        Some(value) => Err(format!(
494            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
495        )),
496    }
497}
498
499pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
500    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
501}
502
503fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
504    match value {
505        None | Some("") | Some("0") => Ok(false),
506        Some("1") => Ok(true),
507        Some(value) => Err(format!(
508            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
509        )),
510    }
511}
512
513/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
514/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
515/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
516/// side of the comparison).
517pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
518    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
519}
520
521fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
522    match value {
523        None | Some("") | Some("0") => Ok(false),
524        Some("1") => Ok(true),
525        Some(value) => Err(format!(
526            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
527        )),
528    }
529}
530
531fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
532    match value {
533        None | Some("") | Some("0") => Ok(false),
534        Some("1") => Ok(true),
535        Some(value) => Err(format!(
536            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
537        )),
538    }
539}
540
541/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
542/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
543/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
544pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
545    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
546}
547
548fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
549    match value {
550        None | Some("") | Some("0") => Ok(false),
551        Some("1") => Ok(true),
552        Some(value) => Err(format!(
553            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
554        )),
555    }
556}
557
558fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
559    match value {
560        None | Some("") | Some("0") => Ok(false),
561        Some("1") => Ok(true),
562        Some(value) => Err(format!(
563            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
564        )),
565    }
566}
567
568/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
569/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
570/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
571/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
572pub fn step_tp_dcw_enabled() -> Result<bool, String> {
573    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
574}
575
576/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
577/// expert program — per-layer multi-device parents built from per-rank children, launched on
578/// the model engine's stream; zero per-token node updates). Mechanism proven by
579/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
580pub fn step_tp_graph_enabled() -> Result<bool, String> {
581    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
582}
583
584/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
585/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
586/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
587pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
588    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
589}
590
591#[derive(Debug, Clone, PartialEq, Eq)]
592pub struct StepEpLayerSpec {
593    pub layer: usize,
594    pub devices: Vec<usize>,
595}
596
597pub type StepTpLayerSpec = StepEpLayerSpec;
598
599fn parse_step_layer_specs(
600    flag: &str,
601    value: Option<&str>,
602    allow_full_model: bool,
603) -> Result<Vec<StepEpLayerSpec>, String> {
604    let Some(value) = value else {
605        return Ok(Vec::new());
606    };
607    if value.is_empty() || value == "0" {
608        return Ok(Vec::new());
609    }
610
611    let mut specs = Vec::new();
612    for item in value.split(';') {
613        let (layers, devices) = item.split_once('@').ok_or_else(|| {
614            let layers = if allow_full_model {
615                "LAYER[-LAYER] or all"
616            } else {
617                "LAYER[-LAYER]"
618            };
619            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
620        })?;
621        let (first, last) = if layers == "all" {
622            if !allow_full_model {
623                return Err(format!(
624                    "{flag} does not support the full-model shorthand; assign routed layers \
625                     explicitly"
626                ));
627            }
628            (0, STEP37_TRUNK_LAYERS - 1)
629        } else {
630            match layers.split_once('-') {
631                Some((first, last)) => {
632                    let first = first
633                        .parse::<usize>()
634                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
635                    let last = last
636                        .parse::<usize>()
637                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
638                    if first > last {
639                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
640                    }
641                    if last - first + 1 > 128 {
642                        return Err(format!(
643                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
644                        ));
645                    }
646                    (first, last)
647                }
648                None => {
649                    let layer = layers
650                        .parse::<usize>()
651                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
652                    (layer, layer)
653                }
654            }
655        };
656        let devices = devices
657            .split(',')
658            .map(|device| {
659                device
660                    .parse::<usize>()
661                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
662            })
663            .collect::<Result<Vec<_>, _>>()?;
664        if !(2..=8).contains(&devices.len()) {
665            return Err(format!(
666                "{flag} requires 2..=8 devices, got {}",
667                devices.len()
668            ));
669        }
670        let mut unique = devices.clone();
671        unique.sort_unstable();
672        unique.dedup();
673        if unique.len() != devices.len() {
674            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
675        }
676        for layer in first..=last {
677            if specs
678                .iter()
679                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
680            {
681                return Err(format!("{flag} assigns layer {layer} more than once"));
682            }
683            specs.push(StepEpLayerSpec {
684                layer,
685                devices: devices.clone(),
686            });
687        }
688    }
689    Ok(specs)
690}
691
692pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
693    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
694}
695
696pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
697    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
698}
699
700pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
701    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
702}
703
704pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
705    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
706}
707
708#[derive(Clone, Copy)]
709pub struct E4m3BlockMatrix<'a> {
710    pub codes: &'a [u8],
711    pub scales: &'a [f32],
712    pub out_features: usize,
713    pub in_features: usize,
714}
715
716impl E4m3BlockMatrix<'_> {
717    fn validate(&self) -> Result<(), String> {
718        let code_count = self
719            .out_features
720            .checked_mul(self.in_features)
721            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
722        if self.codes.len() != code_count {
723            return Err(format!(
724                "E4M3 code count {} != {}x{} ({code_count})",
725                self.codes.len(),
726                self.out_features,
727                self.in_features,
728            ));
729        }
730        let scale_count =
731            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
732        if self.scales.len() != scale_count {
733            return Err(format!(
734                "E4M3 scale count {} != {scale_count} for {}x{}",
735                self.scales.len(),
736                self.out_features,
737                self.in_features,
738            ));
739        }
740        if !self
741            .scales
742            .iter()
743            .all(|scale| scale.is_finite() && *scale > 0.0)
744        {
745            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
746        }
747        Ok(())
748    }
749}
750
751#[derive(Clone, Copy)]
752pub struct E4m3ExpertBank<'a> {
753    pub codes: &'a [u8],
754    pub scales: &'a [f32],
755    pub expert_count: usize,
756    pub out_features: usize,
757    pub in_features: usize,
758}
759
760impl E4m3ExpertBank<'_> {
761    fn validate(&self) -> Result<(), String> {
762        if self.expert_count == 0 {
763            return Err("E4M3 expert bank is empty".to_string());
764        }
765        let code_stride = self
766            .out_features
767            .checked_mul(self.in_features)
768            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
769        let code_count = self
770            .expert_count
771            .checked_mul(code_stride)
772            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
773        if self.codes.len() != code_count {
774            return Err(format!(
775                "E4M3 expert code count {} != {}x{} ({code_count})",
776                self.codes.len(),
777                self.expert_count,
778                code_stride,
779            ));
780        }
781        let scale_stride =
782            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
783        let scale_count = self
784            .expert_count
785            .checked_mul(scale_stride)
786            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
787        if self.scales.len() != scale_count {
788            return Err(format!(
789                "E4M3 expert scale count {} != {}x{} ({scale_count})",
790                self.scales.len(),
791                self.expert_count,
792                scale_stride,
793            ));
794        }
795        if !self
796            .scales
797            .iter()
798            .all(|scale| scale.is_finite() && *scale > 0.0)
799        {
800            return Err(
801                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
802            );
803        }
804        Ok(())
805    }
806
807    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
808        if expert >= self.expert_count {
809            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
810        }
811        let code_stride = self.out_features * self.in_features;
812        let scale_stride =
813            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
814        Ok(E4m3BlockMatrix {
815            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
816            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
817            out_features: self.out_features,
818            in_features: self.in_features,
819        })
820    }
821}
822
823pub struct ColumnParallelResult {
824    pub gathered: Vec<f32>,
825    pub rank_outputs: Vec<Vec<f32>>,
826}
827
828pub struct RowParallelResult {
829    pub reduced: Vec<f32>,
830    pub rank_partials: Vec<Vec<f32>>,
831}
832
833#[derive(Clone, Copy)]
834pub struct Bf16Matrix<'a> {
835    pub bytes: &'a [u8],
836    pub out_features: usize,
837    pub in_features: usize,
838}
839
840impl Bf16Matrix<'_> {
841    pub fn validate(&self) -> Result<(), String> {
842        if self.out_features == 0 || self.in_features == 0 {
843            return Err("BF16 matrix dimensions must be nonzero".into());
844        }
845        let expected = self
846            .out_features
847            .checked_mul(self.in_features)
848            .and_then(|values| values.checked_mul(2))
849            .ok_or("BF16 matrix byte count overflow")?;
850        if self.bytes.len() != expected {
851            return Err(format!(
852                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
853                self.bytes.len(),
854                self.out_features,
855                self.in_features,
856            ));
857        }
858        Ok(())
859    }
860}
861
862struct ResidentE4m3Rank {
863    codes: CudaSlice<u8>,
864    scales: CudaSlice<f32>,
865    out_features: usize,
866    in_features: usize,
867}
868
869enum ResidentBf16Weight {
870    Bf16(CudaSlice<u8>),
871    F32(CudaSlice<f32>),
872}
873
874impl ResidentBf16Weight {
875    fn ordinal(&self) -> usize {
876        match self {
877            Self::Bf16(bytes) => bytes.ordinal(),
878            Self::F32(values) => values.ordinal(),
879        }
880    }
881}
882
883struct ResidentBf16Rank {
884    weight: ResidentBf16Weight,
885    out_features: usize,
886    in_features: usize,
887}
888
889pub struct ResidentColumnParallel {
890    ranks: Vec<ResidentE4m3Rank>,
891    out_features: usize,
892    in_features: usize,
893}
894
895pub struct ResidentRowParallel {
896    ranks: Vec<ResidentE4m3Rank>,
897    out_features: usize,
898    in_features: usize,
899}
900
901pub struct ResidentBf16ColumnParallel {
902    ranks: Vec<ResidentBf16Rank>,
903    out_features: usize,
904    in_features: usize,
905    canonical_chunk_rows: Option<usize>,
906}
907
908pub struct ResidentBf16RowParallel {
909    ranks: Vec<ResidentBf16Rank>,
910    out_features: usize,
911    in_features: usize,
912}
913
914pub struct ResidentStepBf16RowParallel {
915    ranks: Vec<Vec<ResidentBf16Rank>>,
916    out_features: usize,
917    in_features: usize,
918    canonical_chunk_cols: usize,
919}
920
921/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
922pub struct ResidentSigmoidTopKRouter {
923    weight: CudaSlice<f32>,
924    correction_bias: CudaSlice<f32>,
925    active: CudaSlice<u8>,
926    root_device: usize,
927    input_width: usize,
928    expert_count: usize,
929    experts_per_token: usize,
930    active_count: usize,
931    scaling_factor: f32,
932    route_norm: bool,
933}
934
935pub struct SigmoidTopKHostOutput {
936    pub logits: Vec<f32>,
937    pub selected: Vec<u32>,
938    pub weights: Vec<f32>,
939}
940
941/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
942pub struct ResidentReplicatedBf16SwiGlu {
943    gate: Vec<ResidentBf16Rank>,
944    up: Vec<ResidentBf16Rank>,
945    down: Vec<ResidentBf16Rank>,
946    input_width: usize,
947    intermediate_width: usize,
948}
949
950/// One token-major F32 batch replicated across a native-P2P rank group.
951///
952/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
953/// substrate between independently sharded operators; it carries no model or topology claim.
954pub struct ResidentReplicatedDeviceRows {
955    ranks: Vec<CudaSlice<f32>>,
956    tokens: usize,
957    width: usize,
958}
959
960impl ResidentReplicatedDeviceRows {
961    pub fn tokens(&self) -> usize {
962        self.tokens
963    }
964
965    pub fn width(&self) -> usize {
966        self.width
967    }
968
969    pub fn ranks(&self) -> usize {
970        self.ranks.len()
971    }
972}
973
974/// Canonical MoE output order: routed plus shared, then add the layer residual.
975pub fn moe_residual_host(
976    residual: &[f32],
977    routed: &[f32],
978    shared: &[f32],
979) -> Result<Vec<f32>, String> {
980    if residual.len() != routed.len() || residual.len() != shared.len() {
981        return Err(format!(
982            "MoE residual lengths residual={} routed={} shared={}",
983            residual.len(),
984            routed.len(),
985            shared.len()
986        ));
987    }
988    let ffn = routed
989        .iter()
990        .zip(shared)
991        .map(|(&routed, &shared)| routed + shared)
992        .collect::<Vec<_>>();
993    Ok(residual
994        .iter()
995        .zip(ffn)
996        .map(|(&residual, ffn)| residual + ffn)
997        .collect())
998}
999
1000pub use memra_kv::{
1001    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1002};
1003
1004/// Persistent TP2/TP4/TP8 routed-expert reference.
1005///
1006/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1007/// Activations and deterministic host-staged collectives remain per invocation. This is the
1008/// correctness substrate for serving TP/EP, not product-throughput evidence.
1009pub struct ResidentTpExpert {
1010    gate: ResidentColumnParallel,
1011    up: ResidentColumnParallel,
1012    down: ResidentRowParallel,
1013    input_width: usize,
1014    expert_width: usize,
1015}
1016
1017struct ResidentE4m3ExpertBankRank {
1018    codes: CudaSlice<u8>,
1019    scales: CudaSlice<f32>,
1020    expert_range: Range<usize>,
1021    out_features: usize,
1022    in_features: usize,
1023    code_stride: usize,
1024    scale_stride: usize,
1025    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1026    /// checkpoint's global block order exactly. Other banks remain row-major.
1027    k_blocks: Option<usize>,
1028}
1029
1030struct PackedE4m3ExpertBankRank {
1031    codes: Vec<u8>,
1032    scales: Vec<f32>,
1033    expert_range: Range<usize>,
1034    out_features: usize,
1035    in_features: usize,
1036    code_stride: usize,
1037    scale_stride: usize,
1038    k_blocks: Option<usize>,
1039}
1040
1041struct ResidentEpRank {
1042    gate: ResidentE4m3ExpertBankRank,
1043    up: ResidentE4m3ExpertBankRank,
1044    down: ResidentE4m3ExpertBankRank,
1045}
1046
1047/// Persistent expert-parallel reference.
1048///
1049/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1050/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1051/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1052/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1053pub struct ResidentExpertParallel {
1054    ranks: Vec<ResidentEpRank>,
1055    expert_count: usize,
1056    input_width: usize,
1057    expert_width: usize,
1058}
1059
1060/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1061///
1062/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1063/// outside this gate-only adapter.
1064pub struct StepGroupedFp8ProjectionOutput {
1065    pub gate: Vec<f32>,
1066    pub up: Vec<f32>,
1067    pub down: Vec<f32>,
1068}
1069
1070/// Prepared official Step grouped-FP8 projection gate.
1071///
1072/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1073/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1074pub struct PreparedStepGroupedFp8Gate {
1075    device: usize,
1076    gate: ResidentE4m3ExpertBankRank,
1077    up: ResidentE4m3ExpertBankRank,
1078    down: ResidentE4m3ExpertBankRank,
1079    input: CudaSlice<f32>,
1080    route_csr: DeviceExpertCsr,
1081    down_csr: DeviceExpertCsr,
1082    gate_workspace: Fp8GroupedWorkspace,
1083    up_workspace: Fp8GroupedWorkspace,
1084    down_workspace: Fp8GroupedWorkspace,
1085    activation: CudaSlice<f32>,
1086    activation_limit: Option<f32>,
1087    tokens: usize,
1088    pairs: usize,
1089}
1090
1091impl PreparedStepGroupedFp8Gate {
1092    pub fn tokens(&self) -> usize {
1093        self.tokens
1094    }
1095
1096    pub fn pairs(&self) -> usize {
1097        self.pairs
1098    }
1099}
1100
1101struct PreparedStepGroupedExpertOwner {
1102    rank: usize,
1103    global_pairs: Vec<usize>,
1104    route_csr: DeviceExpertCsr,
1105    down_csr: DeviceExpertCsr,
1106    gate_workspace: Fp8GroupedWorkspace,
1107    up_workspace: Fp8GroupedWorkspace,
1108    down_workspace: Fp8GroupedWorkspace,
1109    activation: CudaSlice<f32>,
1110}
1111
1112struct StepGroupedExpertOwnerSchedule {
1113    global_pairs: Vec<usize>,
1114    route_csr: ExpertCsr,
1115    down_csr: ExpertCsr,
1116}
1117
1118/// Prepared official Step expert-owner grouped-FP8 projection gate.
1119///
1120/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1121/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1122/// after every owner has completed its rank-local program.
1123pub struct PreparedStepGroupedExpertParallelGate {
1124    rank_inputs: Vec<CudaSlice<f32>>,
1125    owners: Vec<PreparedStepGroupedExpertOwner>,
1126    activation_limit: Option<f32>,
1127    tokens: usize,
1128    pairs: usize,
1129    max_tokens: usize,
1130    max_pairs: usize,
1131    input_width: usize,
1132    expert_width: usize,
1133    generation: u64,
1134    executed_generation: Option<u64>,
1135    ready: bool,
1136}
1137
1138impl PreparedStepGroupedExpertParallelGate {
1139    pub fn tokens(&self) -> usize {
1140        self.tokens
1141    }
1142
1143    pub fn pairs(&self) -> usize {
1144        self.pairs
1145    }
1146
1147    pub fn max_tokens(&self) -> usize {
1148        self.max_tokens
1149    }
1150
1151    pub fn input_width(&self) -> usize {
1152        self.input_width
1153    }
1154
1155    pub fn expert_width(&self) -> usize {
1156        self.expert_width
1157    }
1158
1159    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1160        validate_step_expert_activation_limit(limit)?;
1161        self.activation_limit = limit;
1162        self.executed_generation = None;
1163        Ok(())
1164    }
1165
1166    pub fn active_owners(&self) -> usize {
1167        self.owners
1168            .iter()
1169            .filter(|owner| !owner.global_pairs.is_empty())
1170            .count()
1171    }
1172
1173    pub fn owner_pair_counts(&self) -> Vec<usize> {
1174        self.owners
1175            .iter()
1176            .map(|owner| owner.global_pairs.len())
1177            .collect()
1178    }
1179
1180    pub fn generation(&self) -> u64 {
1181        self.generation
1182    }
1183}
1184
1185struct PreparedPeerWeightedRouteOwner {
1186    token_rows: CudaSlice<i32>,
1187    slots: CudaSlice<i32>,
1188    weights: CudaSlice<f32>,
1189    active_pairs: usize,
1190}
1191
1192/// Persistent root-side weighted combine for peer-owned canonical route rows.
1193///
1194/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1195/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1196/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1197pub struct PreparedPeerWeightedRouteCombine {
1198    root_device: usize,
1199    owners: Vec<PreparedPeerWeightedRouteOwner>,
1200    peer_staging: CudaSlice<f32>,
1201    slots: CudaSlice<f32>,
1202    weights: CudaSlice<f32>,
1203    output: CudaSlice<f32>,
1204    peer_devices: Vec<usize>,
1205    peer_outputs: Vec<CudaSlice<f32>>,
1206    width: usize,
1207    experts_per_token: usize,
1208    max_tokens: usize,
1209    max_pairs: usize,
1210    tokens: usize,
1211    pairs: usize,
1212    projection_generation: u64,
1213    output_generation: Option<u64>,
1214    broadcast_generation: Option<u64>,
1215    ready: bool,
1216}
1217
1218impl PreparedPeerWeightedRouteCombine {
1219    pub fn tokens(&self) -> usize {
1220        self.tokens
1221    }
1222
1223    pub fn pairs(&self) -> usize {
1224        self.pairs
1225    }
1226
1227    pub fn owner_pair_counts(&self) -> Vec<usize> {
1228        self.owners.iter().map(|owner| owner.active_pairs).collect()
1229    }
1230
1231    pub fn distributed_ranks(&self) -> usize {
1232        1 + self.peer_outputs.len()
1233    }
1234}
1235
1236struct ResidentTpExpertBank {
1237    gate: Vec<ResidentE4m3ExpertBankRank>,
1238    up: Vec<ResidentE4m3ExpertBankRank>,
1239    down: Vec<ResidentE4m3ExpertBankRank>,
1240    expert_count: usize,
1241    input_width: usize,
1242    expert_width: usize,
1243}
1244
1245/// Persistent tensor-parallel expert bank.
1246///
1247/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1248/// input-column shard of every down projection. Activations cross deterministic host-staged
1249/// collectives on hosts where native peer copies are unavailable or corrupt.
1250pub struct ResidentTensorParallel {
1251    bank: ResidentTpExpertBank,
1252}
1253
1254/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1255///
1256/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1257/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1258/// repeated performance evidence qualify it.
1259pub struct TpE4m3HostBounce {
1260    devices: Vec<usize>,
1261    ranks: Vec<Engine>,
1262    native_p2p: bool,
1263    ep_device_arithmetic: bool,
1264    bulk_p2p: bool,
1265    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1266    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1267    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1268}
1269
1270/// Persistent workspace of the v2 rank-local decode-attention driver.
1271///
1272/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1273/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1274/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1275/// before its consumers run in the same call; nothing carries state between tokens.
1276/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1277/// fused kernels read (F32 mirror or raw checkpoint bf16).
1278pub enum StepTpGateShards<'a> {
1279    F32(&'a [crate::CudaSlice<f32>]),
1280    Bf16(&'a [crate::CudaSlice<u8>]),
1281}
1282
1283pub struct StepTpDecodeV2Ws {
1284    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1285    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1286    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1287    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1288    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1289    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1290    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1291    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1292    pub(crate) tcol_cap: usize,
1293    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1294    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1295    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1296    tcol_gated: Vec<CudaSlice<f32>>,
1297    tcol_opart: Vec<CudaSlice<f32>>,
1298    tcol_opeer: Option<CudaSlice<f32>>,
1299    tcol_omix: Option<CudaSlice<f32>>,
1300    tcol_ocap: usize,
1301    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1302    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1303    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1304    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1305    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1306    pub(crate) q: Vec<CudaSlice<f32>>,
1307    pub(crate) k: Vec<CudaSlice<f32>>,
1308    pub(crate) pos: Vec<CudaSlice<i32>>,
1309    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1310    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1311    pub(crate) gate: Vec<CudaSlice<f32>>,
1312    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1313    pub(crate) gated: Vec<CudaSlice<f32>>,
1314    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1315    o_partials: Vec<Vec<CudaSlice<f32>>>,
1316    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1317    ev_rank: Vec<CudaEvent>,
1318    // root-context buffers
1319    peer_partial: CudaSlice<f32>,
1320    reduce_a: CudaSlice<f32>,
1321    reduce_b: CudaSlice<f32>,
1322    /// Never written; the canonical zero start of the v1 add chain.
1323    zeros: CudaSlice<f32>,
1324    pub(crate) k_shadow: CudaSlice<f32>,
1325    pub(crate) v_shadow: CudaSlice<f32>,
1326    ev_refresh: CudaEvent,
1327    ev_oproj: CudaEvent,
1328    // model-engine (e) context
1329    gate_e: CudaSlice<f32>,
1330    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1331    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1332    pub(crate) h_stage: Option<CudaSlice<f32>>,
1333    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1334    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1335    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1336    /// captured/raw address it uses must be layer-invariant).
1337    attn_in: Vec<CudaSlice<f32>>,
1338    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1339    raw_h_stage: u64,
1340    raw_pos_stage: u64,
1341    raw_attn_in: Vec<u64>,
1342    raw_pos: Vec<u64>,
1343    raw_o_partial1: u64,
1344    raw_peer_partial: u64,
1345    raw_k1: u64,
1346    raw_v1: u64,
1347    raw_k_shadow: u64,
1348    raw_v_shadow: u64,
1349    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1350    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1351    /// children read same-context memory (cross-context kernel args are capture-illegal).
1352    raw_mixed_stage_e: u64,
1353    raw_reduce_a: u64,
1354    raw_shadow_stage_e: (u64, u64),
1355    ev_entry: CudaEvent,
1356    e_device: usize,
1357    // geometry pins
1358    local_q_dim: usize,
1359    local_kv_dim: usize,
1360    heads: usize,
1361    pub(crate) o_out: usize,
1362    o_block_cols: usize,
1363    blocks_per_rank: usize,
1364}
1365
1366impl TpE4m3HostBounce {
1367    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1368        Self::new_inner(devices, false, false, false, false)
1369    }
1370
1371    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1372        Self::new_inner(devices, false, true, false, false)
1373    }
1374
1375    pub fn new_native_p2p_device_arithmetic(
1376        devices: &[usize],
1377    ) -> Result<Self, Box<dyn std::error::Error>> {
1378        Self::new_inner(devices, false, true, true, false)
1379    }
1380
1381    pub(crate) fn new_configured(
1382        devices: &[usize],
1383        native_p2p: bool,
1384        ep_device_arithmetic: bool,
1385        bulk_p2p: bool,
1386    ) -> Result<Self, Box<dyn std::error::Error>> {
1387        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1388    }
1389
1390    /// Single-rank execution of the canonical checkpoint-block TP program.
1391    ///
1392    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1393    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1394    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1395        Self::new_inner(&[device], true, false, false, false)
1396    }
1397
1398    fn new_inner(
1399        devices: &[usize],
1400        allow_single_rank: bool,
1401        native_p2p: bool,
1402        ep_device_arithmetic: bool,
1403        bulk_p2p: bool,
1404    ) -> Result<Self, Box<dyn std::error::Error>> {
1405        if ep_device_arithmetic && !native_p2p {
1406            return Err("device-resident EP arithmetic requires native P2P".into());
1407        }
1408        if bulk_p2p && !native_p2p {
1409            return Err("bulk TP transport requires native P2P".into());
1410        }
1411        let minimum = if allow_single_rank { 1 } else { 2 };
1412        if !(minimum..=8).contains(&devices.len()) {
1413            return Err(format!(
1414                "TP reference requires {minimum}..=8 devices, got {}",
1415                devices.len()
1416            )
1417            .into());
1418        }
1419        let mut unique = devices.to_vec();
1420        unique.sort_unstable();
1421        unique.dedup();
1422        if unique.len() != devices.len() {
1423            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1424        }
1425        let ranks = devices
1426            .iter()
1427            .map(|&device| Engine::new(device))
1428            .collect::<Result<Vec<_>, _>>()?;
1429        if native_p2p {
1430            configure_native_p2p(&ranks, devices)?;
1431        }
1432        if allow_single_rank {
1433            eprintln!(
1434                "[tp] canonical oracle transport=local device={} performance_claim=false",
1435                devices[0]
1436            );
1437        } else if native_p2p {
1438            if ep_device_arithmetic {
1439                eprintln!(
1440                    "[tp] correctness transport=native-p2p devices={devices:?} \
1441                     native_p2p=true activation=device-host-exact \
1442                     accumulation=device-host-exact output=root-readback \
1443                     bulk_p2p={bulk_p2p} performance_claim=false"
1444                );
1445            } else {
1446                eprintln!(
1447                    "[tp] correctness transport=native-p2p devices={devices:?} \
1448                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1449                     performance_claim=false"
1450                );
1451            }
1452        } else {
1453            eprintln!(
1454                "[tp] correctness transport=host-bounce devices={devices:?} \
1455                 native_p2p=false performance_claim=false"
1456            );
1457        }
1458        Ok(Self {
1459            devices: devices.to_vec(),
1460            ranks,
1461            native_p2p,
1462            ep_device_arithmetic,
1463            bulk_p2p,
1464            decode_v2: std::sync::Mutex::new(Vec::new()),
1465        })
1466    }
1467
1468    pub fn devices(&self) -> &[usize] {
1469        &self.devices
1470    }
1471
1472    pub fn native_p2p(&self) -> bool {
1473        self.native_p2p
1474    }
1475
1476    pub fn bulk_p2p(&self) -> bool {
1477        self.bulk_p2p
1478    }
1479
1480    pub fn expert_activation_label(&self) -> &'static str {
1481        if self.ep_device_arithmetic {
1482            "device-host-exact"
1483        } else {
1484            "host-canonical"
1485        }
1486    }
1487
1488    pub fn expert_accumulation_label(&self) -> &'static str {
1489        self.expert_activation_label()
1490    }
1491
1492    pub fn expert_output_label(&self) -> &'static str {
1493        if self.ep_device_arithmetic {
1494            "root-readback"
1495        } else {
1496            "host-accumulated"
1497        }
1498    }
1499
1500    pub fn transport_label(&self) -> &'static str {
1501        if self.devices.len() == 1 {
1502            "local"
1503        } else if self.native_p2p {
1504            "native-p2p"
1505        } else {
1506            "host-bounce"
1507        }
1508    }
1509
1510    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1511        self.ranks
1512            .iter()
1513            .map(|rank| rank.ctx().name().map_err(Into::into))
1514            .collect()
1515    }
1516
1517    /// Correctness-gate access to the engine that owns one TP rank.
1518    ///
1519    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1520    /// focused gates can prove that the rank-local projection outputs remain device-resident
1521    /// through the next ownership boundary before that boundary is wired into serving.
1522    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1523        self.ranks.get(rank)
1524    }
1525
1526    pub fn allocate_tp_kv_cache(
1527        &self,
1528        kv_dim_k: usize,
1529        kv_dim_v: usize,
1530        capacity: usize,
1531    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1532        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1533    }
1534
1535    pub fn allocate_tp_swa_kv_cache(
1536        &self,
1537        kv_dim_k: usize,
1538        kv_dim_v: usize,
1539        capacity: usize,
1540        window: usize,
1541    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1542        if window == 0 {
1543            return Err("TP SWA KV window must be nonzero".into());
1544        }
1545        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1546    }
1547
1548    fn allocate_tp_kv_cache_inner(
1549        &self,
1550        kv_dim_k: usize,
1551        kv_dim_v: usize,
1552        capacity: usize,
1553        window: Option<usize>,
1554    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1555        if capacity == 0 || capacity > i32::MAX as usize {
1556            return Err(
1557                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1558            );
1559        }
1560        let tp = self.ranks.len();
1561        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1562        let physical_rows = window
1563            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1564            .unwrap_or(capacity);
1565        let k_plane_bytes = physical_rows
1566            .checked_mul(shape.k_token_bytes)
1567            .and_then(|bytes| bytes.checked_add(8))
1568            .ok_or("TP KV K plane-byte overflow")?;
1569        let v_plane_bytes = physical_rows
1570            .checked_mul(shape.v_token_bytes)
1571            .and_then(|bytes| bytes.checked_add(8))
1572            .ok_or("TP KV V plane-byte overflow")?;
1573        let mut ranks = Vec::with_capacity(tp);
1574        for engine in &self.ranks {
1575            let _main = engine.gpu.enter_main()?;
1576            ranks.push(ResidentTpKvCacheRank::new(
1577                engine.alloc_u8(k_plane_bytes)?,
1578                engine.alloc_u8(v_plane_bytes)?,
1579                engine.htod_i32(&[0])?,
1580            ));
1581        }
1582        Ok(match window {
1583            Some(window) => ResidentTpKvCache::new_swa(
1584                ranks,
1585                shape.kv_dim_k,
1586                shape.kv_dim_v,
1587                shape.k_token_bytes,
1588                shape.v_token_bytes,
1589                capacity,
1590                window,
1591            ),
1592            None => ResidentTpKvCache::new(
1593                ranks,
1594                shape.kv_dim_k,
1595                shape.kv_dim_v,
1596                shape.k_token_bytes,
1597                shape.v_token_bytes,
1598                capacity,
1599            ),
1600        })
1601    }
1602
1603    pub fn grow_tp_kv_cache(
1604        &self,
1605        source: &ResidentTpKvCache,
1606        target_capacity: usize,
1607        rows: usize,
1608    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1609        self.validate_tp_kv_cache(source)?;
1610        let plan = source.prepare_grow(target_capacity, rows)?;
1611        let ranks = self.ranks.len();
1612        let global_k = source
1613            .kv_dim_k()
1614            .checked_mul(ranks)
1615            .ok_or("TP KV grow global K dimension overflow")?;
1616        let global_v = source
1617            .kv_dim_v()
1618            .checked_mul(ranks)
1619            .ok_or("TP KV grow global V dimension overflow")?;
1620        let mut target = match source.ring_window() {
1621            Some(window) => {
1622                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1623            }
1624            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1625        };
1626        self.validate_tp_kv_cache(&target)?;
1627
1628        for (rank, engine) in self.ranks.iter().enumerate() {
1629            let _main = engine.gpu.enter_main()?;
1630            let src = source
1631                .rank(rank)
1632                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1633            let dst = target
1634                .rank_mut(rank)
1635                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1636            if plan.k_bytes() > 0 {
1637                engine.copy_u8_range_into(
1638                    dst.k_mut(),
1639                    0,
1640                    src.k(),
1641                    plan.source_row() * source.k_tok_bytes(),
1642                    plan.k_bytes(),
1643                )?;
1644            }
1645            if plan.v_bytes() > 0 {
1646                engine.copy_u8_range_into(
1647                    dst.v_mut(),
1648                    0,
1649                    src.v(),
1650                    plan.source_row() * source.v_tok_bytes(),
1651                    plan.v_bytes(),
1652                )?;
1653            }
1654        }
1655        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1656
1657        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1658        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1659        for engine in &self.ranks {
1660            let _main = engine.gpu.enter_main()?;
1661            engine.stream().synchronize()?;
1662        }
1663        let physical_copy_rows = plan.copy_rows();
1664        target.publish_grow(plan)?;
1665        eprintln!(
1666            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1667             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1668             rank_streams_synchronized=true generation_preserved=true",
1669            rows,
1670            source.capacity(),
1671            target_capacity,
1672            ranks,
1673            physical_copy_rows,
1674            source.ring_window(),
1675        );
1676        Ok(target)
1677    }
1678
1679    pub fn hydrate_tp_kv_cache(
1680        &self,
1681        cache: &mut ResidentTpKvCache,
1682        rows: usize,
1683        k_rows: &[u8],
1684        v_rows: &[u8],
1685    ) -> Result<(), Box<dyn std::error::Error>> {
1686        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1687    }
1688
1689    pub fn hydrate_tp_kv_cache_from(
1690        &self,
1691        cache: &mut ResidentTpKvCache,
1692        logical_len: usize,
1693        resident_start: usize,
1694        k_rows: &[u8],
1695        v_rows: &[u8],
1696    ) -> Result<(), Box<dyn std::error::Error>> {
1697        self.validate_tp_kv_cache(cache)?;
1698        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1699            return Err(format!(
1700                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1701                cache.committed_len(),
1702                cache.staged_len()
1703            )
1704            .into());
1705        }
1706        if resident_start > logical_len || logical_len > cache.capacity() {
1707            return Err(format!(
1708                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1709                cache.capacity(),
1710            )
1711            .into());
1712        }
1713        let rows = logical_len - resident_start;
1714        if rows > cache.physical_capacity() {
1715            return Err(format!(
1716                "TP KV hydration rows {rows} exceed physical capacity {}",
1717                cache.physical_capacity()
1718            )
1719            .into());
1720        }
1721        for rank in 0..self.ranks.len() {
1722            let k_rank =
1723                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1724            let v_rank =
1725                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1726            let engine = &self.ranks[rank];
1727            let _main = engine.gpu.enter_main()?;
1728            let rank_cache = cache
1729                .rank_mut(rank)
1730                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1731            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1732            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1733        }
1734        cache.publish_hydration(logical_len, resident_start)?;
1735        Ok(())
1736    }
1737
1738    pub fn append_tp_kv_transaction(
1739        &self,
1740        cache: &mut ResidentTpKvCache,
1741        transaction: TpKvTransaction,
1742        k_shards: &[CudaSlice<f32>],
1743        v_shards: &[CudaSlice<f32>],
1744        rows: usize,
1745    ) -> Result<(), Box<dyn std::error::Error>> {
1746        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1747    }
1748
1749    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1750    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1751    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1752    /// which land the same value the in-stream inc produced).
1753    #[allow(clippy::too_many_arguments)]
1754    pub fn append_tp_kv_transaction_inner(
1755        &self,
1756        cache: &mut ResidentTpKvCache,
1757        transaction: TpKvTransaction,
1758        k_shards: &[CudaSlice<f32>],
1759        v_shards: &[CudaSlice<f32>],
1760        rows: usize,
1761        external_rank_appends: bool,
1762    ) -> Result<(), Box<dyn std::error::Error>> {
1763        self.validate_tp_kv_cache(cache)?;
1764        let plan = cache.prepare_append(transaction, rows)?;
1765        let target = plan.target();
1766        let expected_k = rows
1767            .checked_mul(cache.kv_dim_k())
1768            .ok_or("TP KV K append size overflow")?;
1769        let expected_v = rows
1770            .checked_mul(cache.kv_dim_v())
1771            .ok_or("TP KV V append size overflow")?;
1772        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1773        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1774        if !external_rank_appends
1775            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1776        {
1777            return Err(format!(
1778                "TP KV append shard counts k={} v={} != ranks {}",
1779                k_shards.len(),
1780                v_shards.len(),
1781                self.ranks.len()
1782            )
1783            .into());
1784        }
1785        let kv_dim_k = cache.kv_dim_k();
1786        let kv_dim_v = cache.kv_dim_v();
1787        let k_tok_bytes = cache.k_tok_bytes();
1788        let v_tok_bytes = cache.v_tok_bytes();
1789        if let Some(KvRingAppend::Rebase {
1790            src_row,
1791            keep_rows,
1792            new_base,
1793            ..
1794        }) = plan.ring_append()
1795        {
1796            for rank in 0..self.ranks.len() {
1797                let engine = &self.ranks[rank];
1798                let _main = engine.gpu.enter_main()?;
1799                let rank_cache = cache
1800                    .rank_mut(rank)
1801                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1802                if keep_rows > 0 {
1803                    let k_len = keep_rows
1804                        .checked_mul(k_tok_bytes)
1805                        .ok_or("TP KV K rebase-byte overflow")?;
1806                    let v_len = keep_rows
1807                        .checked_mul(v_tok_bytes)
1808                        .ok_or("TP KV V rebase-byte overflow")?;
1809                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
1810                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
1811                    engine.copy_u8_range_into(
1812                        &mut k_tmp,
1813                        0,
1814                        rank_cache.k(),
1815                        src_row * k_tok_bytes,
1816                        k_len,
1817                    )?;
1818                    engine.copy_u8_range_into(
1819                        &mut v_tmp,
1820                        0,
1821                        rank_cache.v(),
1822                        src_row * v_tok_bytes,
1823                        v_len,
1824                    )?;
1825                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
1826                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
1827                }
1828                // dcw base mirror (graph increment A): physical row 0 now holds logical
1829                // row `new_base`; armed device mirrors track it (rebases are rare host
1830                // events, so a host set here is the whole maintenance cost).
1831                if rank_cache.base_d().is_some() {
1832                    let value = new_base as i32;
1833                    let rank_cache = cache
1834                        .rank_mut(rank)
1835                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1836                    if let Some(base_d) = rank_cache.base_d_mut() {
1837                        engine.set_i32_one(base_d, value)?;
1838                    }
1839                }
1840            }
1841        }
1842        cache.publish_append_rebase(plan)?;
1843        let write_row = plan.write_row();
1844        for rank in 0..self.ranks.len() {
1845            if external_rank_appends {
1846                break;
1847            }
1848            let engine = &self.ranks[rank];
1849            let _main = engine.gpu.enter_main()?;
1850            if k_shards[rank].len() != expected_k
1851                || v_shards[rank].len() != expected_v
1852                || k_shards[rank].ordinal() != engine.ctx().ordinal()
1853                || v_shards[rank].ordinal() != engine.ctx().ordinal()
1854            {
1855                return Err(format!(
1856                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
1857                     != expected {expected_k}/{expected_v} on device {}",
1858                    k_shards[rank].len(),
1859                    k_shards[rank].ordinal(),
1860                    v_shards[rank].len(),
1861                    v_shards[rank].ordinal(),
1862                    engine.ctx().ordinal(),
1863                )
1864                .into());
1865            }
1866            let rank_cache = cache
1867                .rank_mut(rank)
1868                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1869            let (rank_k, rank_v) = rank_cache.planes_mut();
1870            engine.append_kv_quantized_rows(
1871                &k_shards[rank],
1872                &v_shards[rank],
1873                rank_k,
1874                rank_v,
1875                write_row,
1876                rows,
1877                kv_dim_k,
1878                kv_dim_v,
1879                k_tok_bytes,
1880                v_tok_bytes,
1881                Engine::kv_fp8_on(),
1882            )?;
1883        }
1884        if !external_rank_appends {
1885            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
1886            // here would race the merged per-rank append (it reads len_d for its write row).
1887            self.set_tp_kv_len_mirrors(cache, target)?;
1888        }
1889        cache.publish_append_plan(plan)?;
1890        Ok(())
1891    }
1892
1893    pub fn commit_tp_kv_transaction(
1894        &self,
1895        cache: &mut ResidentTpKvCache,
1896        transaction: TpKvTransaction,
1897        accepted_rows: usize,
1898    ) -> Result<(), Box<dyn std::error::Error>> {
1899        self.validate_tp_kv_cache(cache)?;
1900        let target = cache.commit_target(transaction, accepted_rows)?;
1901        self.set_tp_kv_len_mirrors(cache, target)?;
1902        cache.publish_finalize(transaction, target)?;
1903        Ok(())
1904    }
1905
1906    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
1907    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
1908    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
1909    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
1910    /// counter backward mid-token.
1911    pub fn commit_tp_kv_transaction_external(
1912        &self,
1913        cache: &mut ResidentTpKvCache,
1914        transaction: TpKvTransaction,
1915        accepted_rows: usize,
1916    ) -> Result<(), Box<dyn std::error::Error>> {
1917        self.validate_tp_kv_cache(cache)?;
1918        let target = cache.commit_target(transaction, accepted_rows)?;
1919        cache.publish_finalize(transaction, target)?;
1920        Ok(())
1921    }
1922
1923    pub fn rollback_tp_kv_transaction(
1924        &self,
1925        cache: &mut ResidentTpKvCache,
1926        transaction: TpKvTransaction,
1927    ) -> Result<(), Box<dyn std::error::Error>> {
1928        self.validate_tp_kv_cache(cache)?;
1929        cache.validate_transaction(transaction)?;
1930        let target = transaction.base_len();
1931        self.set_tp_kv_len_mirrors(cache, target)?;
1932        cache.publish_finalize(transaction, target)?;
1933        Ok(())
1934    }
1935
1936    pub fn tp_kv_device_lengths(
1937        &self,
1938        cache: &ResidentTpKvCache,
1939    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
1940        self.validate_tp_kv_cache(cache)?;
1941        let mut lengths = Vec::with_capacity(self.ranks.len());
1942        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
1943            let _main = engine.gpu.enter_main()?;
1944            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
1945        }
1946        Ok(lengths)
1947    }
1948
1949    fn set_tp_kv_len_mirrors(
1950        &self,
1951        cache: &mut ResidentTpKvCache,
1952        len: usize,
1953    ) -> Result<(), Box<dyn std::error::Error>> {
1954        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
1955        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
1956            let _main = engine.gpu.enter_main()?;
1957            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
1958        }
1959        Ok(())
1960    }
1961
1962    fn validate_tp_kv_cache(
1963        &self,
1964        cache: &ResidentTpKvCache,
1965    ) -> Result<(), Box<dyn std::error::Error>> {
1966        if cache.ranks_len() != self.ranks.len() {
1967            return Err(format!(
1968                "TP KV cache ranks {} != runtime ranks {}",
1969                cache.ranks_len(),
1970                self.ranks.len()
1971            )
1972            .into());
1973        }
1974        let expected_k = cache
1975            .physical_capacity()
1976            .checked_mul(cache.k_tok_bytes())
1977            .and_then(|bytes| bytes.checked_add(8))
1978            .ok_or("TP KV K plane validation overflow")?;
1979        let expected_v = cache
1980            .physical_capacity()
1981            .checked_mul(cache.v_tok_bytes())
1982            .and_then(|bytes| bytes.checked_add(8))
1983            .ok_or("TP KV V plane validation overflow")?;
1984        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
1985            let device = engine.ctx().ordinal();
1986            if rank_cache.k().len() != expected_k
1987                || rank_cache.v().len() != expected_v
1988                || rank_cache.len_d().len() != 1
1989                || rank_cache.k().ordinal() != device
1990                || rank_cache.v().ordinal() != device
1991                || rank_cache.len_d().ordinal() != device
1992            {
1993                return Err(format!(
1994                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
1995                )
1996                .into());
1997            }
1998        }
1999        Ok(())
2000    }
2001
2002    pub fn full(
2003        &self,
2004        matrix: E4m3BlockMatrix<'_>,
2005        activations: &[f32],
2006        tokens: usize,
2007    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2008        matrix.validate()?;
2009        validate_activations(activations, tokens, matrix.in_features)?;
2010        run_rank(&self.ranks[0], matrix, activations, tokens)
2011    }
2012
2013    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2014    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2015    /// output is host-gathered in rank order.
2016    pub fn column_parallel(
2017        &self,
2018        matrix: E4m3BlockMatrix<'_>,
2019        activations: &[f32],
2020        tokens: usize,
2021    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2022        matrix.validate()?;
2023        validate_activations(activations, tokens, matrix.in_features)?;
2024        let tp = self.ranks.len();
2025        if matrix.out_features % tp != 0 {
2026            return Err(format!(
2027                "column-parallel out_features {} is not divisible by TP={tp}",
2028                matrix.out_features
2029            )
2030            .into());
2031        }
2032        let local_out = matrix.out_features / tp;
2033        if local_out % FP8_BLOCK != 0 {
2034            return Err(format!(
2035                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2036                 E4M3 scale block"
2037            )
2038            .into());
2039        }
2040
2041        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2042        let mut rank_outputs = Vec::with_capacity(tp);
2043        for (rank_index, rank) in self.ranks.iter().enumerate() {
2044            let shard = column_shard(matrix, tp, rank_index)?;
2045            let output = run_rank(rank, shard, activations, tokens)?;
2046            let row_start = rank_index * local_out;
2047            for token in 0..tokens {
2048                gathered[token * matrix.out_features + row_start
2049                    ..token * matrix.out_features + row_start + local_out]
2050                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2051            }
2052            rank_outputs.push(output);
2053        }
2054        Ok(ColumnParallelResult {
2055            gathered,
2056            rank_outputs,
2057        })
2058    }
2059
2060    pub fn upload_column_parallel(
2061        &self,
2062        matrix: E4m3BlockMatrix<'_>,
2063    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2064        matrix.validate()?;
2065        let tp = self.ranks.len();
2066        validate_column_shape(matrix, tp)?;
2067        let mut ranks = Vec::with_capacity(tp);
2068        for (rank_index, engine) in self.ranks.iter().enumerate() {
2069            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2070        }
2071        Ok(ResidentColumnParallel {
2072            ranks,
2073            out_features: matrix.out_features,
2074            in_features: matrix.in_features,
2075        })
2076    }
2077
2078    pub fn column_parallel_resident(
2079        &self,
2080        matrix: &ResidentColumnParallel,
2081        activations: &[f32],
2082        tokens: usize,
2083    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2084        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2085        validate_activations(activations, tokens, matrix.in_features)?;
2086        let local_out = matrix.out_features / self.ranks.len();
2087        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2088        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2089        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2090            let output = run_resident_rank(engine, shard, activations, tokens)?;
2091            let row_start = rank_index * local_out;
2092            for token in 0..tokens {
2093                gathered[token * matrix.out_features + row_start
2094                    ..token * matrix.out_features + row_start + local_out]
2095                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2096            }
2097            rank_outputs.push(output);
2098        }
2099        Ok(ColumnParallelResult {
2100            gathered,
2101            rank_outputs,
2102        })
2103    }
2104
2105    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2106    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2107    /// rank order.
2108    pub fn row_parallel(
2109        &self,
2110        matrix: E4m3BlockMatrix<'_>,
2111        activations: &[f32],
2112        tokens: usize,
2113    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2114        matrix.validate()?;
2115        validate_activations(activations, tokens, matrix.in_features)?;
2116        let tp = self.ranks.len();
2117        if matrix.in_features % tp != 0 {
2118            return Err(format!(
2119                "row-parallel in_features {} is not divisible by TP={tp}",
2120                matrix.in_features
2121            )
2122            .into());
2123        }
2124        let local_in = matrix.in_features / tp;
2125        if local_in % FP8_BLOCK != 0 {
2126            return Err(format!(
2127                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2128                 E4M3 scale block"
2129            )
2130            .into());
2131        }
2132
2133        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2134        let mut rank_partials = Vec::with_capacity(tp);
2135        for (rank_index, rank) in self.ranks.iter().enumerate() {
2136            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2137            let local_activations =
2138                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2139            let shard = E4m3BlockMatrix {
2140                codes: &codes,
2141                scales: &scales,
2142                out_features: matrix.out_features,
2143                in_features: local_in,
2144            };
2145            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2146            for (sum, value) in reduced.iter_mut().zip(&partial) {
2147                *sum += *value;
2148            }
2149            rank_partials.push(partial);
2150        }
2151        Ok(RowParallelResult {
2152            reduced,
2153            rank_partials,
2154        })
2155    }
2156
2157    pub fn upload_row_parallel(
2158        &self,
2159        matrix: E4m3BlockMatrix<'_>,
2160    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2161        matrix.validate()?;
2162        let tp = self.ranks.len();
2163        validate_row_shape(matrix, tp)?;
2164        let local_in = matrix.in_features / tp;
2165        let mut ranks = Vec::with_capacity(tp);
2166        for (rank_index, engine) in self.ranks.iter().enumerate() {
2167            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2168            ranks.push(upload_rank(
2169                engine,
2170                E4m3BlockMatrix {
2171                    codes: &codes,
2172                    scales: &scales,
2173                    out_features: matrix.out_features,
2174                    in_features: local_in,
2175                },
2176            )?);
2177        }
2178        Ok(ResidentRowParallel {
2179            ranks,
2180            out_features: matrix.out_features,
2181            in_features: matrix.in_features,
2182        })
2183    }
2184
2185    pub fn row_parallel_resident(
2186        &self,
2187        matrix: &ResidentRowParallel,
2188        activations: &[f32],
2189        tokens: usize,
2190    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2191        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2192        validate_activations(activations, tokens, matrix.in_features)?;
2193        let tp = self.ranks.len();
2194        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2195        let mut rank_partials = Vec::with_capacity(tp);
2196        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2197            let local_activations =
2198                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2199            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2200            for (sum, value) in reduced.iter_mut().zip(&partial) {
2201                *sum += *value;
2202            }
2203            rank_partials.push(partial);
2204        }
2205        Ok(RowParallelResult {
2206            reduced,
2207            rank_partials,
2208        })
2209    }
2210
2211    pub fn upload_bf16_column_parallel(
2212        &self,
2213        matrix: Bf16Matrix<'_>,
2214    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2215        self.upload_bf16_column_parallel_inner(matrix, None, false)
2216    }
2217
2218    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2219    pub fn upload_step_bf16_column_parallel(
2220        &self,
2221        matrix: Bf16Matrix<'_>,
2222    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2223        self.upload_step_bf16_column_parallel_inner(matrix, false)
2224    }
2225
2226    /// Load-time exact F32 expansion of a Step BF16 shard.
2227    ///
2228    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2229    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2230    pub fn upload_step_bf16_column_parallel_f32_mirror(
2231        &self,
2232        matrix: Bf16Matrix<'_>,
2233    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2234        self.upload_step_bf16_column_parallel_inner(matrix, true)
2235    }
2236
2237    fn upload_step_bf16_column_parallel_inner(
2238        &self,
2239        matrix: Bf16Matrix<'_>,
2240        f32_mirror: bool,
2241    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2242        let canonical_chunk_rows =
2243            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2244        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2245    }
2246
2247    fn upload_bf16_column_parallel_inner(
2248        &self,
2249        matrix: Bf16Matrix<'_>,
2250        canonical_chunk_rows: Option<usize>,
2251        f32_mirror: bool,
2252    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2253        matrix.validate()?;
2254        let tp = self.ranks.len();
2255        if matrix.out_features % tp != 0 {
2256            return Err(format!(
2257                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2258                matrix.out_features
2259            )
2260            .into());
2261        }
2262        let mut ranks = Vec::with_capacity(tp);
2263        for (rank, engine) in self.ranks.iter().enumerate() {
2264            ranks.push(upload_bf16_rank(
2265                engine,
2266                bf16_column_shard(matrix, tp, rank)?,
2267                f32_mirror,
2268            )?);
2269        }
2270        Ok(ResidentBf16ColumnParallel {
2271            ranks,
2272            out_features: matrix.out_features,
2273            in_features: matrix.in_features,
2274            canonical_chunk_rows,
2275        })
2276    }
2277
2278    pub fn bf16_column_parallel_resident(
2279        &self,
2280        matrix: &ResidentBf16ColumnParallel,
2281        activations: &[f32],
2282        tokens: usize,
2283    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2284        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2285        validate_activations(activations, tokens, matrix.in_features)?;
2286        let local_out = matrix.out_features / self.ranks.len();
2287        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2288        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2289        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2290            let output = run_resident_bf16_rank(
2291                engine,
2292                shard,
2293                activations,
2294                tokens,
2295                matrix.canonical_chunk_rows,
2296            )?;
2297            for token in 0..tokens {
2298                let src = &output[token * local_out..(token + 1) * local_out];
2299                let dst_start = token * matrix.out_features + rank * local_out;
2300                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2301            }
2302            rank_outputs.push(output);
2303        }
2304        Ok(ColumnParallelResult {
2305            gathered,
2306            rank_outputs,
2307        })
2308    }
2309
2310    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2311    ///
2312    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2313    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2314    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2315    /// attention and KV ownership are separate milestones.
2316    pub fn bf16_column_parallel_resident_native(
2317        &self,
2318        matrix: &ResidentBf16ColumnParallel,
2319        activations: &[f32],
2320        tokens: usize,
2321    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2322        let rank_outputs =
2323            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2324        let local_out = matrix.out_features / self.ranks.len();
2325        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2326    }
2327
2328    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2329    ///
2330    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2331    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2332    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2333    /// and cache ownership; callers must not treat its existence as serving qualification.
2334    pub fn bf16_column_parallel_resident_device_shards(
2335        &self,
2336        matrix: &ResidentBf16ColumnParallel,
2337        activations: &[f32],
2338        tokens: usize,
2339    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2340        if self.ranks.len() > 1 && !self.native_p2p {
2341            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2342        }
2343        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2344        validate_activations(activations, tokens, matrix.in_features)?;
2345
2346        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2347        let root_input = {
2348            let root = &self.ranks[0];
2349            let _main = root.gpu.enter_main()?;
2350            root.htod(activations)?
2351        };
2352        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2353        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2354        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2355        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2356        // `upload_replicated_device_rows`.
2357        {
2358            let root = &self.ranks[0];
2359            let _main = root.gpu.enter_main()?;
2360            root.stream().synchronize()?;
2361        }
2362        rank_inputs.push(root_input);
2363        for engine in &self.ranks[1..] {
2364            let peer_input = {
2365                let _main = engine.gpu.enter_main()?;
2366                let mut peer_input = engine.uninit(activations.len())?;
2367                engine
2368                    .stream()
2369                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2370                peer_input
2371            };
2372            rank_inputs.push(peer_input);
2373        }
2374
2375        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2376        for rank in 0..self.ranks.len() {
2377            rank_outputs.push(run_resident_bf16_rank_device(
2378                &self.ranks[rank],
2379                &matrix.ranks[rank],
2380                &rank_inputs[rank],
2381                tokens,
2382                matrix.canonical_chunk_rows,
2383                self.bulk_p2p,
2384            )?);
2385        }
2386        Ok(rank_outputs)
2387    }
2388
2389    /// Allocate one fixed-shape replicated batch without initializing its contents.
2390    ///
2391    /// Callers must refresh every rank before passing the batch to an operator.
2392    pub fn allocate_replicated_device_rows(
2393        &self,
2394        tokens: usize,
2395        width: usize,
2396    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2397        if self.ranks.len() > 1 && !self.native_p2p {
2398            return Err("replicated device rows require native P2P ranks".into());
2399        }
2400        let values = tokens
2401            .checked_mul(width)
2402            .ok_or("replicated device row size overflow")?;
2403        let rank_lengths = vec![values; self.ranks.len()];
2404        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2405        let mut ranks = Vec::with_capacity(self.ranks.len());
2406        for engine in &self.ranks {
2407            let _main = engine.gpu.enter_main()?;
2408            ranks.push(engine.uninit(values)?);
2409        }
2410        Ok(ResidentReplicatedDeviceRows {
2411            ranks,
2412            tokens,
2413            width,
2414        })
2415    }
2416
2417    /// Replace a fixed-shape replicated batch from a root-device source.
2418    pub fn refresh_replicated_device_rows_from_root(
2419        &self,
2420        rows: &mut ResidentReplicatedDeviceRows,
2421        source: &CudaSlice<f32>,
2422    ) -> Result<(), Box<dyn std::error::Error>> {
2423        if self.ranks.len() > 1 && !self.native_p2p {
2424            return Err("replicated device rows require native P2P ranks".into());
2425        }
2426        validate_replicated_device_rows(&self.ranks, rows)?;
2427        let root = self
2428            .ranks
2429            .first()
2430            .ok_or("replicated rows have no root rank")?;
2431        let values = replicated_device_row_source_values(
2432            rows.tokens,
2433            rows.width,
2434            source.len(),
2435            source.ordinal(),
2436            root.ctx().ordinal(),
2437        )?;
2438        let (root_rows, peer_rows) = rows
2439            .ranks
2440            .split_first_mut()
2441            .ok_or("replicated rows have no root allocation")?;
2442        {
2443            let _main = root.gpu.enter_main()?;
2444            let mut destination = root_rows.slice_mut(0..values);
2445            root.stream()
2446                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2447            root.stream().synchronize()?;
2448        }
2449        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2450            let _main = engine.gpu.enter_main()?;
2451            let mut destination = peer_rows.slice_mut(0..values);
2452            engine
2453                .stream()
2454                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2455        }
2456        Ok(())
2457    }
2458
2459    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2460    pub fn upload_replicated_device_rows(
2461        &self,
2462        rows: &[f32],
2463        tokens: usize,
2464        width: usize,
2465    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2466        if self.ranks.len() > 1 && !self.native_p2p {
2467            return Err("replicated device rows require native P2P ranks".into());
2468        }
2469        validate_activations(rows, tokens, width)?;
2470        let root = self
2471            .ranks
2472            .first()
2473            .ok_or("replicated rows have no root rank")?;
2474        let root_rows = {
2475            let _main = root.gpu.enter_main()?;
2476            root.htod(rows)?
2477        };
2478        {
2479            let _main = root.gpu.enter_main()?;
2480            root.stream().synchronize()?;
2481        }
2482        let mut ranks = Vec::with_capacity(self.ranks.len());
2483        ranks.push(root_rows);
2484        for engine in self.ranks.iter().skip(1) {
2485            let _main = engine.gpu.enter_main()?;
2486            let mut peer_rows = engine.uninit(rows.len())?;
2487            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2488            ranks.push(peer_rows);
2489        }
2490        Ok(ResidentReplicatedDeviceRows {
2491            ranks,
2492            tokens,
2493            width,
2494        })
2495    }
2496
2497    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2498    pub fn bf16_column_parallel_resident_replicated_device_shards(
2499        &self,
2500        matrix: &ResidentBf16ColumnParallel,
2501        activations: &ResidentReplicatedDeviceRows,
2502    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2503        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2504        validate_replicated_device_rows(&self.ranks, activations)?;
2505        if activations.width != matrix.in_features {
2506            return Err(format!(
2507                "replicated BF16 column input width {} != matrix width {}",
2508                activations.width, matrix.in_features
2509            )
2510            .into());
2511        }
2512        let mut outputs = Vec::with_capacity(self.ranks.len());
2513        for rank in 0..self.ranks.len() {
2514            outputs.push(run_resident_bf16_rank_device(
2515                &self.ranks[rank],
2516                &matrix.ranks[rank],
2517                &activations.ranks[rank],
2518                activations.tokens,
2519                matrix.canonical_chunk_rows,
2520                self.bulk_p2p,
2521            )?);
2522        }
2523        Ok(outputs)
2524    }
2525
2526    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2527    #[allow(clippy::too_many_arguments)]
2528    pub fn upload_sigmoid_topk_router(
2529        &self,
2530        weight: Bf16Matrix<'_>,
2531        correction_bias: &[f32],
2532        active: Option<&[bool]>,
2533        experts_per_token: usize,
2534        scaling_factor: f32,
2535        route_norm: bool,
2536    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2537        weight.validate()?;
2538        if correction_bias.len() != weight.out_features
2539            || experts_per_token == 0
2540            || experts_per_token > weight.out_features
2541            || !correction_bias.iter().all(|value| value.is_finite())
2542            || !scaling_factor.is_finite()
2543            || scaling_factor <= 0.0
2544        {
2545            return Err(format!(
2546                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2547                weight.out_features,
2548                weight.in_features,
2549                correction_bias.len(),
2550                experts_per_token,
2551            )
2552            .into());
2553        }
2554        let active_row = active
2555            .map(|mask| {
2556                if mask.len() != weight.out_features {
2557                    return Err(format!(
2558                        "sigmoid router active mask {} != experts {}",
2559                        mask.len(),
2560                        weight.out_features
2561                    ));
2562                }
2563                Ok(mask
2564                    .iter()
2565                    .map(|&enabled| u8::from(enabled))
2566                    .collect::<Vec<_>>())
2567            })
2568            .transpose()?
2569            .unwrap_or_else(|| vec![1; weight.out_features]);
2570        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2571        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2572
2573        let root = self
2574            .ranks
2575            .first()
2576            .ok_or("sigmoid router runtime has no root rank")?;
2577        let _main = root.gpu.enter_main()?;
2578        let bf16 = root.htod_bytes(weight.bytes)?;
2579        let weight_f32 = root.bf16_to_f32(
2580            &bf16.slice(0..bf16.len()),
2581            weight.out_features * weight.in_features,
2582        )?;
2583        Ok(ResidentSigmoidTopKRouter {
2584            weight: weight_f32,
2585            correction_bias: root.htod(correction_bias)?,
2586            active: root.htod_bytes(&active_row)?,
2587            root_device: root.ctx().ordinal(),
2588            input_width: weight.in_features,
2589            expert_count: weight.out_features,
2590            experts_per_token,
2591            active_count,
2592            scaling_factor,
2593            route_norm,
2594        })
2595    }
2596
2597    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2598    ///
2599    /// The logits readback exists for independent oracle comparison. This method is a correctness
2600    /// surface; a serving scheduler may retain logits and selected routes on device.
2601    pub fn sigmoid_topk_replicated_device_rows_host(
2602        &self,
2603        router: &ResidentSigmoidTopKRouter,
2604        input: &ResidentReplicatedDeviceRows,
2605    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2606        validate_replicated_device_rows(&self.ranks, input)?;
2607        if input.width != router.input_width {
2608            return Err(format!(
2609                "sigmoid router input width {} != resident width {}",
2610                input.width, router.input_width
2611            )
2612            .into());
2613        }
2614        let root = self
2615            .ranks
2616            .first()
2617            .ok_or("sigmoid router runtime has no root rank")?;
2618        let _main = root.gpu.enter_main()?;
2619        if root.ctx().ordinal() != router.root_device
2620            || router.weight.ordinal() != router.root_device
2621            || router.correction_bias.ordinal() != router.root_device
2622            || router.active.ordinal() != router.root_device
2623        {
2624            return Err("sigmoid router root residency changed".into());
2625        }
2626        let logits = root.router_gemv(
2627            &router.weight,
2628            &input.ranks[0],
2629            router.input_width,
2630            router.expert_count,
2631            input.tokens,
2632        )?;
2633        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2634            &logits,
2635            input.tokens,
2636            router.expert_count,
2637            router.experts_per_token,
2638            router.active_count,
2639            &router.correction_bias,
2640            &router.active,
2641            router.scaling_factor,
2642            router.route_norm,
2643        )?;
2644        Ok(SigmoidTopKHostOutput {
2645            logits: root.dtoh(&logits)?,
2646            selected,
2647            weights,
2648        })
2649    }
2650
2651    /// Replicate a full BF16 SwiGLU bank on every rank.
2652    pub fn upload_replicated_bf16_swiglu(
2653        &self,
2654        gate: Bf16Matrix<'_>,
2655        up: Bf16Matrix<'_>,
2656        down: Bf16Matrix<'_>,
2657    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2658        gate.validate()?;
2659        up.validate()?;
2660        down.validate()?;
2661        if gate.in_features != up.in_features
2662            || gate.out_features != up.out_features
2663            || down.in_features != gate.out_features
2664            || down.out_features != gate.in_features
2665        {
2666            return Err(format!(
2667                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2668                gate.out_features,
2669                gate.in_features,
2670                up.out_features,
2671                up.in_features,
2672                down.out_features,
2673                down.in_features,
2674            )
2675            .into());
2676        }
2677        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2678        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2679        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2680        for engine in &self.ranks {
2681            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2682            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2683            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2684        }
2685        Ok(ResidentReplicatedBf16SwiGlu {
2686            gate: gate_ranks,
2687            up: up_ranks,
2688            down: down_ranks,
2689            input_width: gate.in_features,
2690            intermediate_width: gate.out_features,
2691        })
2692    }
2693
2694    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2695    pub fn replicated_bf16_swiglu_resident_device(
2696        &self,
2697        mlp: &ResidentReplicatedBf16SwiGlu,
2698        input: &ResidentReplicatedDeviceRows,
2699        activation_limit: Option<f32>,
2700    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2701        validate_step_expert_activation_limit(activation_limit)?;
2702        validate_replicated_device_rows(&self.ranks, input)?;
2703        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
2704        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
2705        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
2706        if input.width != mlp.input_width
2707            || mlp.gate.len() != self.ranks.len()
2708            || mlp.up.len() != self.ranks.len()
2709            || mlp.down.len() != self.ranks.len()
2710        {
2711            return Err("replicated BF16 SwiGLU residency or input width changed".into());
2712        }
2713
2714        let mut outputs = Vec::with_capacity(self.ranks.len());
2715        for rank in 0..self.ranks.len() {
2716            let engine = &self.ranks[rank];
2717            let gate = run_resident_bf16_rank_device(
2718                engine,
2719                &mlp.gate[rank],
2720                &input.ranks[rank],
2721                input.tokens,
2722                None,
2723                self.bulk_p2p,
2724            )?;
2725            let up = run_resident_bf16_rank_device(
2726                engine,
2727                &mlp.up[rank],
2728                &input.ranks[rank],
2729                input.tokens,
2730                None,
2731                self.bulk_p2p,
2732            )?;
2733            let _main = engine.gpu.enter_main()?;
2734            let values = input
2735                .tokens
2736                .checked_mul(mlp.intermediate_width)
2737                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
2738            let mut activation = engine.uninit(values)?;
2739            if let Some(limit) = activation_limit {
2740                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
2741            } else {
2742                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
2743            }
2744            outputs.push(run_resident_bf16_rank_device(
2745                engine,
2746                &mlp.down[rank],
2747                &activation,
2748                input.tokens,
2749                None,
2750                self.bulk_p2p,
2751            )?);
2752        }
2753        Ok(ResidentReplicatedDeviceRows {
2754            ranks: outputs,
2755            tokens: input.tokens,
2756            width: mlp.input_width,
2757        })
2758    }
2759
2760    /// Apply the same RMS-norm row program independently on every replicated rank.
2761    pub fn rms_norm_replicated_device_rows(
2762        &self,
2763        input: &ResidentReplicatedDeviceRows,
2764        weight: &[f32],
2765        eps: f32,
2766    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2767        validate_replicated_device_rows(&self.ranks, input)?;
2768        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
2769            return Err(format!(
2770                "replicated RMS norm weight/eps {}/{} != width {}",
2771                weight.len(),
2772                eps,
2773                input.width
2774            )
2775            .into());
2776        }
2777        let mut ranks = Vec::with_capacity(self.ranks.len());
2778        for (rank, engine) in self.ranks.iter().enumerate() {
2779            let _main = engine.gpu.enter_main()?;
2780            let weight = engine.htod(weight)?;
2781            let mut output = engine.uninit(input.tokens * input.width)?;
2782            engine.rms_norm(
2783                &input.ranks[rank],
2784                &weight,
2785                &mut output,
2786                input.width,
2787                input.tokens,
2788                eps,
2789            )?;
2790            ranks.push(output);
2791        }
2792        Ok(ResidentReplicatedDeviceRows {
2793            ranks,
2794            tokens: input.tokens,
2795            width: input.width,
2796        })
2797    }
2798
2799    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
2800    pub fn add_rms_norm_replicated_device_rows(
2801        &self,
2802        input: &ResidentReplicatedDeviceRows,
2803        update: &ResidentReplicatedDeviceRows,
2804        weight: &[f32],
2805        eps: f32,
2806    ) -> Result<
2807        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
2808        Box<dyn std::error::Error>,
2809    > {
2810        validate_replicated_device_rows(&self.ranks, input)?;
2811        validate_replicated_device_rows(&self.ranks, update)?;
2812        if input.tokens != update.tokens
2813            || input.width != update.width
2814            || weight.len() != input.width
2815            || !eps.is_finite()
2816            || eps <= 0.0
2817        {
2818            return Err(format!(
2819                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
2820                input.tokens,
2821                input.width,
2822                update.tokens,
2823                update.width,
2824                weight.len(),
2825            )
2826            .into());
2827        }
2828        let values = input.tokens * input.width;
2829        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
2830        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
2831        for (rank, engine) in self.ranks.iter().enumerate() {
2832            let _main = engine.gpu.enter_main()?;
2833            let weight = engine.htod(weight)?;
2834            let mut residual = engine.uninit(values)?;
2835            let mut normalized = engine.uninit(values)?;
2836            engine.add_rms_norm(
2837                &input.ranks[rank],
2838                &update.ranks[rank],
2839                &weight,
2840                &mut residual,
2841                &mut normalized,
2842                input.width,
2843                input.tokens,
2844                eps,
2845            )?;
2846            residual_ranks.push(residual);
2847            normalized_ranks.push(normalized);
2848        }
2849        Ok((
2850            ResidentReplicatedDeviceRows {
2851                ranks: residual_ranks,
2852                tokens: input.tokens,
2853                width: input.width,
2854            },
2855            ResidentReplicatedDeviceRows {
2856                ranks: normalized_ranks,
2857                tokens: input.tokens,
2858                width: input.width,
2859            },
2860        ))
2861    }
2862
2863    pub fn collect_replicated_device_rows(
2864        &self,
2865        rows: &ResidentReplicatedDeviceRows,
2866    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
2867        validate_replicated_device_rows(&self.ranks, rows)?;
2868        let mut outputs = Vec::with_capacity(self.ranks.len());
2869        for (rank, engine) in self.ranks.iter().enumerate() {
2870            let _main = engine.gpu.enter_main()?;
2871            outputs.push(engine.dtoh(&rows.ranks[rank])?);
2872        }
2873        Ok(outputs)
2874    }
2875
2876    pub fn upload_bf16_row_parallel(
2877        &self,
2878        matrix: Bf16Matrix<'_>,
2879    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
2880        matrix.validate()?;
2881        let tp = self.ranks.len();
2882        if matrix.in_features % tp != 0 {
2883            return Err(format!(
2884                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
2885                matrix.in_features
2886            )
2887            .into());
2888        }
2889        let mut ranks = Vec::with_capacity(tp);
2890        for (rank, engine) in self.ranks.iter().enumerate() {
2891            let shard = bf16_row_shard(matrix, tp, rank)?;
2892            ranks.push(upload_bf16_rank(
2893                engine,
2894                Bf16Matrix {
2895                    bytes: &shard,
2896                    out_features: matrix.out_features,
2897                    in_features: matrix.in_features / tp,
2898                },
2899                false,
2900            )?);
2901        }
2902        Ok(ResidentBf16RowParallel {
2903            ranks,
2904            out_features: matrix.out_features,
2905            in_features: matrix.in_features,
2906        })
2907    }
2908
2909    pub fn bf16_row_parallel_resident(
2910        &self,
2911        matrix: &ResidentBf16RowParallel,
2912        activations: &[f32],
2913        tokens: usize,
2914    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2915        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2916        validate_activations(activations, tokens, matrix.in_features)?;
2917        let tp = self.ranks.len();
2918        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2919        let mut rank_partials = Vec::with_capacity(tp);
2920        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2921            let local_activations =
2922                activation_shard(activations, tokens, matrix.in_features, tp, rank);
2923            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
2924            for (sum, value) in reduced.iter_mut().zip(&partial) {
2925                *sum += value;
2926            }
2927            rank_partials.push(partial);
2928        }
2929        Ok(RowParallelResult {
2930            reduced,
2931            rank_partials,
2932        })
2933    }
2934
2935    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
2936    pub fn upload_step_bf16_row_parallel(
2937        &self,
2938        matrix: Bf16Matrix<'_>,
2939    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2940        self.upload_step_bf16_row_parallel_inner(matrix, false)
2941    }
2942
2943    pub fn upload_step_bf16_row_parallel_f32_mirror(
2944        &self,
2945        matrix: Bf16Matrix<'_>,
2946    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2947        self.upload_step_bf16_row_parallel_inner(matrix, true)
2948    }
2949
2950    fn upload_step_bf16_row_parallel_inner(
2951        &self,
2952        matrix: Bf16Matrix<'_>,
2953        f32_mirror: bool,
2954    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
2955        matrix.validate()?;
2956        let tp = self.ranks.len();
2957        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
2958        let local_in = matrix.in_features / tp;
2959        let blocks_per_rank = local_in / canonical_chunk_cols;
2960        let mut ranks = Vec::with_capacity(tp);
2961        for (rank, engine) in self.ranks.iter().enumerate() {
2962            let mut blocks = Vec::with_capacity(blocks_per_rank);
2963            for block in 0..blocks_per_rank {
2964                let global_block = rank * blocks_per_rank + block;
2965                let col_start = global_block * canonical_chunk_cols;
2966                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
2967                blocks.push(upload_bf16_rank(
2968                    engine,
2969                    Bf16Matrix {
2970                        bytes: &bytes,
2971                        out_features: matrix.out_features,
2972                        in_features: canonical_chunk_cols,
2973                    },
2974                    f32_mirror,
2975                )?);
2976            }
2977            ranks.push(blocks);
2978        }
2979        Ok(ResidentStepBf16RowParallel {
2980            ranks,
2981            out_features: matrix.out_features,
2982            in_features: matrix.in_features,
2983            canonical_chunk_cols,
2984        })
2985    }
2986
2987    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
2988    ///
2989    /// Block inputs and partials cross host memory, but every partial is added on the root device
2990    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
2991    pub fn step_bf16_row_parallel_resident(
2992        &self,
2993        matrix: &ResidentStepBf16RowParallel,
2994        activations: &[f32],
2995        tokens: usize,
2996    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2997        validate_step_bf16_row_residency(&self.ranks, matrix)?;
2998        validate_activations(activations, tokens, matrix.in_features)?;
2999        let root = &self.ranks[0];
3000        let output_len = tokens
3001            .checked_mul(matrix.out_features)
3002            .ok_or("Step BF16 row output size overflow")?;
3003        let mut reduced = {
3004            let _main = root.gpu.enter_main()?;
3005            root.htod(&vec![0.0f32; output_len])?
3006        };
3007        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3008        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3009            for (block, resident) in blocks.iter().enumerate() {
3010                let global_block = rank * blocks_per_rank + block;
3011                let input = activation_shard(
3012                    activations,
3013                    tokens,
3014                    matrix.in_features,
3015                    PRODUCT_MAX_CARDS,
3016                    global_block,
3017                );
3018                let partial =
3019                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
3020                let next = {
3021                    let _main = root.gpu.enter_main()?;
3022                    let partial = root.htod(&partial)?;
3023                    let mut next = root.uninit(output_len)?;
3024                    root.add(&reduced, &partial, &mut next, output_len)?;
3025                    next
3026                };
3027                reduced = next;
3028            }
3029        }
3030        let _main = root.gpu.enter_main()?;
3031        root.dtoh(&reduced)
3032    }
3033
3034    /// Native-P2P Step row projection with canonical global K-block reduction.
3035    ///
3036    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3037    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3038    /// replay the same eight-block order as TP1 and the host-staged oracle.
3039    pub fn step_bf16_row_parallel_resident_native(
3040        &self,
3041        matrix: &ResidentStepBf16RowParallel,
3042        activations: &[f32],
3043        tokens: usize,
3044    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3045        if self.ranks.len() > 1 && !self.native_p2p {
3046            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3047        }
3048        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3049        validate_activations(activations, tokens, matrix.in_features)?;
3050        let root = &self.ranks[0];
3051        let root_input = {
3052            let _main = root.gpu.enter_main()?;
3053            root.htod(activations)?
3054        };
3055        let output_len = tokens
3056            .checked_mul(matrix.out_features)
3057            .ok_or("native Step BF16 row output size overflow")?;
3058        let mut reduced = {
3059            let _main = root.gpu.enter_main()?;
3060            root.htod(&vec![0.0f32; output_len])?
3061        };
3062        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3063        // from the other ranks' streams while root's clone_htod may still be in flight.
3064        {
3065            let _main = root.gpu.enter_main()?;
3066            root.stream().synchronize()?;
3067        }
3068        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3069        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3070        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3071        let mut remote_partial_keepalive = Vec::new();
3072        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3073            for (block, resident) in blocks.iter().enumerate() {
3074                let global_block = rank * blocks_per_rank + block;
3075                let col_start = global_block * matrix.canonical_chunk_cols;
3076                let block_len = tokens
3077                    .checked_mul(matrix.canonical_chunk_cols)
3078                    .ok_or("native Step BF16 row block size overflow")?;
3079                let block_input = if self.bulk_p2p {
3080                    let root_packed = {
3081                        let _main = root.gpu.enter_main()?;
3082                        let mut root_packed = root.uninit(block_len)?;
3083                        root.copy_rows_strided(
3084                            &root_input,
3085                            &mut root_packed,
3086                            matrix.canonical_chunk_cols,
3087                            tokens,
3088                            matrix.in_features,
3089                            col_start,
3090                        )?;
3091                        root_packed
3092                    };
3093                    if rank == 0 {
3094                        root_packed
3095                    } else {
3096                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3097                        // root stream; this rank's peer read must not overtake it.
3098                        {
3099                            let _main = root.gpu.enter_main()?;
3100                            root.stream().synchronize()?;
3101                        }
3102                        let engine = &self.ranks[rank];
3103                        let _main = engine.gpu.enter_main()?;
3104                        let mut block_input = engine.uninit(block_len)?;
3105                        engine
3106                            .stream()
3107                            .memcpy_dtod(&root_packed, &mut block_input)?;
3108                        root_packed_keepalive.push(root_packed);
3109                        block_input
3110                    }
3111                } else {
3112                    let engine = &self.ranks[rank];
3113                    let _main = engine.gpu.enter_main()?;
3114                    let mut block_input = engine.uninit(block_len)?;
3115                    for token in 0..tokens {
3116                        let source_start = token * matrix.in_features + col_start;
3117                        let source = root_input
3118                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3119                        let destination_start = token * matrix.canonical_chunk_cols;
3120                        let mut destination = block_input.slice_mut(
3121                            destination_start..destination_start + matrix.canonical_chunk_cols,
3122                        );
3123                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3124                    }
3125                    block_input
3126                };
3127                let partial = run_resident_bf16_rank_device(
3128                    &self.ranks[rank],
3129                    resident,
3130                    &block_input,
3131                    tokens,
3132                    None,
3133                    self.bulk_p2p,
3134                )?;
3135                block_input_keepalive.push(block_input);
3136                let root_partial = if rank == 0 {
3137                    partial
3138                } else {
3139                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3140                    // rank's kernel on its own stream; root's peer read must not overtake it.
3141                    {
3142                        let engine = &self.ranks[rank];
3143                        let _main = engine.gpu.enter_main()?;
3144                        engine.stream().synchronize()?;
3145                    }
3146                    let _main = root.gpu.enter_main()?;
3147                    let mut peer_partial = root.uninit(output_len)?;
3148                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3149                    remote_partial_keepalive.push(partial);
3150                    peer_partial
3151                };
3152                let next = {
3153                    let _main = root.gpu.enter_main()?;
3154                    let mut next = root.uninit(output_len)?;
3155                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3156                    next
3157                };
3158                reduced = next;
3159            }
3160        }
3161        let output = {
3162            let _main = root.gpu.enter_main()?;
3163            root.dtoh(&reduced)?
3164        };
3165        drop(remote_partial_keepalive);
3166        drop(root_packed_keepalive);
3167        drop(block_input_keepalive);
3168        Ok(output)
3169    }
3170
3171    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3172    /// on the root device.
3173    pub fn step_bf16_row_parallel_resident_root_device(
3174        &self,
3175        matrix: &ResidentStepBf16RowParallel,
3176        rank_activations: &[CudaSlice<f32>],
3177        tokens: usize,
3178    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3179        if self.ranks.len() > 1 && !self.native_p2p {
3180            return Err(
3181                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3182            );
3183        }
3184        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3185        let local_width = matrix.in_features / self.ranks.len();
3186        let shard_len = tokens
3187            .checked_mul(local_width)
3188            .ok_or("device Step BF16 row shard size overflow")?;
3189        if tokens == 0
3190            || rank_activations.len() != self.ranks.len()
3191            || rank_activations
3192                .iter()
3193                .zip(&self.ranks)
3194                .any(|(rows, engine)| {
3195                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3196                })
3197        {
3198            return Err("device Step BF16 row activation shard geometry changed".into());
3199        }
3200
3201        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3202        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3203        let mut partials = Vec::with_capacity(self.ranks.len());
3204        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3205            if blocks.len() != blocks_per_rank {
3206                return Err(format!(
3207                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3208                    blocks.len()
3209                )
3210                .into());
3211            }
3212            let engine = &self.ranks[rank];
3213            let _main = engine.gpu.enter_main()?;
3214            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3215            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3216            for (block, resident) in blocks.iter().enumerate() {
3217                let block_len = tokens
3218                    .checked_mul(matrix.canonical_chunk_cols)
3219                    .ok_or("device Step BF16 row block size overflow")?;
3220                let mut block_input = engine.uninit(block_len)?;
3221                let local_col_start = block * matrix.canonical_chunk_cols;
3222                if self.bulk_p2p {
3223                    engine.copy_rows_strided(
3224                        &rank_activations[rank],
3225                        &mut block_input,
3226                        matrix.canonical_chunk_cols,
3227                        tokens,
3228                        local_width,
3229                        local_col_start,
3230                    )?;
3231                } else {
3232                    for token in 0..tokens {
3233                        let source_start = token * local_width + local_col_start;
3234                        let source = rank_activations[rank]
3235                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3236                        let destination_start = token * matrix.canonical_chunk_cols;
3237                        let mut destination = block_input.slice_mut(
3238                            destination_start..destination_start + matrix.canonical_chunk_cols,
3239                        );
3240                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3241                    }
3242                }
3243                let partial = run_resident_bf16_rank_device(
3244                    engine,
3245                    resident,
3246                    &block_input,
3247                    tokens,
3248                    None,
3249                    self.bulk_p2p,
3250                )?;
3251                rank_inputs.push(block_input);
3252                rank_partials.push(partial);
3253            }
3254            block_inputs.push(rank_inputs);
3255            partials.push(rank_partials);
3256        }
3257        for engine in self.ranks.iter().skip(1) {
3258            let _main = engine.gpu.enter_main()?;
3259            engine.stream().synchronize()?;
3260        }
3261
3262        let output_len = tokens
3263            .checked_mul(matrix.out_features)
3264            .ok_or("device Step BF16 row output size overflow")?;
3265        let root = &self.ranks[0];
3266        let _main = root.gpu.enter_main()?;
3267        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3268        let mut remote_partials = Vec::new();
3269        for (rank, rank_partials) in partials.into_iter().enumerate() {
3270            for partial in rank_partials {
3271                let root_partial = if rank == 0 {
3272                    partial
3273                } else {
3274                    let mut peer_partial = root.uninit(output_len)?;
3275                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3276                    remote_partials.push(partial);
3277                    peer_partial
3278                };
3279                let mut next = root.uninit(output_len)?;
3280                root.add(&reduced, &root_partial, &mut next, output_len)?;
3281                reduced = next;
3282            }
3283        }
3284        root.stream().synchronize()?;
3285        drop(remote_partials);
3286        drop(block_inputs);
3287        Ok(reduced)
3288    }
3289
3290    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3291    pub fn step_bf16_row_parallel_resident_replicated_device(
3292        &self,
3293        matrix: &ResidentStepBf16RowParallel,
3294        rank_activations: &[CudaSlice<f32>],
3295        tokens: usize,
3296    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3297        let reduced =
3298            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3299        let output_len = tokens
3300            .checked_mul(matrix.out_features)
3301            .ok_or("device Step BF16 row output size overflow")?;
3302        let mut ranks = Vec::with_capacity(self.ranks.len());
3303        ranks.push(reduced);
3304        for engine in self.ranks.iter().skip(1) {
3305            let _main = engine.gpu.enter_main()?;
3306            let mut peer_output = engine.uninit(output_len)?;
3307            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3308            ranks.push(peer_output);
3309        }
3310        Ok(ResidentReplicatedDeviceRows {
3311            ranks,
3312            tokens,
3313            width: matrix.out_features,
3314        })
3315    }
3316
3317    pub fn upload_expert(
3318        &self,
3319        gate: E4m3BlockMatrix<'_>,
3320        up: E4m3BlockMatrix<'_>,
3321        down: E4m3BlockMatrix<'_>,
3322    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3323        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3324            return Err("TP expert gate/up dimensions differ".into());
3325        }
3326        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3327            return Err(format!(
3328                "TP expert down {}x{} does not invert gate/up {}x{}",
3329                down.out_features, down.in_features, gate.out_features, gate.in_features
3330            )
3331            .into());
3332        }
3333        Ok(ResidentTpExpert {
3334            gate: self.upload_column_parallel(gate)?,
3335            up: self.upload_column_parallel(up)?,
3336            down: self.upload_row_parallel(down)?,
3337            input_width: gate.in_features,
3338            expert_width: gate.out_features,
3339        })
3340    }
3341
3342    pub fn run_expert(
3343        &self,
3344        expert: &ResidentTpExpert,
3345        input: &[f32],
3346        tokens: usize,
3347    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3348        validate_activations(input, tokens, expert.input_width)?;
3349        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3350        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3351        let activated: Vec<f32> = gate
3352            .gathered
3353            .iter()
3354            .zip(&up.gathered)
3355            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3356            .collect();
3357        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3358        Ok(self
3359            .row_parallel_resident(&expert.down, &activated, tokens)?
3360            .reduced)
3361    }
3362
3363    pub fn upload_expert_parallel(
3364        &self,
3365        gate: E4m3ExpertBank<'_>,
3366        up: E4m3ExpertBank<'_>,
3367        down: E4m3ExpertBank<'_>,
3368    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3369        gate.validate()?;
3370        up.validate()?;
3371        down.validate()?;
3372        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3373            return Err("EP gate/up/down expert counts differ".into());
3374        }
3375        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3376            return Err("EP gate/up dimensions differ".into());
3377        }
3378        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3379            return Err(format!(
3380                "EP down {}x{} does not invert gate/up {}x{}",
3381                down.out_features, down.in_features, gate.out_features, gate.in_features
3382            )
3383            .into());
3384        }
3385        if gate.expert_count % self.ranks.len() != 0 {
3386            return Err(format!(
3387                "EP expert count {} is not divisible by {} ranks",
3388                gate.expert_count,
3389                self.ranks.len()
3390            )
3391            .into());
3392        }
3393
3394        let per_rank = gate.expert_count / self.ranks.len();
3395        let mut ranks = Vec::with_capacity(self.ranks.len());
3396        for (rank, engine) in self.ranks.iter().enumerate() {
3397            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3398            ranks.push(ResidentEpRank {
3399                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3400                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3401                down: upload_expert_bank_rank(engine, down, expert_range)?,
3402            });
3403        }
3404        Ok(ResidentExpertParallel {
3405            ranks,
3406            expert_count: gate.expert_count,
3407            input_width: gate.in_features,
3408            expert_width: gate.out_features,
3409        })
3410    }
3411
3412    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3413    ///
3414    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3415    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3416    /// without routing, transport, or combine changing underneath it.
3417    #[allow(clippy::too_many_arguments)]
3418    pub fn prepare_step_grouped_fp8_gate(
3419        &self,
3420        gate: E4m3ExpertBank<'_>,
3421        up: E4m3ExpertBank<'_>,
3422        down: E4m3ExpertBank<'_>,
3423        input: &[f32],
3424        tokens: usize,
3425        selected: &[usize],
3426        activation_limit: Option<f32>,
3427    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3428        gate.validate()?;
3429        up.validate()?;
3430        down.validate()?;
3431        validate_step_expert_activation_limit(activation_limit)?;
3432        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3433            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3434            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3435        {
3436            return Err(format!(
3437                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3438                 got gate/up/down={}/{}/{}",
3439                gate.expert_count, up.expert_count, down.expert_count,
3440            )
3441            .into());
3442        }
3443        if gate.in_features != up.in_features
3444            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3445            || up.out_features != STEP_GROUPED_FP8_WIDTH
3446            || down.in_features != STEP_GROUPED_FP8_WIDTH
3447            || down.out_features != gate.in_features
3448        {
3449            return Err(format!(
3450                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3451                gate.out_features,
3452                gate.in_features,
3453                up.out_features,
3454                up.in_features,
3455                down.out_features,
3456                down.in_features,
3457            )
3458            .into());
3459        }
3460        validate_activations(input, tokens, gate.in_features)?;
3461        let pairs = tokens
3462            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3463            .ok_or("official Step grouped FP8 route count overflow")?;
3464        if selected.len() != pairs {
3465            return Err(format!(
3466                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3467                 ({pairs})",
3468                selected.len()
3469            )
3470            .into());
3471        }
3472        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3473            let mut unique = routes.to_vec();
3474            unique.sort_unstable();
3475            unique.dedup();
3476            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3477                return Err(format!(
3478                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3479                     {routes:?}"
3480                )
3481                .into());
3482            }
3483        }
3484
3485        let engine = self
3486            .ranks
3487            .first()
3488            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3489        let _main = engine.gpu.enter_main()?;
3490        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3491        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3492        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3493        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3494        let input = engine.htod(input)?;
3495        let route_csr = ExpertCsr::from_token_routes(
3496            STEP_GROUPED_FP8_EXPERTS,
3497            tokens,
3498            STEP_GROUPED_FP8_TOP_K,
3499            selected,
3500        )?
3501        .upload(engine)?;
3502        let pair_rows = (0..pairs).collect::<Vec<_>>();
3503        let down_csr =
3504            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3505                .upload(engine)?;
3506        let gate_workspace =
3507            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3508        let up_workspace =
3509            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3510        let down_workspace =
3511            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3512        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3513        Ok(PreparedStepGroupedFp8Gate {
3514            device: engine.ctx().ordinal(),
3515            gate,
3516            up,
3517            down,
3518            input,
3519            route_csr,
3520            down_csr,
3521            gate_workspace,
3522            up_workspace,
3523            down_workspace,
3524            activation,
3525            activation_limit,
3526            tokens,
3527            pairs,
3528        })
3529    }
3530
3531    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3532    pub fn run_step_grouped_fp8_gate(
3533        &self,
3534        plan: &mut PreparedStepGroupedFp8Gate,
3535    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3536        let engine = self
3537            .ranks
3538            .first()
3539            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3540        if engine.ctx().ordinal() != plan.device {
3541            return Err(format!(
3542                "official Step grouped FP8 plan device {} != rank-zero device {}",
3543                plan.device,
3544                engine.ctx().ordinal()
3545            )
3546            .into());
3547        }
3548        let _main = engine.gpu.enter_main()?;
3549
3550        plan.gate_workspace.quantize(engine, &plan.input)?;
3551        plan.gate_workspace.project(
3552            engine,
3553            &plan.gate.codes,
3554            &plan.gate.scales,
3555            &plan.route_csr,
3556            plan.gate.code_stride,
3557            plan.gate.scale_stride,
3558            1.0,
3559        )?;
3560        plan.up_workspace.quantize(engine, &plan.input)?;
3561        plan.up_workspace.project(
3562            engine,
3563            &plan.up.codes,
3564            &plan.up.scales,
3565            &plan.route_csr,
3566            plan.up.code_stride,
3567            plan.up.scale_stride,
3568            1.0,
3569        )?;
3570        if let Some(limit) = plan.activation_limit {
3571            engine.silu_clamped_mul_host_expf(
3572                plan.gate_workspace.output(),
3573                plan.up_workspace.output(),
3574                limit,
3575                &mut plan.activation,
3576                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3577            )?;
3578        } else {
3579            engine.silu_mul_host_expf(
3580                plan.gate_workspace.output(),
3581                plan.up_workspace.output(),
3582                &mut plan.activation,
3583                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3584            )?;
3585        }
3586        plan.down_workspace.quantize(engine, &plan.activation)?;
3587        plan.down_workspace.project(
3588            engine,
3589            &plan.down.codes,
3590            &plan.down.scales,
3591            &plan.down_csr,
3592            plan.down.code_stride,
3593            plan.down.scale_stride,
3594            1.0,
3595        )?;
3596
3597        Ok(StepGroupedFp8ProjectionOutput {
3598            gate: engine.dtoh(plan.gate_workspace.output())?,
3599            up: engine.dtoh(plan.up_workspace.output())?,
3600            down: engine.dtoh(plan.down_workspace.output())?,
3601        })
3602    }
3603
3604    pub fn prepare_step_grouped_expert_parallel_gate(
3605        &self,
3606        experts: &ResidentExpertParallel,
3607        input: &[f32],
3608        tokens: usize,
3609        selected: &[usize],
3610        activation_limit: Option<f32>,
3611    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3612        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3613            experts,
3614            input,
3615            tokens,
3616            selected,
3617            activation_limit,
3618            tokens,
3619        )
3620    }
3621
3622    #[allow(clippy::too_many_arguments)]
3623    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3624        &self,
3625        experts: &ResidentExpertParallel,
3626        input: &[f32],
3627        tokens: usize,
3628        selected: &[usize],
3629        activation_limit: Option<f32>,
3630        max_tokens: usize,
3631    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3632        if !self.native_p2p || !self.ep_device_arithmetic {
3633            return Err(
3634                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3635            );
3636        }
3637        validate_step_expert_activation_limit(activation_limit)?;
3638        validate_ep_residency(&self.ranks, experts)?;
3639        validate_activations(input, tokens, experts.input_width)?;
3640        if max_tokens < tokens || max_tokens > i32::MAX as usize {
3641            return Err(format!(
3642                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
3643            )
3644            .into());
3645        }
3646        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
3647            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
3648        {
3649            return Err(format!(
3650                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
3651                STEP_GROUPED_FP8_EXPERTS,
3652                STEP_GROUPED_FP8_WIDTH,
3653                experts.expert_count,
3654                experts.expert_width,
3655            )
3656            .into());
3657        }
3658        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3659        let max_pairs = max_tokens
3660            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3661            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
3662        let input_capacity = max_tokens
3663            .checked_mul(experts.input_width)
3664            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
3665
3666        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3667        for engine in &self.ranks {
3668            let _main = engine.gpu.enter_main()?;
3669            rank_inputs.push(engine.uninit(input_capacity)?);
3670        }
3671
3672        let mut owners = Vec::with_capacity(self.ranks.len());
3673        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
3674            if rank.gate.expert_range != rank.up.expert_range
3675                || rank.gate.expert_range != rank.down.expert_range
3676            {
3677                return Err(format!(
3678                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
3679                    owner_rank
3680                )
3681                .into());
3682            }
3683            let local_experts = rank.gate.expert_range.len();
3684            let engine = &self.ranks[owner_rank];
3685            let _main = engine.gpu.enter_main()?;
3686            let route_csr =
3687                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
3688            let down_csr =
3689                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
3690            let gate_workspace = Fp8GroupedWorkspace::new(
3691                engine,
3692                experts.input_width,
3693                experts.expert_width,
3694                max_tokens,
3695                max_pairs,
3696            )?;
3697            let up_workspace = Fp8GroupedWorkspace::new(
3698                engine,
3699                experts.input_width,
3700                experts.expert_width,
3701                max_tokens,
3702                max_pairs,
3703            )?;
3704            let down_workspace = Fp8GroupedWorkspace::new(
3705                engine,
3706                experts.expert_width,
3707                experts.input_width,
3708                max_pairs,
3709                max_pairs,
3710            )?;
3711            let activation = engine.uninit(
3712                max_pairs
3713                    .checked_mul(experts.expert_width)
3714                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
3715            )?;
3716            owners.push(PreparedStepGroupedExpertOwner {
3717                rank: owner_rank,
3718                global_pairs: Vec::new(),
3719                route_csr,
3720                down_csr,
3721                gate_workspace,
3722                up_workspace,
3723                down_workspace,
3724                activation,
3725            });
3726        }
3727
3728        let mut plan = PreparedStepGroupedExpertParallelGate {
3729            rank_inputs,
3730            owners,
3731            activation_limit,
3732            tokens: 0,
3733            pairs: 0,
3734            max_tokens,
3735            max_pairs,
3736            input_width: experts.input_width,
3737            expert_width: experts.expert_width,
3738            generation: 0,
3739            executed_generation: None,
3740            ready: false,
3741        };
3742        self.refresh_step_grouped_expert_parallel_gate(
3743            experts, &mut plan, input, tokens, selected,
3744        )?;
3745        Ok(plan)
3746    }
3747
3748    fn prepare_step_grouped_expert_parallel_refresh(
3749        &self,
3750        experts: &ResidentExpertParallel,
3751        plan: &PreparedStepGroupedExpertParallelGate,
3752        tokens: usize,
3753        selected: &[usize],
3754    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
3755    {
3756        validate_ep_residency(&self.ranks, experts)?;
3757        if plan.rank_inputs.len() != self.ranks.len()
3758            || plan.owners.len() != self.ranks.len()
3759            || plan.input_width != experts.input_width
3760            || plan.expert_width != experts.expert_width
3761            || tokens > plan.max_tokens
3762        {
3763            return Err(format!(
3764                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
3765                 input={}/{} expert={}/{} tokens={}/{}",
3766                plan.rank_inputs.len(),
3767                self.ranks.len(),
3768                plan.owners.len(),
3769                self.ranks.len(),
3770                plan.input_width,
3771                experts.input_width,
3772                plan.expert_width,
3773                experts.expert_width,
3774                tokens,
3775                plan.max_tokens,
3776            )
3777            .into());
3778        }
3779        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3780        if pairs > plan.max_pairs {
3781            return Err(format!(
3782                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
3783                plan.max_pairs
3784            )
3785            .into());
3786        }
3787        let next_generation = plan
3788            .generation
3789            .checked_add(1)
3790            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
3791        let owner_routes = partition_expert_owner_routes(
3792            experts.expert_count,
3793            self.ranks.len(),
3794            tokens,
3795            STEP_GROUPED_FP8_TOP_K,
3796            selected,
3797        )?;
3798        let mut schedules = Vec::with_capacity(self.ranks.len());
3799        for routes in owner_routes {
3800            if routes.selected.is_empty() {
3801                schedules.push(None);
3802                continue;
3803            }
3804            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
3805            let local_pairs = routes.selected.len();
3806            let route_csr = ExpertCsr::from_pair_rows(
3807                local_experts,
3808                tokens,
3809                &routes.selected,
3810                &routes.token_rows,
3811            )?;
3812            let down_rows = (0..local_pairs).collect::<Vec<_>>();
3813            let down_csr = ExpertCsr::from_pair_rows(
3814                local_experts,
3815                local_pairs,
3816                &routes.selected,
3817                &down_rows,
3818            )?;
3819            schedules.push(Some(StepGroupedExpertOwnerSchedule {
3820                global_pairs: routes.global_pairs,
3821                route_csr,
3822                down_csr,
3823            }));
3824        }
3825        Ok((pairs, next_generation, schedules))
3826    }
3827
3828    fn commit_step_grouped_expert_parallel_refresh(
3829        &self,
3830        plan: &mut PreparedStepGroupedExpertParallelGate,
3831        tokens: usize,
3832        pairs: usize,
3833        next_generation: u64,
3834        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
3835    ) -> Result<(), Box<dyn std::error::Error>> {
3836        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
3837            let engine = &self.ranks[owner.rank];
3838            let _main = engine.gpu.enter_main()?;
3839            if let Some(schedule) = schedule {
3840                owner.route_csr.refresh(engine, &schedule.route_csr)?;
3841                owner.down_csr.refresh(engine, &schedule.down_csr)?;
3842                owner.global_pairs = schedule.global_pairs;
3843            } else {
3844                owner.route_csr.clear();
3845                owner.down_csr.clear();
3846                owner.global_pairs.clear();
3847            }
3848        }
3849        plan.tokens = tokens;
3850        plan.pairs = pairs;
3851        plan.generation = next_generation;
3852        plan.ready = true;
3853        Ok(())
3854    }
3855
3856    pub fn refresh_step_grouped_expert_parallel_gate(
3857        &self,
3858        experts: &ResidentExpertParallel,
3859        plan: &mut PreparedStepGroupedExpertParallelGate,
3860        input: &[f32],
3861        tokens: usize,
3862        selected: &[usize],
3863    ) -> Result<(), Box<dyn std::error::Error>> {
3864        validate_activations(input, tokens, experts.input_width)?;
3865        let (pairs, next_generation, schedules) =
3866            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
3867
3868        plan.ready = false;
3869        plan.executed_generation = None;
3870        {
3871            let root = &self.ranks[0];
3872            let _main = root.gpu.enter_main()?;
3873            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
3874            root.stream().memcpy_htod(input, &mut destination)?;
3875            root.stream().synchronize()?;
3876        }
3877        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
3878        let root_input = &root_inputs[0];
3879        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
3880            let engine = &self.ranks[rank + 1];
3881            let _main = engine.gpu.enter_main()?;
3882            let mut destination = peer_input.slice_mut(0..input.len());
3883            engine
3884                .stream()
3885                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
3886        }
3887        self.commit_step_grouped_expert_parallel_refresh(
3888            plan,
3889            tokens,
3890            pairs,
3891            next_generation,
3892            schedules,
3893        )
3894    }
3895
3896    /// Refresh routes and inputs from an already-resident rank-zero activation.
3897    ///
3898    /// The caller must order the source producer before this call. The root copy is completed
3899    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
3900    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
3901        &self,
3902        experts: &ResidentExpertParallel,
3903        plan: &mut PreparedStepGroupedExpertParallelGate,
3904        input: &CudaSlice<f32>,
3905        tokens: usize,
3906        selected: &[usize],
3907    ) -> Result<(), Box<dyn std::error::Error>> {
3908        let input_values = tokens
3909            .checked_mul(experts.input_width)
3910            .ok_or("Step owner-grouped FP8 input size overflow")?;
3911        let root = self
3912            .ranks
3913            .first()
3914            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
3915        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
3916            return Err(format!(
3917                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
3918                 device {}",
3919                input.len(),
3920                input.ordinal(),
3921                input_values,
3922                root.ctx().ordinal(),
3923            )
3924            .into());
3925        }
3926        let (pairs, next_generation, schedules) =
3927            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
3928
3929        plan.ready = false;
3930        plan.executed_generation = None;
3931        {
3932            let _main = root.gpu.enter_main()?;
3933            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
3934            root.stream()
3935                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
3936            root.stream().synchronize()?;
3937        }
3938        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
3939        let root_input = &root_inputs[0];
3940        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
3941            let engine = &self.ranks[rank + 1];
3942            let _main = engine.gpu.enter_main()?;
3943            let mut destination = peer_input.slice_mut(0..input_values);
3944            engine
3945                .stream()
3946                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
3947        }
3948        self.commit_step_grouped_expert_parallel_refresh(
3949            plan,
3950            tokens,
3951            pairs,
3952            next_generation,
3953            schedules,
3954        )
3955    }
3956
3957    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
3958    ///
3959    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
3960    /// and combine result, so callers must refresh combine metadata before executing again.
3961    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
3962        &self,
3963        experts: &ResidentExpertParallel,
3964        plan: &mut PreparedStepGroupedExpertParallelGate,
3965        input: &ResidentReplicatedDeviceRows,
3966    ) -> Result<(), Box<dyn std::error::Error>> {
3967        validate_ep_residency(&self.ranks, experts)?;
3968        validate_replicated_device_rows(&self.ranks, input)?;
3969        if !plan.ready
3970            || input.tokens != plan.tokens
3971            || input.width != plan.input_width
3972            || input.tokens > plan.max_tokens
3973            || plan.rank_inputs.len() != self.ranks.len()
3974            || plan.owners.len() != self.ranks.len()
3975            || plan.input_width != experts.input_width
3976            || plan.expert_width != experts.expert_width
3977        {
3978            return Err("Step owner-grouped replicated input geometry changed".into());
3979        }
3980        let values = input
3981            .tokens
3982            .checked_mul(input.width)
3983            .ok_or("Step owner-grouped replicated input size overflow")?;
3984        let next_generation = plan
3985            .generation
3986            .checked_add(1)
3987            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
3988        plan.ready = false;
3989        plan.executed_generation = None;
3990        for (rank, engine) in self.ranks.iter().enumerate() {
3991            let _main = engine.gpu.enter_main()?;
3992            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
3993            engine
3994                .stream()
3995                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
3996        }
3997        plan.generation = next_generation;
3998        plan.ready = true;
3999        Ok(())
4000    }
4001
4002    pub fn execute_step_grouped_expert_parallel_gate(
4003        &self,
4004        experts: &ResidentExpertParallel,
4005        plan: &mut PreparedStepGroupedExpertParallelGate,
4006    ) -> Result<(), Box<dyn std::error::Error>> {
4007        validate_ep_residency(&self.ranks, experts)?;
4008        if !plan.ready
4009            || plan.rank_inputs.len() != self.ranks.len()
4010            || plan.owners.len() != self.ranks.len()
4011            || plan.input_width != experts.input_width
4012            || plan.expert_width != experts.expert_width
4013        {
4014            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
4015        }
4016        plan.executed_generation = None;
4017
4018        for owner in &mut plan.owners {
4019            if owner.global_pairs.is_empty() {
4020                continue;
4021            }
4022            let engine = &self.ranks[owner.rank];
4023            let bank = &experts.ranks[owner.rank];
4024            let _main = engine.gpu.enter_main()?;
4025            let local_pairs = owner.global_pairs.len();
4026            owner.gate_workspace.quantize_for_shape(
4027                engine,
4028                &plan.rank_inputs[owner.rank],
4029                plan.tokens,
4030                local_pairs,
4031            )?;
4032            owner.gate_workspace.project(
4033                engine,
4034                &bank.gate.codes,
4035                &bank.gate.scales,
4036                &owner.route_csr,
4037                bank.gate.code_stride,
4038                bank.gate.scale_stride,
4039                1.0,
4040            )?;
4041            owner.up_workspace.quantize_for_shape(
4042                engine,
4043                &plan.rank_inputs[owner.rank],
4044                plan.tokens,
4045                local_pairs,
4046            )?;
4047            owner.up_workspace.project(
4048                engine,
4049                &bank.up.codes,
4050                &bank.up.scales,
4051                &owner.route_csr,
4052                bank.up.code_stride,
4053                bank.up.scale_stride,
4054                1.0,
4055            )?;
4056        }
4057        for owner in &mut plan.owners {
4058            if owner.global_pairs.is_empty() {
4059                continue;
4060            }
4061            let engine = &self.ranks[owner.rank];
4062            let _main = engine.gpu.enter_main()?;
4063            let values = owner.global_pairs.len() * plan.expert_width;
4064            if let Some(limit) = plan.activation_limit {
4065                engine.silu_clamped_mul_host_expf(
4066                    owner.gate_workspace.output(),
4067                    owner.up_workspace.output(),
4068                    limit,
4069                    &mut owner.activation,
4070                    values,
4071                )?;
4072            } else {
4073                engine.silu_mul_host_expf(
4074                    owner.gate_workspace.output(),
4075                    owner.up_workspace.output(),
4076                    &mut owner.activation,
4077                    values,
4078                )?;
4079            }
4080        }
4081        for owner in &mut plan.owners {
4082            if owner.global_pairs.is_empty() {
4083                continue;
4084            }
4085            let engine = &self.ranks[owner.rank];
4086            let bank = &experts.ranks[owner.rank];
4087            let _main = engine.gpu.enter_main()?;
4088            let local_pairs = owner.global_pairs.len();
4089            owner.down_workspace.quantize_for_shape(
4090                engine,
4091                &owner.activation,
4092                local_pairs,
4093                local_pairs,
4094            )?;
4095            owner.down_workspace.project(
4096                engine,
4097                &bank.down.codes,
4098                &bank.down.scales,
4099                &owner.down_csr,
4100                bank.down.code_stride,
4101                bank.down.scale_stride,
4102                1.0,
4103            )?;
4104        }
4105        plan.executed_generation = Some(plan.generation);
4106        Ok(())
4107    }
4108
4109    pub fn collect_step_grouped_expert_parallel_gate(
4110        &self,
4111        plan: &PreparedStepGroupedExpertParallelGate,
4112    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4113        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4114            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4115        }
4116        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4117        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4118        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4119        for owner in &plan.owners {
4120            if owner.global_pairs.is_empty() {
4121                continue;
4122            }
4123            let engine = &self.ranks[owner.rank];
4124            let _main = engine.gpu.enter_main()?;
4125            let owner_gate = engine.dtoh_view(
4126                &owner
4127                    .gate_workspace
4128                    .output()
4129                    .slice(0..owner.gate_workspace.output_len()),
4130            )?;
4131            let owner_up = engine.dtoh_view(
4132                &owner
4133                    .up_workspace
4134                    .output()
4135                    .slice(0..owner.up_workspace.output_len()),
4136            )?;
4137            let owner_down = engine.dtoh_view(
4138                &owner
4139                    .down_workspace
4140                    .output()
4141                    .slice(0..owner.down_workspace.output_len()),
4142            )?;
4143            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4144                let local_expert = local_pair * plan.expert_width;
4145                let global_expert = global_pair * plan.expert_width;
4146                gate[global_expert..global_expert + plan.expert_width]
4147                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4148                up[global_expert..global_expert + plan.expert_width]
4149                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4150
4151                let local_hidden = local_pair * plan.input_width;
4152                let global_hidden = global_pair * plan.input_width;
4153                down[global_hidden..global_hidden + plan.input_width]
4154                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4155            }
4156        }
4157        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4158    }
4159
4160    pub fn run_step_grouped_expert_parallel_gate(
4161        &self,
4162        experts: &ResidentExpertParallel,
4163        plan: &mut PreparedStepGroupedExpertParallelGate,
4164    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4165        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4166        self.collect_step_grouped_expert_parallel_gate(plan)
4167    }
4168
4169    pub fn prepare_step_grouped_expert_parallel_combine(
4170        &self,
4171        plan: &PreparedStepGroupedExpertParallelGate,
4172        route_weights: &[f32],
4173    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4174        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4175            return Err(
4176                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4177            );
4178        }
4179        let owner_pairs = plan
4180            .owners
4181            .iter()
4182            .map(|owner| owner.global_pairs.as_slice())
4183            .collect::<Vec<_>>();
4184        let shape = validate_weighted_route_combine(
4185            plan.input_width,
4186            STEP_GROUPED_FP8_TOP_K,
4187            plan.max_tokens,
4188            plan.tokens,
4189            &owner_pairs,
4190            route_weights,
4191        )?;
4192        if shape.max_pairs != plan.max_pairs {
4193            return Err(format!(
4194                "Step owner-grouped combine capacity {} != projection capacity {}",
4195                shape.max_pairs, plan.max_pairs
4196            )
4197            .into());
4198        }
4199        let root = self
4200            .ranks
4201            .first()
4202            .ok_or("Step owner-grouped combine has no root rank")?;
4203        let slot_values = shape
4204            .max_pairs
4205            .checked_mul(plan.input_width)
4206            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4207        let output_values = plan
4208            .max_tokens
4209            .checked_mul(plan.input_width)
4210            .ok_or("Step owner-grouped combine output capacity overflow")?;
4211        let (root_device, owners, peer_staging, slots, weights, output) = {
4212            let _main = root.gpu.enter_main()?;
4213            let mut owners = Vec::with_capacity(plan.owners.len());
4214            for _ in &plan.owners {
4215                owners.push(PreparedPeerWeightedRouteOwner {
4216                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4217                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4218                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4219                    active_pairs: 0,
4220                });
4221            }
4222            (
4223                root.ctx().ordinal(),
4224                owners,
4225                root.uninit(slot_values)?,
4226                root.uninit(slot_values)?,
4227                root.uninit(shape.max_pairs)?,
4228                root.uninit(output_values)?,
4229            )
4230        };
4231        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4232        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4233        for engine in self.ranks.iter().skip(1) {
4234            let _main = engine.gpu.enter_main()?;
4235            peer_devices.push(engine.ctx().ordinal());
4236            peer_outputs.push(engine.uninit(output_values)?);
4237        }
4238        let mut combine = PreparedPeerWeightedRouteCombine {
4239            root_device,
4240            owners,
4241            peer_staging,
4242            slots,
4243            weights,
4244            output,
4245            peer_devices,
4246            peer_outputs,
4247            width: plan.input_width,
4248            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4249            max_tokens: plan.max_tokens,
4250            max_pairs: shape.max_pairs,
4251            tokens: 0,
4252            pairs: 0,
4253            projection_generation: 0,
4254            output_generation: None,
4255            broadcast_generation: None,
4256            ready: false,
4257        };
4258        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4259        Ok(combine)
4260    }
4261
4262    pub fn refresh_step_grouped_expert_parallel_combine(
4263        &self,
4264        plan: &PreparedStepGroupedExpertParallelGate,
4265        combine: &mut PreparedPeerWeightedRouteCombine,
4266        route_weights: &[f32],
4267    ) -> Result<(), Box<dyn std::error::Error>> {
4268        let output_capacity = combine
4269            .max_tokens
4270            .checked_mul(combine.width)
4271            .ok_or("Step owner-grouped combine output capacity overflow")?;
4272        if !plan.ready
4273            || combine.owners.len() != plan.owners.len()
4274            || combine.peer_devices.len() + 1 != self.ranks.len()
4275            || combine.peer_outputs.len() + 1 != self.ranks.len()
4276            || combine.width != plan.input_width
4277            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4278            || combine.max_tokens != plan.max_tokens
4279            || combine.max_pairs != plan.max_pairs
4280            || combine.output.len() < output_capacity
4281            || combine
4282                .peer_outputs
4283                .iter()
4284                .any(|output| output.len() < output_capacity)
4285        {
4286            return Err("Step owner-grouped combine/projection geometry changed".into());
4287        }
4288        if self
4289            .ranks
4290            .iter()
4291            .skip(1)
4292            .zip(&combine.peer_devices)
4293            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4294        {
4295            return Err("Step owner-grouped combine peer devices changed".into());
4296        }
4297        let owner_pairs = plan
4298            .owners
4299            .iter()
4300            .map(|owner| owner.global_pairs.as_slice())
4301            .collect::<Vec<_>>();
4302        let shape = validate_weighted_route_combine(
4303            combine.width,
4304            combine.experts_per_token,
4305            combine.max_tokens,
4306            plan.tokens,
4307            &owner_pairs,
4308            route_weights,
4309        )?;
4310        if shape.max_pairs != combine.max_pairs {
4311            return Err("Step owner-grouped combine capacity changed during refresh".into());
4312        }
4313        let metadata = owner_pairs
4314            .iter()
4315            .map(|pairs| {
4316                let token_rows = pairs
4317                    .iter()
4318                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4319                    .collect::<Vec<_>>();
4320                let slots = pairs
4321                    .iter()
4322                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4323                    .collect::<Vec<_>>();
4324                let weights = pairs
4325                    .iter()
4326                    .map(|&pair| route_weights[pair])
4327                    .collect::<Vec<_>>();
4328                (token_rows, slots, weights)
4329            })
4330            .collect::<Vec<_>>();
4331
4332        combine.ready = false;
4333        combine.output_generation = None;
4334        combine.broadcast_generation = None;
4335        let root = self
4336            .ranks
4337            .first()
4338            .ok_or("Step owner-grouped combine has no root rank")?;
4339        let _main = root.gpu.enter_main()?;
4340        if root.ctx().ordinal() != combine.root_device {
4341            return Err(format!(
4342                "Step owner-grouped combine root device changed {} != {}",
4343                root.ctx().ordinal(),
4344                combine.root_device
4345            )
4346            .into());
4347        }
4348        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4349            if token_rows.is_empty() {
4350                owner.active_pairs = 0;
4351                continue;
4352            }
4353            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4354            root.htod_i32_into(&mut owner.slots, &slots)?;
4355            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4356            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4357            owner.active_pairs = token_rows.len();
4358        }
4359        combine.tokens = plan.tokens;
4360        combine.pairs = shape.pairs;
4361        combine.projection_generation = plan.generation;
4362        combine.ready = true;
4363        Ok(())
4364    }
4365
4366    pub fn execute_step_grouped_expert_parallel_combine(
4367        &self,
4368        plan: &PreparedStepGroupedExpertParallelGate,
4369        combine: &mut PreparedPeerWeightedRouteCombine,
4370    ) -> Result<(), Box<dyn std::error::Error>> {
4371        if !plan.ready
4372            || plan.executed_generation != Some(plan.generation)
4373            || !combine.ready
4374            || combine.tokens != plan.tokens
4375            || combine.pairs != plan.pairs
4376            || combine.width != plan.input_width
4377            || combine.owners.len() != plan.owners.len()
4378            || combine.projection_generation != plan.generation
4379        {
4380            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4381        }
4382        combine.output_generation = None;
4383        combine.broadcast_generation = None;
4384        for owner in &plan.owners {
4385            if owner.rank == 0 || owner.global_pairs.is_empty() {
4386                continue;
4387            }
4388            let engine = &self.ranks[owner.rank];
4389            let _main = engine.gpu.enter_main()?;
4390            engine.stream().synchronize()?;
4391        }
4392        let root = self
4393            .ranks
4394            .first()
4395            .ok_or("Step owner-grouped combine has no root rank")?;
4396        let _main = root.gpu.enter_main()?;
4397        if root.ctx().ordinal() != combine.root_device {
4398            return Err("Step owner-grouped combine is not resident on the root device".into());
4399        }
4400        for (index, owner) in plan.owners.iter().enumerate() {
4401            let metadata = &combine.owners[index];
4402            if owner.global_pairs.len() != metadata.active_pairs {
4403                return Err(format!(
4404                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4405                    owner.global_pairs.len(),
4406                    metadata.active_pairs
4407                )
4408                .into());
4409            }
4410            if metadata.active_pairs == 0 {
4411                continue;
4412            }
4413            let values = metadata
4414                .active_pairs
4415                .checked_mul(combine.width)
4416                .ok_or("Step owner-grouped combine peer value count overflow")?;
4417            if owner.rank == 0 {
4418                root.scatter_slot(
4419                    owner.down_workspace.output(),
4420                    &metadata.token_rows,
4421                    &metadata.slots,
4422                    &metadata.weights,
4423                    &mut combine.slots,
4424                    &mut combine.weights,
4425                    combine.width,
4426                    combine.experts_per_token,
4427                    metadata.active_pairs,
4428                )?;
4429            } else {
4430                let source = owner.down_workspace.output().slice(0..values);
4431                let mut destination = combine.peer_staging.slice_mut(0..values);
4432                root.stream().memcpy_dtod(&source, &mut destination)?;
4433                root.scatter_slot(
4434                    &combine.peer_staging,
4435                    &metadata.token_rows,
4436                    &metadata.slots,
4437                    &metadata.weights,
4438                    &mut combine.slots,
4439                    &mut combine.weights,
4440                    combine.width,
4441                    combine.experts_per_token,
4442                    metadata.active_pairs,
4443                )?;
4444            }
4445        }
4446        root.reduce_slots_host(
4447            &combine.slots,
4448            &combine.weights,
4449            &mut combine.output,
4450            combine.width,
4451            combine.experts_per_token,
4452            combine.tokens,
4453        )?;
4454        combine.output_generation = Some(plan.generation);
4455        Ok(())
4456    }
4457
4458    pub fn collect_step_grouped_expert_parallel_combine(
4459        &self,
4460        plan: &PreparedStepGroupedExpertParallelGate,
4461        combine: &PreparedPeerWeightedRouteCombine,
4462    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4463        if !plan.ready
4464            || combine.output_generation != Some(plan.generation)
4465            || combine.projection_generation != plan.generation
4466        {
4467            return Err("Step owner-grouped combine output is stale or has not executed".into());
4468        }
4469        let root = self
4470            .ranks
4471            .first()
4472            .ok_or("Step owner-grouped combine has no root rank")?;
4473        let _main = root.gpu.enter_main()?;
4474        if root.ctx().ordinal() != combine.root_device {
4475            return Err("Step owner-grouped combine is not resident on the root device".into());
4476        }
4477        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4478    }
4479
4480    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4481    ///
4482    /// The persistent combine buffer remains reusable by the next route generation; the returned
4483    /// allocation follows the serving runtime's ordinary transient-output ownership.
4484    pub fn copy_step_grouped_expert_parallel_combine_root(
4485        &self,
4486        plan: &PreparedStepGroupedExpertParallelGate,
4487        combine: &PreparedPeerWeightedRouteCombine,
4488        destination: &Engine,
4489    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4490        if !plan.ready
4491            || combine.output_generation != Some(plan.generation)
4492            || combine.projection_generation != plan.generation
4493        {
4494            return Err("Step owner-grouped combine output is stale or has not executed".into());
4495        }
4496        let root = self
4497            .ranks
4498            .first()
4499            .ok_or("Step owner-grouped combine has no root rank")?;
4500        if root.ctx().ordinal() != combine.root_device
4501            || destination.ctx().ordinal() != combine.root_device
4502        {
4503            return Err(format!(
4504                "Step owner-grouped combine root/destination devices {}/{} != {}",
4505                root.ctx().ordinal(),
4506                destination.ctx().ordinal(),
4507                combine.root_device,
4508            )
4509            .into());
4510        }
4511        let values = combine
4512            .tokens
4513            .checked_mul(combine.width)
4514            .ok_or("Step owner-grouped combine copy size overflow")?;
4515        {
4516            let _main = root.gpu.enter_main()?;
4517            root.stream().synchronize()?;
4518        }
4519        let _main = destination.gpu.enter_main()?;
4520        let mut output = destination.uninit(values)?;
4521        destination
4522            .stream()
4523            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4524        Ok(output)
4525    }
4526
4527    pub fn broadcast_step_grouped_expert_parallel_combine(
4528        &self,
4529        plan: &PreparedStepGroupedExpertParallelGate,
4530        combine: &mut PreparedPeerWeightedRouteCombine,
4531    ) -> Result<(), Box<dyn std::error::Error>> {
4532        if !plan.ready
4533            || combine.output_generation != Some(plan.generation)
4534            || combine.projection_generation != plan.generation
4535            || combine.peer_devices.len() + 1 != self.ranks.len()
4536            || combine.peer_outputs.len() + 1 != self.ranks.len()
4537        {
4538            return Err("Step owner-grouped combine output cannot be broadcast".into());
4539        }
4540        combine.broadcast_generation = None;
4541        let values = combine
4542            .tokens
4543            .checked_mul(combine.width)
4544            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4545        {
4546            let root = self
4547                .ranks
4548                .first()
4549                .ok_or("Step owner-grouped combine has no root rank")?;
4550            let _main = root.gpu.enter_main()?;
4551            if root.ctx().ordinal() != combine.root_device {
4552                return Err("Step owner-grouped combine root device changed".into());
4553            }
4554            root.stream().synchronize()?;
4555        }
4556        let source = &combine.output;
4557        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4558            let engine = &self.ranks[index + 1];
4559            let _main = engine.gpu.enter_main()?;
4560            if engine.ctx().ordinal() != combine.peer_devices[index] {
4561                return Err(format!(
4562                    "Step owner-grouped combine peer {} device changed",
4563                    index + 1
4564                )
4565                .into());
4566            }
4567            let mut destination = destination_buffer.slice_mut(0..values);
4568            engine
4569                .stream()
4570                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4571        }
4572        combine.broadcast_generation = Some(plan.generation);
4573        Ok(())
4574    }
4575
4576    pub fn collect_step_grouped_expert_parallel_broadcast(
4577        &self,
4578        plan: &PreparedStepGroupedExpertParallelGate,
4579        combine: &PreparedPeerWeightedRouteCombine,
4580    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4581        if !plan.ready
4582            || combine.output_generation != Some(plan.generation)
4583            || combine.broadcast_generation != Some(plan.generation)
4584            || combine.peer_outputs.len() + 1 != self.ranks.len()
4585        {
4586            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4587        }
4588        let values = combine
4589            .tokens
4590            .checked_mul(combine.width)
4591            .ok_or("Step owner-grouped combine collection size overflow")?;
4592        let mut outputs = Vec::with_capacity(self.ranks.len());
4593        {
4594            let root = &self.ranks[0];
4595            let _main = root.gpu.enter_main()?;
4596            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4597        }
4598        for (index, output) in combine.peer_outputs.iter().enumerate() {
4599            let engine = &self.ranks[index + 1];
4600            let _main = engine.gpu.enter_main()?;
4601            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4602        }
4603        Ok(outputs)
4604    }
4605
4606    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4607    pub fn finish_step_grouped_expert_parallel_layer(
4608        &self,
4609        plan: &PreparedStepGroupedExpertParallelGate,
4610        combine: &PreparedPeerWeightedRouteCombine,
4611        shared: &ResidentReplicatedDeviceRows,
4612        residual: &ResidentReplicatedDeviceRows,
4613    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4614        validate_replicated_device_rows(&self.ranks, shared)?;
4615        validate_replicated_device_rows(&self.ranks, residual)?;
4616        if !plan.ready
4617            || plan.executed_generation != Some(plan.generation)
4618            || combine.output_generation != Some(plan.generation)
4619            || combine.broadcast_generation != Some(plan.generation)
4620            || combine.projection_generation != plan.generation
4621            || combine.peer_outputs.len() + 1 != self.ranks.len()
4622            || shared.tokens != combine.tokens
4623            || residual.tokens != combine.tokens
4624            || shared.width != combine.width
4625            || residual.width != combine.width
4626        {
4627            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4628        }
4629        let values = combine
4630            .tokens
4631            .checked_mul(combine.width)
4632            .ok_or("Step full-layer output size overflow")?;
4633        let mut ranks = Vec::with_capacity(self.ranks.len());
4634        for rank in 0..self.ranks.len() {
4635            let engine = &self.ranks[rank];
4636            let _main = engine.gpu.enter_main()?;
4637            let routed = if rank == 0 {
4638                &combine.output
4639            } else {
4640                &combine.peer_outputs[rank - 1]
4641            };
4642            let mut ffn = engine.uninit(values)?;
4643            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
4644            let mut output = engine.uninit(values)?;
4645            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
4646            ranks.push(output);
4647        }
4648        Ok(ResidentReplicatedDeviceRows {
4649            ranks,
4650            tokens: combine.tokens,
4651            width: combine.width,
4652        })
4653    }
4654
4655    pub fn run_step_grouped_expert_parallel_combine(
4656        &self,
4657        plan: &PreparedStepGroupedExpertParallelGate,
4658        combine: &mut PreparedPeerWeightedRouteCombine,
4659    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4660        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
4661        self.collect_step_grouped_expert_parallel_combine(plan, combine)
4662    }
4663
4664    pub fn upload_tensor_parallel(
4665        &self,
4666        gate: E4m3ExpertBank<'_>,
4667        up: E4m3ExpertBank<'_>,
4668        down: E4m3ExpertBank<'_>,
4669    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
4670        gate.validate()?;
4671        up.validate()?;
4672        down.validate()?;
4673        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
4674            return Err("TP gate/up/down expert counts differ".into());
4675        }
4676        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4677            return Err("TP gate/up dimensions differ".into());
4678        }
4679        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4680            return Err(format!(
4681                "TP down {}x{} does not invert gate/up {}x{}",
4682                down.out_features, down.in_features, gate.out_features, gate.in_features
4683            )
4684            .into());
4685        }
4686        let tp = self.ranks.len();
4687        validate_column_bank_shape(gate, tp)?;
4688        validate_column_bank_shape(up, tp)?;
4689        validate_row_bank_shape(down, tp)?;
4690
4691        let mut gate_ranks = Vec::with_capacity(tp);
4692        let mut up_ranks = Vec::with_capacity(tp);
4693        let mut down_ranks = Vec::with_capacity(tp);
4694        for (rank, engine) in self.ranks.iter().enumerate() {
4695            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
4696            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
4697            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
4698        }
4699        Ok(ResidentTensorParallel {
4700            bank: ResidentTpExpertBank {
4701                gate: gate_ranks,
4702                up: up_ranks,
4703                down: down_ranks,
4704                expert_count: gate.expert_count,
4705                input_width: gate.in_features,
4706                expert_width: gate.out_features,
4707            },
4708        })
4709    }
4710
4711    pub fn run_tensor_parallel_routes(
4712        &self,
4713        experts: &ResidentTensorParallel,
4714        input: &[f32],
4715        tokens: usize,
4716        selected: &[usize],
4717        route_weights: &[f32],
4718        experts_per_token: usize,
4719    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4720        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
4721        validate_activations(input, tokens, experts.bank.input_width)?;
4722        let pairs = tokens
4723            .checked_mul(experts_per_token)
4724            .ok_or("TP route count overflow")?;
4725        if selected.len() != pairs || route_weights.len() != pairs {
4726            return Err(format!(
4727                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
4728                 {experts_per_token} ({pairs})",
4729                selected.len(),
4730                route_weights.len(),
4731            )
4732            .into());
4733        }
4734        if !route_weights.iter().all(|weight| weight.is_finite()) {
4735            return Err("TP route weights contain a non-finite value".into());
4736        }
4737
4738        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
4739        for token in 0..tokens {
4740            let input_row =
4741                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
4742            for slot in 0..experts_per_token {
4743                let pair = token * experts_per_token + slot;
4744                let expert = selected[pair];
4745                if expert >= experts.bank.expert_count {
4746                    return Err(format!(
4747                        "TP selected expert {expert} outside 0..{}",
4748                        experts.bank.expert_count
4749                    )
4750                    .into());
4751                }
4752                let down = if self.native_p2p {
4753                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
4754                } else {
4755                    let gate =
4756                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
4757                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
4758                    let activated: Vec<f32> = gate
4759                        .iter()
4760                        .zip(&up)
4761                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4762                        .collect();
4763                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
4764                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
4765                };
4766                let weight = route_weights[pair];
4767                for (sum, value) in output
4768                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
4769                    .iter_mut()
4770                    .zip(down)
4771                {
4772                    *sum += weight * value;
4773                }
4774            }
4775        }
4776        Ok(output)
4777    }
4778
4779    fn run_column_bank_expert(
4780        &self,
4781        ranks: &[ResidentE4m3ExpertBankRank],
4782        expert: usize,
4783        input: &[f32],
4784    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4785        let local_out = ranks
4786            .first()
4787            .ok_or("TP column bank has no ranks")?
4788            .out_features;
4789        let mut gathered = vec![0.0f32; local_out * ranks.len()];
4790        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
4791            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
4792            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
4793        }
4794        Ok(gathered)
4795    }
4796
4797    fn run_row_bank_expert(
4798        &self,
4799        ranks: &[ResidentE4m3ExpertBankRank],
4800        expert: usize,
4801        input: &[f32],
4802    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4803        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
4804        if input.len() != local_in * ranks.len() {
4805            return Err(format!(
4806                "TP row input {} != {} ranks x {local_in}",
4807                input.len(),
4808                ranks.len()
4809            )
4810            .into());
4811        }
4812        let out_features = ranks[0].out_features;
4813        let mut reduced = vec![0.0f32; out_features];
4814        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
4815            let blocks = bank
4816                .k_blocks
4817                .ok_or("TP row bank is not packed in native K-block order")?;
4818            if blocks * FP8_BLOCK != local_in {
4819                return Err(format!(
4820                    "TP row bank has {blocks} blocks but local input width is {local_in}"
4821                )
4822                .into());
4823            }
4824            for block in 0..blocks {
4825                let global_start = rank * local_in + block * FP8_BLOCK;
4826                let partial = run_resident_bank_expert_block(
4827                    engine,
4828                    bank,
4829                    expert,
4830                    block,
4831                    &input[global_start..global_start + FP8_BLOCK],
4832                )?;
4833                for (sum, value) in reduced.iter_mut().zip(partial) {
4834                    *sum += value;
4835                }
4836            }
4837        }
4838        Ok(reduced)
4839    }
4840
4841    fn run_tensor_parallel_expert_native(
4842        &self,
4843        bank: &ResidentTpExpertBank,
4844        expert: usize,
4845        input: &[f32],
4846    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4847        if !self.native_p2p || self.ranks.len() < 2 {
4848            return Err("native TP expert execution requires at least two P2P ranks".into());
4849        }
4850        let local_out = bank
4851            .gate
4852            .first()
4853            .ok_or("native TP gate bank has no ranks")?
4854            .out_features;
4855        if local_out * self.ranks.len() != bank.expert_width {
4856            return Err(format!(
4857                "native TP gate shards {}x{local_out} != expert width {}",
4858                self.ranks.len(),
4859                bank.expert_width
4860            )
4861            .into());
4862        }
4863
4864        // The caller's routed input is already host-canonical. Upload once on rank zero, then
4865        // broadcast over peer copies so no other rank receives a host-staged duplicate.
4866        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
4867        let root_input = {
4868            let root = &self.ranks[0];
4869            let _main = root.gpu.enter_main()?;
4870            root.htod(input)?
4871        };
4872        rank_inputs.push(root_input);
4873        for engine in &self.ranks[1..] {
4874            let peer_input = {
4875                let _main = engine.gpu.enter_main()?;
4876                let mut peer_input = engine.uninit(input.len())?;
4877                engine
4878                    .stream()
4879                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
4880                peer_input
4881            };
4882            rank_inputs.push(peer_input);
4883        }
4884
4885        let mut gate_shards = Vec::with_capacity(self.ranks.len());
4886        let mut up_shards = Vec::with_capacity(self.ranks.len());
4887        for rank in 0..self.ranks.len() {
4888            gate_shards.push(run_resident_bank_expert_device(
4889                &self.ranks[rank],
4890                &bank.gate[rank],
4891                expert,
4892                &rank_inputs[rank],
4893                1,
4894            )?);
4895            up_shards.push(run_resident_bank_expert_device(
4896                &self.ranks[rank],
4897                &bank.up[rank],
4898                expert,
4899                &rank_inputs[rank],
4900                1,
4901            )?);
4902        }
4903
4904        // Preserve the established canonical activation program for the first native transport
4905        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
4906        // executes on host. A later device-activation increment must earn its own exactness gate.
4907        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
4908        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
4909        let activated = gate
4910            .iter()
4911            .zip(&up)
4912            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4913            .collect::<Vec<_>>();
4914        debug_assert_eq!(activated.len(), bank.expert_width);
4915
4916        let root_activated = {
4917            let root = &self.ranks[0];
4918            let _main = root.gpu.enter_main()?;
4919            root.htod(&activated)?
4920        };
4921        let mut rank_activated = Vec::with_capacity(self.ranks.len());
4922        for (rank, engine) in self.ranks.iter().enumerate() {
4923            let start = rank * local_out;
4924            let source = root_activated.slice(start..start + local_out);
4925            let local = {
4926                let _main = engine.gpu.enter_main()?;
4927                let mut local = engine.uninit(local_out)?;
4928                engine.stream().memcpy_dtod(&source, &mut local)?;
4929                local
4930            };
4931            rank_activated.push(local);
4932        }
4933
4934        let out_features = bank
4935            .down
4936            .first()
4937            .ok_or("native TP down bank has no ranks")?
4938            .out_features;
4939        let mut reduced = {
4940            let root = &self.ranks[0];
4941            let _main = root.gpu.enter_main()?;
4942            root.htod(&vec![0.0f32; out_features])?
4943        };
4944        let mut remote_partial_keepalive = Vec::new();
4945        for rank in 0..self.ranks.len() {
4946            let down = &bank.down[rank];
4947            let blocks = down
4948                .k_blocks
4949                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
4950            if blocks * FP8_BLOCK != local_out {
4951                return Err(format!(
4952                    "native TP rank {rank} has {blocks} blocks but local activation width is \
4953                     {local_out}"
4954                )
4955                .into());
4956            }
4957            for block in 0..blocks {
4958                let start = block * FP8_BLOCK;
4959                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
4960                let partial = run_resident_bank_expert_block_device(
4961                    &self.ranks[rank],
4962                    down,
4963                    expert,
4964                    block,
4965                    &input_block,
4966                )?;
4967                let root_partial = if rank == 0 {
4968                    partial
4969                } else {
4970                    let root = &self.ranks[0];
4971                    let _main = root.gpu.enter_main()?;
4972                    let mut peer_partial = root.uninit(out_features)?;
4973                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
4974                    remote_partial_keepalive.push(partial);
4975                    peer_partial
4976                };
4977                let next = {
4978                    let root = &self.ranks[0];
4979                    let _main = root.gpu.enter_main()?;
4980                    let mut next = root.uninit(out_features)?;
4981                    root.add(&reduced, &root_partial, &mut next, out_features)?;
4982                    next
4983                };
4984                reduced = next;
4985            }
4986        }
4987        let output = {
4988            let root = &self.ranks[0];
4989            let _main = root.gpu.enter_main()?;
4990            root.dtoh(&reduced)?
4991        };
4992        drop(remote_partial_keepalive);
4993        Ok(output)
4994    }
4995
4996    /// Gather token-major rank-local columns into one canonical root-device matrix.
4997    pub fn gather_native_column_shards_device(
4998        &self,
4999        shards: &[CudaSlice<f32>],
5000        tokens: usize,
5001        local_out: usize,
5002    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5003        let shard_len = tokens
5004            .checked_mul(local_out)
5005            .ok_or("native TP gather shard size overflow")?;
5006        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
5007            return Err("native TP gather shard geometry mismatch".into());
5008        }
5009        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
5010        // the other ranks' streams; without fencing those producers the copy can read a partial
5011        // kernel output.
5012        for engine in &self.ranks[1..] {
5013            let _main = engine.gpu.enter_main()?;
5014            engine.stream().synchronize()?;
5015        }
5016        let root = &self.ranks[0];
5017        let _main = root.gpu.enter_main()?;
5018        let global_out = shards
5019            .len()
5020            .checked_mul(local_out)
5021            .ok_or("native TP gather output width overflow")?;
5022        let gathered_len = tokens
5023            .checked_mul(global_out)
5024            .ok_or("native TP gather output size overflow")?;
5025        let mut gathered = root.uninit(gathered_len)?;
5026        if self.bulk_p2p {
5027            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
5028            if shards.len() > 1 {
5029                let mut staging = root.uninit(shard_len)?;
5030                for (rank, shard) in shards.iter().enumerate().skip(1) {
5031                    root.stream().memcpy_dtod(shard, &mut staging)?;
5032                    root.place_rows_strided(
5033                        &staging,
5034                        &mut gathered,
5035                        local_out,
5036                        tokens,
5037                        global_out,
5038                        rank * local_out,
5039                    )?;
5040                }
5041            }
5042        } else {
5043            for token in 0..tokens {
5044                for (rank, shard) in shards.iter().enumerate() {
5045                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5046                    let start = token * global_out + rank * local_out;
5047                    let mut destination = gathered.slice_mut(start..start + local_out);
5048                    root.stream().memcpy_dtod(&source, &mut destination)?;
5049                }
5050            }
5051        }
5052        Ok(gathered)
5053    }
5054
5055    pub fn gather_native_column_shards(
5056        &self,
5057        shards: &[CudaSlice<f32>],
5058        tokens: usize,
5059        local_out: usize,
5060    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5061        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5062        let root = &self.ranks[0];
5063        let _main = root.gpu.enter_main()?;
5064        root.dtoh(&gathered)
5065    }
5066
5067    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5068        &self.decode_v2
5069    }
5070
5071    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5072    /// return the index of the matching one. Attention geometry varies across the trunk
5073    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5074    /// handful exist per model, never one per layer.
5075    ///
5076    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5077    /// holds per residency class, and only the mirror class has no per-call weight expansion
5078    /// to hide allocation churn behind.
5079    pub(crate) fn decode_v2_ensure(
5080        &self,
5081        e: &Engine,
5082        q_m: &ResidentBf16ColumnParallel,
5083        k_m: &ResidentBf16ColumnParallel,
5084        v_m: &ResidentBf16ColumnParallel,
5085        o_m: &ResidentStepBf16RowParallel,
5086        heads: usize,
5087    ) -> Result<usize, Box<dyn std::error::Error>> {
5088        if self.ranks.len() > 1 && !self.native_p2p {
5089            return Err("step TP decode v2 requires native P2P ranks".into());
5090        }
5091        let ranks = self.ranks.len();
5092        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5093        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5094        // traffic), so bf16 residency is accepted when that door is on.
5095        let fused_door = step_tp_qkv_fused_enabled()?;
5096        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5097            ResidentBf16Weight::F32(_) => true,
5098            ResidentBf16Weight::Bf16(_) => fused_door,
5099        };
5100        for matrix in [q_m, k_m, v_m] {
5101            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5102            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5103                return Err("step TP decode v2 QKV geometry mismatch".into());
5104            }
5105            for rank in &matrix.ranks {
5106                if !arm_ok(&rank.weight) {
5107                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5108                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5109                        .into());
5110                }
5111            }
5112        }
5113        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5114        for blocks in &o_m.ranks {
5115            for block in blocks {
5116                if !arm_ok(&block.weight) {
5117                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5118                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5119                        .into());
5120                }
5121            }
5122        }
5123        if v_m.out_features != k_m.out_features
5124            || o_m.in_features != q_m.out_features
5125            || heads == 0
5126            || heads % ranks != 0
5127        {
5128            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5129        }
5130        let local_q_dim = q_m.out_features / ranks;
5131        let local_kv_dim = k_m.out_features / ranks;
5132        let o_out = o_m.out_features;
5133        let o_block_cols = o_m.canonical_chunk_cols;
5134        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5135        if blocks_per_rank == 0
5136            || o_m
5137                .ranks
5138                .iter()
5139                .any(|blocks| blocks.len() != blocks_per_rank)
5140            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5141        {
5142            return Err("step TP decode v2 O canonical block grid mismatch".into());
5143        }
5144
5145        let mut guard = self
5146            .decode_v2
5147            .lock()
5148            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5149        if let Some(index) = guard.iter().position(|ws| {
5150            ws.local_q_dim == local_q_dim
5151                && ws.local_kv_dim == local_kv_dim
5152                && ws.heads == heads
5153                && ws.o_out == o_out
5154                && ws.o_block_cols == o_block_cols
5155                && ws.blocks_per_rank == blocks_per_rank
5156                && ws.e_device == e.ctx().ordinal()
5157                && ws.q.len() == ranks
5158        }) {
5159            return Ok(index);
5160        }
5161
5162        let mut q_raw = Vec::with_capacity(ranks);
5163        let mut k_raw = Vec::with_capacity(ranks);
5164        let mut v_raw = Vec::with_capacity(ranks);
5165        let mut q = Vec::with_capacity(ranks);
5166        let mut k = Vec::with_capacity(ranks);
5167        let mut pos = Vec::with_capacity(ranks);
5168        let mut gate = Vec::with_capacity(ranks);
5169        let mut attn_out = Vec::with_capacity(ranks);
5170        let mut gated = Vec::with_capacity(ranks);
5171        let mut fuse_ctr = Vec::with_capacity(ranks);
5172        let mut o_partials = Vec::with_capacity(ranks);
5173        let mut ev_rank = Vec::with_capacity(ranks);
5174        let direct_join = oproj_direct_on();
5175        for (rank, engine) in self.ranks.iter().enumerate() {
5176            let _main = engine.gpu.enter_main()?;
5177            q_raw.push(engine.uninit(local_q_dim)?);
5178            k_raw.push(engine.uninit(local_kv_dim)?);
5179            v_raw.push(engine.uninit(local_kv_dim)?);
5180            q.push(engine.uninit(local_q_dim)?);
5181            k.push(engine.uninit(local_kv_dim)?);
5182            pos.push(engine.htod_i32(&[0])?);
5183            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5184            gate.push(engine.uninit(heads / ranks)?);
5185            attn_out.push(engine.uninit(local_q_dim)?);
5186            gated.push(engine.uninit(local_q_dim)?);
5187            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5188            for _ in 0..blocks_per_rank {
5189                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5190                // stores land there over P2P (UVA) and no pull copy is needed.
5191                if direct_join && rank != 0 {
5192                    let root = &self.ranks[0];
5193                    let _root_main = root.gpu.enter_main()?;
5194                    rank_partials.push(root.uninit(o_out)?);
5195                } else {
5196                    rank_partials.push(engine.uninit(o_out)?);
5197                }
5198            }
5199            o_partials.push(rank_partials);
5200            ev_rank.push(engine.ctx().new_event(None)?);
5201        }
5202        let root = &self.ranks[0];
5203        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5204            let _main = root.gpu.enter_main()?;
5205            (
5206                root.uninit(o_out)?,
5207                root.uninit(o_out)?,
5208                root.uninit(o_out)?,
5209                root.htod(&vec![0.0f32; o_out])?,
5210                root.uninit(ranks * local_kv_dim)?,
5211                root.uninit(ranks * local_kv_dim)?,
5212                root.ctx().new_event(None)?,
5213                root.ctx().new_event(None)?,
5214            )
5215        };
5216        let (gate_e, ev_entry) = {
5217            let _main = e.gpu.enter_main()?;
5218            (e.uninit(heads)?, e.ctx().new_event(None)?)
5219        };
5220        let raw_attn_in = Vec::new();
5221        let raw_pos = Vec::new();
5222        guard.push(StepTpDecodeV2Ws {
5223            tcol_q: Vec::new(),
5224            tcol_k: Vec::new(),
5225            tcol_v: Vec::new(),
5226            tcol_g: Vec::new(),
5227            tcol_in: Vec::new(),
5228            tcol_cap: 0,
5229            tcol_gated: Vec::new(),
5230            tcol_opart: Vec::new(),
5231            tcol_opeer: None,
5232            tcol_omix: None,
5233            tcol_ocap: 0,
5234            q_raw,
5235            k_raw,
5236            v_raw,
5237            q,
5238            k,
5239            pos,
5240            fuse_ctr,
5241            gate,
5242            attn_out,
5243            gated,
5244            o_partials,
5245            ev_rank,
5246            peer_partial,
5247            reduce_a,
5248            reduce_b,
5249            zeros,
5250            k_shadow,
5251            v_shadow,
5252            ev_refresh,
5253            ev_oproj,
5254            gate_e,
5255            attn_in: Vec::new(),
5256            h_stage: None,
5257            pos_stage: None,
5258            raw_h_stage: 0,
5259            raw_pos_stage: 0,
5260            raw_attn_in,
5261            raw_pos,
5262            raw_o_partial1: 0,
5263            raw_peer_partial: 0,
5264            raw_k1: 0,
5265            raw_v1: 0,
5266            raw_k_shadow: 0,
5267            raw_v_shadow: 0,
5268            raw_mixed_stage_e: 0,
5269            raw_reduce_a: 0,
5270            raw_shadow_stage_e: (0, 0),
5271            ev_entry,
5272            e_device: e.ctx().ordinal(),
5273            local_q_dim,
5274            local_kv_dim,
5275            heads,
5276            o_out,
5277            o_block_cols,
5278            blocks_per_rank,
5279        });
5280        eprintln!(
5281            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5282             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5283             residency=persistent ordering=evented performance_claim=false"
5284        );
5285        Ok(guard.len() - 1)
5286    }
5287
5288    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5289    /// all into the persistent workspace, ordered by events instead of host syncs.
5290    ///
5291    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5292    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5293    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5294    /// previous layer's outputs was queued on `e`'s stream before this record).
5295    #[allow(clippy::too_many_arguments)]
5296    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5297    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5298    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5299    /// column vs the t=1 kernel by construction.
5300    #[allow(clippy::too_many_arguments)]
5301    pub fn decode_v2_input_qkv_tcol(
5302        &self,
5303        ws_index: usize,
5304        e: &Engine,
5305        h_t: &CudaSlice<f32>,
5306        t: usize,
5307        q_m: &ResidentBf16ColumnParallel,
5308        k_m: &ResidentBf16ColumnParallel,
5309        v_m: &ResidentBf16ColumnParallel,
5310        gate_shards: Option<StepTpGateShards<'_>>,
5311    ) -> Result<(), Box<dyn std::error::Error>> {
5312        let ranks = self.ranks.len();
5313        let mut guard = self
5314            .decode_v2
5315            .lock()
5316            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5317        let ws = guard
5318            .get_mut(ws_index)
5319            .ok_or("step TP decode v2 workspace index out of range")?;
5320        let in_f = q_m.in_features;
5321        if h_t.len() < t * in_f || t == 0 || t > 8 {
5322            return Err("decode_v2_input_qkv_tcol geometry".into());
5323        }
5324        // Lazily arm the slabs to capacity.
5325        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5326            ws.tcol_q.clear();
5327            ws.tcol_k.clear();
5328            ws.tcol_v.clear();
5329            ws.tcol_g.clear();
5330            ws.tcol_in.clear();
5331            for engine in &self.ranks {
5332                let _m = engine.gpu.enter_main()?;
5333                ws.tcol_q.push(engine.uninit(8 * ws.local_q_dim)?);
5334                ws.tcol_k.push(engine.uninit(8 * ws.local_kv_dim)?);
5335                ws.tcol_v.push(engine.uninit(8 * ws.local_kv_dim)?);
5336                ws.tcol_g
5337                    .push(engine.uninit(8 * (ws.heads / ranks).max(1))?);
5338                ws.tcol_in.push(engine.uninit(8 * in_f)?);
5339            }
5340            ws.tcol_cap = 8;
5341        }
5342        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5343        use cudarc::driver::DevicePtr;
5344        let raw_src = {
5345            let _main = e.gpu.enter_main()?;
5346            let stream = e.stream();
5347            let (p, _g) = h_t.device_ptr(&stream);
5348            ws.ev_entry.record(&stream)?;
5349            p as u64
5350        };
5351        for rank in 0..ranks {
5352            let engine = &self.ranks[rank];
5353            let _main = engine.gpu.enter_main()?;
5354            engine.stream().wait(&ws.ev_entry)?;
5355            let raw_dst = {
5356                let stream = engine.stream();
5357                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5358                p as u64
5359            };
5360            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5361            let out_g = match &gate_shards {
5362                Some(_) => ws.heads / ranks,
5363                None => 0,
5364            };
5365            match (
5366                &q_m.ranks[rank].weight,
5367                &k_m.ranks[rank].weight,
5368                &v_m.ranks[rank].weight,
5369            ) {
5370                (
5371                    ResidentBf16Weight::Bf16(wq),
5372                    ResidentBf16Weight::Bf16(wk),
5373                    ResidentBf16Weight::Bf16(wv),
5374                ) => {
5375                    let wg = match &gate_shards {
5376                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5377                        Some(StepTpGateShards::F32(_)) => {
5378                            return Err(
5379                                "tcol verify: gate shard class does not match bf16 QKV".into()
5380                            );
5381                        }
5382                        None => wq,
5383                    };
5384                    let StepTpDecodeV2Ws {
5385                        tcol_q,
5386                        tcol_k,
5387                        tcol_v,
5388                        tcol_g,
5389                        tcol_in,
5390                        local_q_dim,
5391                        local_kv_dim,
5392                        ..
5393                    } = &mut *ws;
5394                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5395                    // column — separates driver bugs from tcol-kernel bugs.
5396                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5397                    let refk = *REFK
5398                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5399                    if refk {
5400                        let lq = *local_q_dim;
5401                        let lkv = *local_kv_dim;
5402                        let mut hrow = engine.uninit(in_f)?;
5403                        let mut qr = engine.uninit(lq)?;
5404                        let mut kr = engine.uninit(lkv)?;
5405                        let mut vr = engine.uninit(lkv)?;
5406                        let mut gr = engine.uninit(out_g.max(1))?;
5407                        for c in 0..t {
5408                            {
5409                                let mut dst = hrow.slice_mut(0..in_f);
5410                                engine.stream().memcpy_dtod(
5411                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5412                                    &mut dst,
5413                                )?;
5414                            }
5415                            engine.matvec_bf16_qkvg_into(
5416                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5417                                lq, lkv, out_g,
5418                            )?;
5419                            let stream = engine.stream();
5420                            {
5421                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5422                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5423                            }
5424                            {
5425                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5426                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5427                            }
5428                            {
5429                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5430                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5431                            }
5432                            if out_g > 0 {
5433                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5434                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5435                            }
5436                        }
5437                    } else {
5438                        engine.matvec_bf16_qkvg_tcol_into(
5439                            wq,
5440                            wk,
5441                            wv,
5442                            wg,
5443                            &tcol_in[rank],
5444                            &mut tcol_q[rank],
5445                            &mut tcol_k[rank],
5446                            &mut tcol_v[rank],
5447                            &mut tcol_g[rank],
5448                            in_f,
5449                            *local_q_dim,
5450                            *local_kv_dim,
5451                            out_g,
5452                            t,
5453                        )?;
5454                    }
5455                }
5456                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5457            }
5458        }
5459        Ok(())
5460    }
5461
5462    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5463    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5464    /// skipped — so it requires the same doors that arm dictate that finish shape.
5465    pub(crate) fn decode_v2_oproj_tcol_eligible(
5466        &self,
5467        ws: &StepTpDecodeV2Ws,
5468        o_m: &ResidentStepBf16RowParallel,
5469    ) -> bool {
5470        self.ranks.len() == 2
5471            && ws.blocks_per_rank == 4
5472            && step_tp_qkv_fused_enabled().unwrap_or(false)
5473            && no_local_shadow_on()
5474            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5475            && o_m
5476                .ranks
5477                .iter()
5478                .flatten()
5479                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5480    }
5481
5482    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
5483    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
5484    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
5485    /// every column afterwards.
5486    pub(crate) fn decode_v2_stash_gated(
5487        &self,
5488        ws: &mut StepTpDecodeV2Ws,
5489        e: &Engine,
5490        col: usize,
5491    ) -> Result<(), Box<dyn std::error::Error>> {
5492        let ranks = self.ranks.len();
5493        if col >= 8 {
5494            return Err("decode_v2_stash_gated column out of range".into());
5495        }
5496        let lq = ws.local_q_dim;
5497        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
5498            ws.tcol_gated.clear();
5499            ws.tcol_opart.clear();
5500            for engine in &self.ranks {
5501                let _m = engine.gpu.enter_main()?;
5502                ws.tcol_gated.push(engine.uninit(8 * lq)?);
5503                ws.tcol_opart.push(engine.uninit(8 * ws.o_out)?);
5504            }
5505            let root = &self.ranks[0];
5506            let _m = root.gpu.enter_main()?;
5507            ws.tcol_opeer = Some(root.uninit(8 * ws.o_out)?);
5508            ws.tcol_omix = Some(root.uninit(8 * ws.o_out)?);
5509            ws.tcol_ocap = 8;
5510        }
5511        for rank in 0..ranks {
5512            let engine = &self.ranks[rank];
5513            let _main = engine.gpu.enter_main()?;
5514            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
5515            engine
5516                .stream()
5517                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
5518            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
5519            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
5520            // Record each rank here and make e wait — same protection, no o_proj work.
5521            ws.ev_rank[rank].record(&engine.stream())?;
5522        }
5523        {
5524            let _main = e.gpu.enter_main()?;
5525            for ev in ws.ev_rank.iter() {
5526                e.stream().wait(ev)?;
5527            }
5528        }
5529        Ok(())
5530    }
5531
5532    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
5533    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
5534    /// partial slab, one elementwise slab add on the root (independent elements — each
5535    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
5536    /// lands on `e`. Returns [t, o_out] on the model engine.
5537    pub(crate) fn decode_v2_oproj_tcol(
5538        &self,
5539        ws_index: usize,
5540        e: &Engine,
5541        o_m: &ResidentStepBf16RowParallel,
5542        t: usize,
5543    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5544        let ranks = self.ranks.len();
5545        let mut guard = self
5546            .decode_v2
5547            .lock()
5548            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5549        let ws = guard
5550            .get_mut(ws_index)
5551            .ok_or("step TP decode v2 workspace index out of range")?;
5552        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 8 || ws.tcol_ocap < t {
5553            return Err("decode_v2_oproj_tcol geometry".into());
5554        }
5555        for rank in 0..ranks {
5556            let engine = &self.ranks[rank];
5557            let _main = engine.gpu.enter_main()?;
5558            let mut weights = Vec::with_capacity(4);
5559            for block in 0..4 {
5560                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
5561                    return Err("tcol o_proj requires bf16-resident O blocks".into());
5562                };
5563                weights.push(weight);
5564            }
5565            {
5566                let StepTpDecodeV2Ws {
5567                    tcol_gated,
5568                    tcol_opart,
5569                    local_q_dim,
5570                    o_block_cols,
5571                    o_out,
5572                    ..
5573                } = &mut *ws;
5574                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
5575                // kernel per column — separates choreography bugs from tcol-kernel bugs.
5576                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5577                let refk = *REFK
5578                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
5579                if refk {
5580                    let lq = *local_q_dim;
5581                    let mut xr = engine.uninit(lq)?;
5582                    let mut yr = engine.uninit(*o_out)?;
5583                    for c in 0..t {
5584                        {
5585                            let mut dst = xr.slice_mut(0..lq);
5586                            engine.stream().memcpy_dtod(
5587                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
5588                                &mut dst,
5589                            )?;
5590                        }
5591                        engine.matvec_bf16_b4_into(
5592                            [weights[0], weights[1], weights[2], weights[3]],
5593                            &xr,
5594                            &mut yr,
5595                            *o_block_cols,
5596                            *o_out,
5597                        )?;
5598                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
5599                        engine
5600                            .stream()
5601                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
5602                    }
5603                } else {
5604                    engine.matvec_bf16_b4_tcol_into(
5605                        [weights[0], weights[1], weights[2], weights[3]],
5606                        &tcol_gated[rank],
5607                        &mut tcol_opart[rank],
5608                        *o_block_cols,
5609                        *o_out,
5610                        t,
5611                    )?;
5612                }
5613            }
5614            if rank != 0 {
5615                ws.ev_rank[rank].record(&engine.stream())?;
5616            }
5617        }
5618        let root = &self.ranks[0];
5619        {
5620            let _main = root.gpu.enter_main()?;
5621            for ev in ws.ev_rank.iter().skip(1) {
5622                root.stream().wait(ev)?;
5623            }
5624            {
5625                let StepTpDecodeV2Ws {
5626                    tcol_opart,
5627                    tcol_opeer,
5628                    tcol_omix,
5629                    o_out,
5630                    ..
5631                } = &mut *ws;
5632                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
5633                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
5634                {
5635                    let mut dst = opeer.slice_mut(0..t * *o_out);
5636                    root.stream()
5637                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
5638                }
5639                // Elementwise over the whole slab: per element identical to the per-column
5640                // direct-join add (independent lanes, same operand values).
5641                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
5642            }
5643            ws.ev_oproj.record(&root.stream())?;
5644        }
5645        let _main = e.gpu.enter_main()?;
5646        e.stream().wait(&ws.ev_oproj)?;
5647        let mut out = e.uninit(t * ws.o_out)?;
5648        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
5649        e.stream().memcpy_dtod(
5650            &omix.slice(0..t * ws.o_out),
5651            &mut out.slice_mut(0..t * ws.o_out),
5652        )?;
5653        Ok(out)
5654    }
5655
5656    pub(crate) fn decode_v2_input_qkv(
5657        &self,
5658        ws: &mut StepTpDecodeV2Ws,
5659        e: &Engine,
5660        h: &CudaSlice<f32>,
5661        pos_d: &CudaSlice<i32>,
5662        gate_raw: Option<&CudaSlice<f32>>,
5663        gate_shards: Option<StepTpGateShards<'_>>,
5664        decode_input: &mut ResidentReplicatedDeviceRows,
5665        q_m: &ResidentBf16ColumnParallel,
5666        k_m: &ResidentBf16ColumnParallel,
5667        v_m: &ResidentBf16ColumnParallel,
5668        q_norm: &[CudaSlice<f32>],
5669        k_norm: &[CudaSlice<f32>],
5670        head_dim: usize,
5671        n_rot: usize,
5672        rope_base: f32,
5673        rope_freqs: &[Option<&CudaSlice<f32>>],
5674        rms_eps: f32,
5675        defer_norm_rope: bool,
5676        tcol_col: Option<usize>,
5677    ) -> Result<(), Box<dyn std::error::Error>> {
5678        let ranks = self.ranks.len();
5679        validate_replicated_device_rows(&self.ranks, decode_input)?;
5680        if decode_input.tokens != 1
5681            || decode_input.width != q_m.in_features
5682            || pos_d.len() != 1
5683            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
5684            || gate_raw.is_none() != gate_shards.is_some()
5685            || gate_shards.as_ref().is_some_and(|shards| match shards {
5686                StepTpGateShards::F32(shards) => shards.len() != ranks,
5687                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
5688            })
5689            || q_norm.len() != ranks
5690            || k_norm.len() != ranks
5691            || rope_freqs.len() != ranks
5692            || e.ctx().ordinal() != ws.e_device
5693        {
5694            return Err("step TP decode v2 input geometry mismatch".into());
5695        }
5696
5697        let qkv_fused = step_tp_qkv_fused_enabled()?;
5698        if gate_shards.is_some() && !qkv_fused {
5699            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
5700        }
5701        let values = decode_input.width;
5702        if h.len() != values {
5703            return Err(format!(
5704                "step TP decode v2 hidden width {} != replicated width {values}",
5705                h.len()
5706            )
5707            .into());
5708        }
5709
5710        if qkv_fused {
5711            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
5712            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
5713            // from the stages on its own stream — exactly the shape graph capture wraps.
5714            if ws.h_stage.is_none() {
5715                use cudarc::driver::DevicePtr;
5716                let _main = e.gpu.enter_main()?;
5717                let h_stage = e.uninit(values)?;
5718                let pos_stage = e.htod_i32(&[0])?;
5719                {
5720                    let stream = e.stream();
5721                    let (hp, _g0) = h_stage.device_ptr(&stream);
5722                    let (pp, _g1) = pos_stage.device_ptr(&stream);
5723                    ws.raw_h_stage = hp as u64;
5724                    ws.raw_pos_stage = pp as u64;
5725                }
5726                ws.h_stage = Some(h_stage);
5727                ws.pos_stage = Some(pos_stage);
5728                for rank in 0..ranks {
5729                    use cudarc::driver::DevicePtr;
5730                    let engine = &self.ranks[rank];
5731                    let _rmain = engine.gpu.enter_main()?;
5732                    let attn_in = engine.uninit(values)?;
5733                    let (dp, pp) = {
5734                        let stream = engine.stream();
5735                        let (dp, _g2) = attn_in.device_ptr(&stream);
5736                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
5737                        (dp as u64, pp as u64)
5738                    };
5739                    ws.raw_attn_in.push(dp);
5740                    ws.raw_pos.push(pp);
5741                    ws.attn_in.push(attn_in);
5742                }
5743                {
5744                    use cudarc::driver::DevicePtr;
5745                    let root = &self.ranks[0];
5746                    let _rmain = root.gpu.enter_main()?;
5747                    let stream = root.stream();
5748                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
5749                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
5750                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
5751                    ws.raw_peer_partial = a as u64;
5752                    ws.raw_k_shadow = b as u64;
5753                    ws.raw_v_shadow = c as u64;
5754                }
5755                {
5756                    use cudarc::driver::DevicePtr;
5757                    let rank1 = &self.ranks[1];
5758                    let _rmain = rank1.gpu.enter_main()?;
5759                    let stream = rank1.stream();
5760                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
5761                    let (b, _g) = ws.k[1].device_ptr(&stream);
5762                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
5763                    ws.raw_o_partial1 = a as u64;
5764                    ws.raw_k1 = b as u64;
5765                    ws.raw_v1 = c as u64;
5766                }
5767            }
5768            {
5769                let _main = e.gpu.enter_main()?;
5770                {
5771                    // (Always staged: a tcol column below the dcw floor falls back to the
5772                    // normal fused arm, which reads h through this stage.)
5773                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
5774                    let mut dst = h_stage.slice_mut(0..values);
5775                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
5776                }
5777                {
5778                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
5779                    let mut dst = pos_stage.slice_mut(0..1);
5780                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
5781                }
5782                ws.ev_entry.record(&e.stream())?;
5783            }
5784            for rank in 0..ranks {
5785                let engine = &self.ranks[rank];
5786                let _main = engine.gpu.enter_main()?;
5787                engine.stream().wait(&ws.ev_entry)?;
5788            }
5789        } else {
5790            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
5791            {
5792                let _main = e.gpu.enter_main()?;
5793                if let Some(gate_raw) = gate_raw {
5794                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
5795                    e.stream()
5796                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
5797                }
5798                ws.ev_entry.record(&e.stream())?;
5799            }
5800            {
5801                let root = &self.ranks[0];
5802                let _main = root.gpu.enter_main()?;
5803                root.stream().wait(&ws.ev_entry)?;
5804                let mut destination = decode_input.ranks[0].slice_mut(0..values);
5805                root.stream()
5806                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
5807                ws.ev_refresh.record(&root.stream())?;
5808            }
5809            for rank in 1..ranks {
5810                let engine = &self.ranks[rank];
5811                let _main = engine.gpu.enter_main()?;
5812                engine.stream().wait(&ws.ev_refresh)?;
5813                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
5814                let mut destination = peer_rows[0].slice_mut(0..values);
5815                engine
5816                    .stream()
5817                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
5818            }
5819        }
5820        for rank in 0..ranks {
5821            self.decode_v2_input_qkv_rank(
5822                ws,
5823                pos_d,
5824                decode_input,
5825                q_m,
5826                k_m,
5827                v_m,
5828                q_norm,
5829                k_norm,
5830                head_dim,
5831                n_rot,
5832                rope_base,
5833                rope_freqs,
5834                rms_eps,
5835                gate_shards.as_ref(),
5836                qkv_fused,
5837                defer_norm_rope,
5838                rank,
5839                tcol_col,
5840            )?;
5841        }
5842        Ok(())
5843    }
5844
5845    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
5846    /// per-device issue unit the whole-token graph captures on that rank's stream.
5847    #[allow(clippy::too_many_arguments)]
5848    pub(crate) fn decode_v2_input_qkv_rank(
5849        &self,
5850        ws: &mut StepTpDecodeV2Ws,
5851        pos_d: &CudaSlice<i32>,
5852        decode_input: &mut ResidentReplicatedDeviceRows,
5853        q_m: &ResidentBf16ColumnParallel,
5854        k_m: &ResidentBf16ColumnParallel,
5855        v_m: &ResidentBf16ColumnParallel,
5856        q_norm: &[CudaSlice<f32>],
5857        k_norm: &[CudaSlice<f32>],
5858        head_dim: usize,
5859        n_rot: usize,
5860        rope_base: f32,
5861        rope_freqs: &[Option<&CudaSlice<f32>>],
5862        rms_eps: f32,
5863        gate_shards: Option<&StepTpGateShards<'_>>,
5864        qkv_fused: bool,
5865        defer_norm_rope: bool,
5866        rank: usize,
5867        tcol_col: Option<usize>,
5868    ) -> Result<(), Box<dyn std::error::Error>> {
5869        let ranks = self.ranks.len();
5870        let local_heads = ws.local_q_dim / head_dim;
5871        let local_kv_heads = ws.local_kv_dim / head_dim;
5872        let engine = &self.ranks[rank];
5873        let _main = engine.gpu.enter_main()?;
5874        let ws_e_device = ws.e_device;
5875        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
5876        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
5877        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
5878        // below exactly as in the t=1 program.
5879        if qkv_fused && tcol_col.is_some() {
5880            let c = tcol_col.expect("checked");
5881            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
5882                return Err("tcol select without precompute".into());
5883            }
5884            // The select skips the matvec but NOT the position: rope/append below still
5885            // read this rank's pos buffer, which only the (skipped) stage path fills for
5886            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
5887            if engine.ctx().ordinal() != ws_e_device {
5888                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
5889            }
5890            let StepTpDecodeV2Ws {
5891                tcol_q,
5892                tcol_k,
5893                tcol_v,
5894                tcol_g,
5895                q_raw,
5896                k_raw,
5897                v_raw,
5898                gate,
5899                local_q_dim,
5900                local_kv_dim,
5901                heads,
5902                ..
5903            } = &mut *ws;
5904            let lg = *heads / ranks;
5905            let stream = engine.stream();
5906            {
5907                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
5908                stream.memcpy_dtod(
5909                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
5910                    &mut dst,
5911                )?;
5912            }
5913            {
5914                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
5915                stream.memcpy_dtod(
5916                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
5917                    &mut dst,
5918                )?;
5919            }
5920            {
5921                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
5922                stream.memcpy_dtod(
5923                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
5924                    &mut dst,
5925                )?;
5926            }
5927            if lg > 0 {
5928                let mut dst = gate[rank].slice_mut(0..lg);
5929                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
5930            }
5931            if !defer_norm_rope {
5932                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
5933                // fall through and recompute this column's QKV from the REAL h row — the
5934                // caller always passes it. The slab copies above are dead stores.
5935            } else {
5936                return Ok(());
5937            }
5938        }
5939        if qkv_fused {
5940            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
5941            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
5942            // SHARING e's device reads the stages directly — same context (probed), ordering
5943            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
5944            let same_dev = engine.ctx().ordinal() == ws.e_device;
5945            if !same_dev {
5946                raw_copy_bytes(
5947                    ws.raw_attn_in[rank],
5948                    ws.raw_h_stage,
5949                    q_m.in_features * 4,
5950                    engine,
5951                )?;
5952                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
5953            }
5954            let StepTpDecodeV2Ws {
5955                q_raw,
5956                k_raw,
5957                v_raw,
5958                gate,
5959                gate_e,
5960                attn_in,
5961                h_stage,
5962                heads,
5963                local_q_dim,
5964                local_kv_dim,
5965                ..
5966            } = &mut *ws;
5967            let input_ref: &CudaSlice<f32> = if same_dev {
5968                h_stage
5969                    .as_ref()
5970                    .ok_or("step TP decode v2 stage not armed")?
5971            } else {
5972                &attn_in[rank]
5973            };
5974            match (
5975                &q_m.ranks[rank].weight,
5976                &k_m.ranks[rank].weight,
5977                &v_m.ranks[rank].weight,
5978            ) {
5979                (
5980                    ResidentBf16Weight::F32(wq),
5981                    ResidentBf16Weight::F32(wk),
5982                    ResidentBf16Weight::F32(wv),
5983                ) => {
5984                    let (wg, out_g) = match &gate_shards {
5985                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
5986                        Some(StepTpGateShards::Bf16(_)) => {
5987                            return Err("step TP decode v2 gate shard class does not \
5988                                            match the F32 projections"
5989                                .into());
5990                        }
5991                        // out_g = 0: the kernel never reads wg; any resident buffer works.
5992                        None => (&*gate_e, 0),
5993                    };
5994                    engine.matvec_f32_qkv_into(
5995                        wq,
5996                        wk,
5997                        wv,
5998                        wg,
5999                        input_ref,
6000                        &mut q_raw[rank],
6001                        &mut k_raw[rank],
6002                        &mut v_raw[rank],
6003                        &mut gate[rank],
6004                        q_m.in_features,
6005                        *local_q_dim,
6006                        *local_kv_dim,
6007                        out_g,
6008                    )?;
6009                }
6010                (
6011                    ResidentBf16Weight::Bf16(wq),
6012                    ResidentBf16Weight::Bf16(wk),
6013                    ResidentBf16Weight::Bf16(wv),
6014                ) => {
6015                    let (wg, out_g) = match &gate_shards {
6016                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
6017                        Some(StepTpGateShards::F32(_)) => {
6018                            return Err("step TP decode v2 gate shard class does not \
6019                                            match the bf16 projections"
6020                                .into());
6021                        }
6022                        None => (wq, 0),
6023                    };
6024                    engine.matvec_bf16_qkvg_into(
6025                        wq,
6026                        wk,
6027                        wv,
6028                        wg,
6029                        input_ref,
6030                        &mut q_raw[rank],
6031                        &mut k_raw[rank],
6032                        &mut v_raw[rank],
6033                        &mut gate[rank],
6034                        q_m.in_features,
6035                        *local_q_dim,
6036                        *local_kv_dim,
6037                        out_g,
6038                    )?;
6039                }
6040                _ => {
6041                    return Err("step TP decode v2 QKV projections mix residency classes".into());
6042                }
6043            }
6044        } else {
6045            for (matrix, local_out, raw) in [
6046                (q_m, ws.local_q_dim, &mut ws.q_raw),
6047                (k_m, ws.local_kv_dim, &mut ws.k_raw),
6048                (v_m, ws.local_kv_dim, &mut ws.v_raw),
6049            ] {
6050                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
6051                    return Err("step TP decode v2 lost its F32 projection residency".into());
6052                };
6053                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
6054                engine.linear_f32_resident_canonical_rows_t1_into(
6055                    &decode_input.ranks[rank],
6056                    values_w,
6057                    &mut raw[rank],
6058                    matrix.in_features,
6059                    local_out,
6060                    chunk_rows,
6061                )?;
6062            }
6063        }
6064        if qkv_fused && defer_norm_rope {
6065            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
6066        } else if qkv_fused {
6067            // Fused norm+rope: one launch; the position comes from the rank-local staged
6068            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
6069            let StepTpDecodeV2Ws {
6070                q_raw,
6071                k_raw,
6072                q,
6073                k,
6074                pos,
6075                pos_stage,
6076                ..
6077            } = &mut *ws;
6078            let same_dev = engine.ctx().ordinal() == ws_e_device;
6079            let pos_ref: &CudaSlice<i32> = if same_dev {
6080                pos_stage
6081                    .as_ref()
6082                    .ok_or("step TP decode v2 pos stage not armed")?
6083            } else {
6084                &pos[rank]
6085            };
6086            engine.qk_norm_rope_into(
6087                &q_raw[rank],
6088                &k_raw[rank],
6089                &q_norm[rank],
6090                &k_norm[rank],
6091                &mut q[rank],
6092                &mut k[rank],
6093                pos_ref,
6094                head_dim,
6095                n_rot,
6096                local_heads,
6097                local_kv_heads,
6098                rms_eps,
6099                rope_base,
6100                1.0,
6101                rope_freqs[rank],
6102            )?;
6103        } else {
6104            engine.rms_norm(
6105                &ws.q_raw[rank],
6106                &q_norm[rank],
6107                &mut ws.q[rank],
6108                head_dim,
6109                local_heads,
6110                rms_eps,
6111            )?;
6112            engine.rms_norm(
6113                &ws.k_raw[rank],
6114                &k_norm[rank],
6115                &mut ws.k[rank],
6116                head_dim,
6117                local_kv_heads,
6118                rms_eps,
6119            )?;
6120            {
6121                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
6122                engine
6123                    .stream()
6124                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
6125            }
6126            engine.rope_neox2(
6127                &mut ws.q[rank],
6128                &mut ws.k[rank],
6129                &ws.pos[rank],
6130                head_dim,
6131                n_rot,
6132                local_heads,
6133                local_kv_heads,
6134                1,
6135                rope_base,
6136                1.0,
6137                rope_freqs[rank],
6138            )?;
6139        }
6140        if gate_shards.is_none() {
6141            let gate_start = rank * (ws.heads / ranks);
6142            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
6143            engine.stream().memcpy_dtod(
6144                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
6145                &mut gate_dst,
6146            )?;
6147        }
6148        Ok(())
6149    }
6150
6151    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
6152    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
6153    /// eager caller; graphs order via parent edges instead).
6154    pub(crate) fn decode_v2_finish_rank_partial(
6155        &self,
6156        ws: &mut StepTpDecodeV2Ws,
6157        o_m: &ResidentStepBf16RowParallel,
6158        o_fused: bool,
6159        rank: usize,
6160    ) -> Result<(), Box<dyn std::error::Error>> {
6161        let engine = &self.ranks[rank];
6162        let _main = engine.gpu.enter_main()?;
6163        if o_fused {
6164            let StepTpDecodeV2Ws {
6165                gated,
6166                o_partials,
6167                o_block_cols,
6168                o_out,
6169                ..
6170            } = &mut *ws;
6171            let all_f32 = o_m.ranks[rank]
6172                .iter()
6173                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
6174            if all_f32 {
6175                let mut weights = Vec::with_capacity(4);
6176                for block in 0..4 {
6177                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6178                        unreachable!("all_f32 checked above");
6179                    };
6180                    weights.push(weight);
6181                }
6182                engine.matvec_f32_b4_into(
6183                    [weights[0], weights[1], weights[2], weights[3]],
6184                    &gated[rank],
6185                    &mut o_partials[rank][0],
6186                    *o_block_cols,
6187                    *o_out,
6188                )?;
6189            } else {
6190                let mut weights = Vec::with_capacity(4);
6191                for block in 0..4 {
6192                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6193                        return Err("step TP decode v2 O projections mix residency classes".into());
6194                    };
6195                    weights.push(weight);
6196                }
6197                engine.matvec_bf16_b4_into(
6198                    [weights[0], weights[1], weights[2], weights[3]],
6199                    &gated[rank],
6200                    &mut o_partials[rank][0],
6201                    *o_block_cols,
6202                    *o_out,
6203                )?;
6204            }
6205        } else {
6206            for block in 0..ws.blocks_per_rank {
6207                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6208                    return Err("step TP decode v2 lost its F32 O residency".into());
6209                };
6210                let x =
6211                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
6212                let w = weight.slice(0..weight.len());
6213                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
6214                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
6215            }
6216        }
6217        Ok(())
6218    }
6219
6220    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
6221    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
6222    ///
6223    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
6224    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
6225    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
6226    /// rank's blocks, one `add` per block.
6227    pub(crate) fn decode_v2_finish(
6228        &self,
6229        ws: &mut StepTpDecodeV2Ws,
6230        e: &Engine,
6231        o_m: &ResidentStepBf16RowParallel,
6232    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6233        let ranks = self.ranks.len();
6234        if e.ctx().ordinal() != ws.e_device {
6235            return Err("step TP decode v2 finish engine changed".into());
6236        }
6237        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
6238        // (in-order canonical block accumulation per element) and a single peer-copy + add on
6239        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
6240        // numeric-class door and gate as the fused QKV projection.
6241        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
6242
6243        // Per-rank O block partials on the owning rank's stream (serial after the attention
6244        // kernels the driver queued there), then the rank-done event for root's peer reads.
6245        for rank in 0..ranks {
6246            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
6247            if rank == 0 {
6248                // root == rank0: its own stream order covers the partial; only peers need
6249                // the record/wait pair (host-op diet, matches the routes-arm skip).
6250                continue;
6251            }
6252            let engine = &self.ranks[rank];
6253            let _main = engine.gpu.enter_main()?;
6254            ws.ev_rank[rank].record(&engine.stream())?;
6255        }
6256
6257        // Root reduce in canonical order + shadow gathers, all on the root stream.
6258        let root = &self.ranks[0];
6259        #[allow(unused_assignments)]
6260        let mut final_in_a = false;
6261        {
6262            let _main = root.gpu.enter_main()?;
6263            for ev in ws.ev_rank.iter().skip(1) {
6264                root.stream().wait(ev)?;
6265            }
6266            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
6267                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
6268                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
6269                // partial is root-stream-ordered — record ONE event and let the model
6270                // engine do the single add itself, straight into its own output row.
6271                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
6272                ws.ev_oproj.record(&root.stream())?;
6273                let _main = e.gpu.enter_main()?;
6274                e.stream().wait(&ws.ev_oproj)?;
6275                let mut output = e.uninit(ws.o_out)?;
6276                if oproj_tail_on() && oproj_tail_eligible() {
6277                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
6278                    // only the arithmetic moves). `output` is returned unwritten.
6279                    use cudarc::driver::DevicePtr;
6280                    let stream = e.stream();
6281                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
6282                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
6283                    set_oproj_tail((p0 as u64, p1 as u64));
6284                    return Ok(output);
6285                }
6286                e.add(
6287                    &ws.o_partials[0][0],
6288                    &ws.o_partials[1][0],
6289                    &mut output,
6290                    ws.o_out,
6291                )?;
6292                return Ok(output);
6293            }
6294            if o_fused {
6295                self.decode_v2_finish_root_fused(ws)?;
6296                ws.ev_oproj.record(&root.stream())?;
6297                let _main = e.gpu.enter_main()?;
6298                e.stream().wait(&ws.ev_oproj)?;
6299                let mut output = e.uninit(ws.o_out)?;
6300                e.stream().memcpy_dtod(
6301                    &ws.reduce_a.slice(0..ws.o_out),
6302                    &mut output.slice_mut(0..ws.o_out),
6303                )?;
6304                return Ok(output);
6305            }
6306            let mut first = true;
6307            let mut current_is_a = false;
6308            for rank in 0..ranks {
6309                for block in 0..ws.blocks_per_rank {
6310                    let use_peer = rank != 0;
6311                    if use_peer {
6312                        root.stream()
6313                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
6314                    }
6315                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
6316                    match (first, current_is_a, use_peer) {
6317                        (true, _, true) => {
6318                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
6319                        }
6320                        (true, _, false) => root.add(
6321                            &ws.zeros,
6322                            &ws.o_partials[0][block],
6323                            &mut ws.reduce_a,
6324                            ws.o_out,
6325                        )?,
6326                        (false, true, true) => {
6327                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
6328                        }
6329                        (false, true, false) => root.add(
6330                            &ws.reduce_a,
6331                            &ws.o_partials[0][block],
6332                            &mut ws.reduce_b,
6333                            ws.o_out,
6334                        )?,
6335                        (false, false, true) => {
6336                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
6337                        }
6338                        (false, false, false) => root.add(
6339                            &ws.reduce_b,
6340                            &ws.o_partials[0][block],
6341                            &mut ws.reduce_a,
6342                            ws.o_out,
6343                        )?,
6344                    }
6345                    current_is_a = first || !current_is_a;
6346                    first = false;
6347                }
6348            }
6349            final_in_a = current_is_a;
6350
6351            for rank in 0..ranks {
6352                let start = rank * ws.local_kv_dim;
6353                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
6354                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
6355                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
6356                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
6357            }
6358            ws.ev_oproj.record(&root.stream())?;
6359        }
6360
6361        // Model-engine output: e waits the root event, then copies the reduced row into a
6362        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
6363        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
6364        let _main = e.gpu.enter_main()?;
6365        e.stream().wait(&ws.ev_oproj)?;
6366        let mut output = e.uninit(ws.o_out)?;
6367        let source = if final_in_a {
6368            &ws.reduce_a
6369        } else {
6370            &ws.reduce_b
6371        };
6372        e.stream().memcpy_dtod(
6373            &source.slice(0..ws.o_out),
6374            &mut output.slice_mut(0..ws.o_out),
6375        )?;
6376        Ok(output)
6377    }
6378
6379    pub fn run_routed_experts(
6380        &self,
6381        experts: &ResidentExpertParallel,
6382        input: &[f32],
6383        tokens: usize,
6384        selected: &[usize],
6385        route_weights: &[f32],
6386        experts_per_token: usize,
6387        activation_limit: Option<f32>,
6388    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6389        validate_step_expert_activation_limit(activation_limit)?;
6390        validate_ep_residency(&self.ranks, experts)?;
6391        validate_activations(input, tokens, experts.input_width)?;
6392        let pairs = tokens
6393            .checked_mul(experts_per_token)
6394            .ok_or("EP route count overflow")?;
6395        if selected.len() != pairs || route_weights.len() != pairs {
6396            return Err(format!(
6397                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
6398                 {experts_per_token} ({pairs})",
6399                selected.len(),
6400                route_weights.len(),
6401            )
6402            .into());
6403        }
6404        if !route_weights.iter().all(|weight| weight.is_finite()) {
6405            return Err("EP route weights contain a non-finite value".into());
6406        }
6407        if self.native_p2p {
6408            return self.run_routed_experts_native(
6409                experts,
6410                input,
6411                tokens,
6412                selected,
6413                route_weights,
6414                experts_per_token,
6415                activation_limit,
6416            );
6417        }
6418
6419        let mut output = vec![0.0f32; tokens * experts.input_width];
6420        let per_rank = experts.expert_count / experts.ranks.len();
6421        for token in 0..tokens {
6422            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6423            for slot in 0..experts_per_token {
6424                let pair = token * experts_per_token + slot;
6425                let expert = selected[pair];
6426                if expert >= experts.expert_count {
6427                    return Err(format!(
6428                        "EP selected expert {expert} outside 0..{}",
6429                        experts.expert_count
6430                    )
6431                    .into());
6432                }
6433                let owner = expert / per_rank;
6434                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6435                let rank = &experts.ranks[owner];
6436                let engine = &self.ranks[owner];
6437                let gate =
6438                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
6439                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
6440                let activated: Vec<f32> = gate
6441                    .iter()
6442                    .zip(&up)
6443                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
6444                    .collect();
6445                debug_assert_eq!(activated.len(), experts.expert_width);
6446                let down =
6447                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
6448                let weight = route_weights[pair];
6449                for (sum, value) in output
6450                    [token * experts.input_width..(token + 1) * experts.input_width]
6451                    .iter_mut()
6452                    .zip(down)
6453                {
6454                    *sum += weight * value;
6455                }
6456            }
6457        }
6458        Ok(output)
6459    }
6460
6461    fn run_routed_experts_native(
6462        &self,
6463        experts: &ResidentExpertParallel,
6464        input: &[f32],
6465        tokens: usize,
6466        selected: &[usize],
6467        route_weights: &[f32],
6468        experts_per_token: usize,
6469        activation_limit: Option<f32>,
6470    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6471        if !self.native_p2p || self.ranks.len() < 2 {
6472            return Err("native EP execution requires at least two P2P ranks".into());
6473        }
6474        if self.ep_device_arithmetic {
6475            return self.run_routed_experts_native_device(
6476                experts,
6477                input,
6478                tokens,
6479                selected,
6480                route_weights,
6481                experts_per_token,
6482                activation_limit,
6483            );
6484        }
6485        let mut output = vec![0.0f32; tokens * experts.input_width];
6486        let per_rank = experts.expert_count / experts.ranks.len();
6487        for token in 0..tokens {
6488            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6489            let mut rank_inputs = (0..self.ranks.len())
6490                .map(|_| None)
6491                .collect::<Vec<Option<CudaSlice<f32>>>>();
6492            rank_inputs[0] = Some({
6493                let root = &self.ranks[0];
6494                let _main = root.gpu.enter_main()?;
6495                root.htod(input_row)?
6496            });
6497
6498            for slot in 0..experts_per_token {
6499                let pair = token * experts_per_token + slot;
6500                let expert = selected[pair];
6501                if expert >= experts.expert_count {
6502                    return Err(format!(
6503                        "EP selected expert {expert} outside 0..{}",
6504                        experts.expert_count
6505                    )
6506                    .into());
6507                }
6508                let owner = expert / per_rank;
6509                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6510                if rank_inputs[owner].is_none() {
6511                    let peer_input = {
6512                        let root_input = rank_inputs[0]
6513                            .as_ref()
6514                            .ok_or("native EP lost its root input")?;
6515                        let engine = &self.ranks[owner];
6516                        let _main = engine.gpu.enter_main()?;
6517                        let mut peer_input = engine.uninit(experts.input_width)?;
6518                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
6519                        peer_input
6520                    };
6521                    rank_inputs[owner] = Some(peer_input);
6522                }
6523
6524                let rank = &experts.ranks[owner];
6525                let engine = &self.ranks[owner];
6526                let owner_input = rank_inputs[owner]
6527                    .as_ref()
6528                    .ok_or("native EP owner input is absent after dispatch")?;
6529                let gate = run_resident_bank_expert_device(
6530                    engine,
6531                    &rank.gate,
6532                    local_expert,
6533                    owner_input,
6534                    1,
6535                )?;
6536                let up = run_resident_bank_expert_device(
6537                    engine,
6538                    &rank.up,
6539                    local_expert,
6540                    owner_input,
6541                    1,
6542                )?;
6543                let (gate, up) = {
6544                    let _main = engine.gpu.enter_main()?;
6545                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
6546                };
6547                let activated = gate
6548                    .iter()
6549                    .zip(&up)
6550                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
6551                    .collect::<Vec<_>>();
6552                debug_assert_eq!(activated.len(), experts.expert_width);
6553                let activated = {
6554                    let _main = engine.gpu.enter_main()?;
6555                    engine.htod(&activated)?
6556                };
6557                let down = run_resident_bank_expert_device(
6558                    engine,
6559                    &rank.down,
6560                    local_expert,
6561                    &activated,
6562                    1,
6563                )?;
6564                let down = if owner == 0 {
6565                    let _main = engine.gpu.enter_main()?;
6566                    engine.dtoh(&down)?
6567                } else {
6568                    let root = &self.ranks[0];
6569                    let _main = root.gpu.enter_main()?;
6570                    let mut root_down = root.uninit(experts.input_width)?;
6571                    root.stream().memcpy_dtod(&down, &mut root_down)?;
6572                    root.dtoh(&root_down)?
6573                };
6574                let weight = route_weights[pair];
6575                for (sum, value) in output
6576                    [token * experts.input_width..(token + 1) * experts.input_width]
6577                    .iter_mut()
6578                    .zip(down)
6579                {
6580                    *sum += weight * value;
6581                }
6582            }
6583        }
6584        Ok(output)
6585    }
6586
6587    fn run_routed_experts_native_device(
6588        &self,
6589        experts: &ResidentExpertParallel,
6590        input: &[f32],
6591        tokens: usize,
6592        selected: &[usize],
6593        route_weights: &[f32],
6594        experts_per_token: usize,
6595        activation_limit: Option<f32>,
6596    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6597        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
6598            return Err(
6599                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
6600            );
6601        }
6602        let mut output = Vec::with_capacity(tokens * experts.input_width);
6603        let per_rank = experts.expert_count / experts.ranks.len();
6604        let root = &self.ranks[0];
6605        for token in 0..tokens {
6606            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
6607            let mut rank_inputs = (0..self.ranks.len())
6608                .map(|_| None)
6609                .collect::<Vec<Option<CudaSlice<f32>>>>();
6610            rank_inputs[0] = Some({
6611                let _main = root.gpu.enter_main()?;
6612                root.htod(input_row)?
6613            });
6614            let mut root_output = {
6615                let _main = root.gpu.enter_main()?;
6616                root.zeros(experts.input_width)?
6617            };
6618            let mut remote_down_keepalive = Vec::new();
6619
6620            for slot in 0..experts_per_token {
6621                let pair = token * experts_per_token + slot;
6622                let expert = selected[pair];
6623                if expert >= experts.expert_count {
6624                    return Err(format!(
6625                        "EP selected expert {expert} outside 0..{}",
6626                        experts.expert_count
6627                    )
6628                    .into());
6629                }
6630                let owner = expert / per_rank;
6631                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
6632                if rank_inputs[owner].is_none() {
6633                    let peer_input = {
6634                        let root_input = rank_inputs[0]
6635                            .as_ref()
6636                            .ok_or("native EP lost its root input")?;
6637                        let engine = &self.ranks[owner];
6638                        let _main = engine.gpu.enter_main()?;
6639                        let mut peer_input = engine.uninit(experts.input_width)?;
6640                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
6641                        peer_input
6642                    };
6643                    rank_inputs[owner] = Some(peer_input);
6644                }
6645
6646                let rank = &experts.ranks[owner];
6647                let engine = &self.ranks[owner];
6648                let owner_input = rank_inputs[owner]
6649                    .as_ref()
6650                    .ok_or("native EP owner input is absent after dispatch")?;
6651                let gate = run_resident_bank_expert_device(
6652                    engine,
6653                    &rank.gate,
6654                    local_expert,
6655                    owner_input,
6656                    1,
6657                )?;
6658                let up = run_resident_bank_expert_device(
6659                    engine,
6660                    &rank.up,
6661                    local_expert,
6662                    owner_input,
6663                    1,
6664                )?;
6665                let activated = {
6666                    let _main = engine.gpu.enter_main()?;
6667                    let mut activated = engine.uninit(experts.expert_width)?;
6668                    if let Some(limit) = activation_limit {
6669                        engine.silu_clamped_mul_host_expf(
6670                            &gate,
6671                            &up,
6672                            limit,
6673                            &mut activated,
6674                            experts.expert_width,
6675                        )?;
6676                    } else {
6677                        engine.silu_mul_host_expf(
6678                            &gate,
6679                            &up,
6680                            &mut activated,
6681                            experts.expert_width,
6682                        )?;
6683                    }
6684                    activated
6685                };
6686                let down = run_resident_bank_expert_device(
6687                    engine,
6688                    &rank.down,
6689                    local_expert,
6690                    &activated,
6691                    1,
6692                )?;
6693                let root_down = if owner == 0 {
6694                    down
6695                } else {
6696                    let _main = root.gpu.enter_main()?;
6697                    let mut root_down = root.uninit(experts.input_width)?;
6698                    root.stream().memcpy_dtod(&down, &mut root_down)?;
6699                    // The peer copy runs on the root stream. Keep its remote source alive until
6700                    // the final root readback synchronizes that stream; otherwise async free can
6701                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
6702                    remote_down_keepalive.push(down);
6703                    root_down
6704                };
6705                let _main = root.gpu.enter_main()?;
6706                let mut destination = root_output.slice_mut(0..experts.input_width);
6707                root.axpy_host_into(
6708                    &root_down.slice(0..root_down.len()),
6709                    route_weights[pair],
6710                    &mut destination,
6711                    experts.input_width,
6712                )?;
6713            }
6714
6715            let _main = root.gpu.enter_main()?;
6716            let root_output = root.dtoh(&root_output)?;
6717            drop(remote_down_keepalive);
6718            output.extend(root_output);
6719        }
6720        Ok(output)
6721    }
6722}
6723
6724fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
6725    if matrix.out_features % tp != 0 {
6726        return Err(format!(
6727            "column-parallel out_features {} is not divisible by TP={tp}",
6728            matrix.out_features
6729        ));
6730    }
6731    let local_out = matrix.out_features / tp;
6732    if local_out % FP8_BLOCK != 0 {
6733        return Err(format!(
6734            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
6735             E4M3 scale block"
6736        ));
6737    }
6738    Ok(())
6739}
6740
6741fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
6742    if !matches!(tp, 1 | 2 | 4 | 8) {
6743        return Err(format!(
6744            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
6745        ));
6746    }
6747    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
6748        return Err(format!(
6749            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
6750        ));
6751    }
6752    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
6753    let local_out = out_features / tp;
6754    if local_out % canonical_rows != 0 {
6755        return Err(format!(
6756            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
6757             {canonical_rows}-row chunks"
6758        ));
6759    }
6760    Ok(canonical_rows)
6761}
6762
6763fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
6764    if !matches!(tp, 1 | 2 | 4 | 8) {
6765        return Err(format!(
6766            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
6767        ));
6768    }
6769    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
6770        return Err(format!(
6771            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
6772        ));
6773    }
6774    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
6775    let local_in = in_features / tp;
6776    if local_in % canonical_cols != 0 {
6777        return Err(format!(
6778            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
6779             {canonical_cols}-column chunks"
6780        ));
6781    }
6782    Ok(canonical_cols)
6783}
6784
6785fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
6786    if matrix.in_features % tp != 0 {
6787        return Err(format!(
6788            "row-parallel in_features {} is not divisible by TP={tp}",
6789            matrix.in_features
6790        ));
6791    }
6792    let local_in = matrix.in_features / tp;
6793    if local_in % FP8_BLOCK != 0 {
6794        return Err(format!(
6795            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
6796             E4M3 scale block"
6797        ));
6798    }
6799    Ok(())
6800}
6801
6802fn upload_rank(
6803    engine: &Engine,
6804    matrix: E4m3BlockMatrix<'_>,
6805) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
6806    let _main = engine.gpu.enter_main()?;
6807    matrix.validate()?;
6808    Ok(ResidentE4m3Rank {
6809        codes: engine.htod_bytes(matrix.codes)?,
6810        scales: engine.htod(matrix.scales)?,
6811        out_features: matrix.out_features,
6812        in_features: matrix.in_features,
6813    })
6814}
6815
6816fn upload_bf16_rank(
6817    engine: &Engine,
6818    matrix: Bf16Matrix<'_>,
6819    f32_mirror: bool,
6820) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
6821    let _main = engine.gpu.enter_main()?;
6822    matrix.validate()?;
6823    let bytes = engine.htod_bytes(matrix.bytes)?;
6824    let weight = if f32_mirror {
6825        let values = matrix
6826            .out_features
6827            .checked_mul(matrix.in_features)
6828            .ok_or("resident BF16 mirror element count overflow")?;
6829        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
6830    } else {
6831        ResidentBf16Weight::Bf16(bytes)
6832    };
6833    Ok(ResidentBf16Rank {
6834        weight,
6835        out_features: matrix.out_features,
6836        in_features: matrix.in_features,
6837    })
6838}
6839
6840fn upload_expert_bank_rank(
6841    engine: &Engine,
6842    bank: E4m3ExpertBank<'_>,
6843    expert_range: Range<usize>,
6844) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
6845    let _main = engine.gpu.enter_main()?;
6846    bank.validate()?;
6847    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
6848        return Err(format!(
6849            "invalid EP expert range {expert_range:?} for {} experts",
6850            bank.expert_count
6851        )
6852        .into());
6853    }
6854    let code_stride = bank.out_features * bank.in_features;
6855    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
6856    Ok(ResidentE4m3ExpertBankRank {
6857        codes: engine.htod_bytes(
6858            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
6859        )?,
6860        scales: engine.htod(
6861            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
6862        )?,
6863        expert_range,
6864        out_features: bank.out_features,
6865        in_features: bank.in_features,
6866        code_stride,
6867        scale_stride,
6868        k_blocks: None,
6869    })
6870}
6871
6872fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
6873    if bank.out_features % tp != 0 {
6874        return Err(format!(
6875            "TP expert output width {} is not divisible by TP={tp}",
6876            bank.out_features
6877        ));
6878    }
6879    let local_out = bank.out_features / tp;
6880    if local_out % FP8_BLOCK != 0 {
6881        return Err(format!(
6882            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
6883        ));
6884    }
6885    Ok(())
6886}
6887
6888fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
6889    if bank.in_features % tp != 0 {
6890        return Err(format!(
6891            "TP expert input width {} is not divisible by TP={tp}",
6892            bank.in_features
6893        ));
6894    }
6895    let local_in = bank.in_features / tp;
6896    if local_in % FP8_BLOCK != 0 {
6897        return Err(format!(
6898            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
6899        ));
6900    }
6901    Ok(())
6902}
6903
6904fn upload_column_bank_rank(
6905    engine: &Engine,
6906    bank: E4m3ExpertBank<'_>,
6907    tp: usize,
6908    rank: usize,
6909) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
6910    let _main = engine.gpu.enter_main()?;
6911    let packed = pack_column_bank_rank(bank, tp, rank)?;
6912    Ok(ResidentE4m3ExpertBankRank {
6913        codes: engine.htod_bytes(&packed.codes)?,
6914        scales: engine.htod(&packed.scales)?,
6915        expert_range: packed.expert_range,
6916        out_features: packed.out_features,
6917        in_features: packed.in_features,
6918        code_stride: packed.code_stride,
6919        scale_stride: packed.scale_stride,
6920        k_blocks: packed.k_blocks,
6921    })
6922}
6923
6924fn pack_column_bank_rank(
6925    bank: E4m3ExpertBank<'_>,
6926    tp: usize,
6927    rank: usize,
6928) -> Result<PackedE4m3ExpertBankRank, String> {
6929    bank.validate()?;
6930    validate_column_bank_shape(bank, tp)?;
6931    if rank >= tp {
6932        return Err(format!("TP rank {rank} outside 0..{tp}"));
6933    }
6934    let local_out = bank.out_features / tp;
6935    let full_code_stride = bank.out_features * bank.in_features;
6936    let local_code_stride = local_out * bank.in_features;
6937    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
6938    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
6939    let local_scale_rows = local_out / FP8_BLOCK;
6940    let local_scale_stride = local_scale_rows * scale_cols;
6941    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
6942    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
6943    let row_start = rank * local_out;
6944    let scale_row_start = rank * local_scale_rows;
6945    for expert in 0..bank.expert_count {
6946        let code_start = expert * full_code_stride + row_start * bank.in_features;
6947        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
6948        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
6949        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
6950    }
6951    Ok(PackedE4m3ExpertBankRank {
6952        codes,
6953        scales,
6954        expert_range: 0..bank.expert_count,
6955        out_features: local_out,
6956        in_features: bank.in_features,
6957        code_stride: local_code_stride,
6958        scale_stride: local_scale_stride,
6959        k_blocks: None,
6960    })
6961}
6962
6963fn upload_row_bank_rank(
6964    engine: &Engine,
6965    bank: E4m3ExpertBank<'_>,
6966    tp: usize,
6967    rank: usize,
6968) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
6969    let _main = engine.gpu.enter_main()?;
6970    let packed = pack_row_bank_rank(bank, tp, rank)?;
6971    Ok(ResidentE4m3ExpertBankRank {
6972        codes: engine.htod_bytes(&packed.codes)?,
6973        scales: engine.htod(&packed.scales)?,
6974        expert_range: packed.expert_range,
6975        out_features: packed.out_features,
6976        in_features: packed.in_features,
6977        code_stride: packed.code_stride,
6978        scale_stride: packed.scale_stride,
6979        k_blocks: packed.k_blocks,
6980    })
6981}
6982
6983fn pack_row_bank_rank(
6984    bank: E4m3ExpertBank<'_>,
6985    tp: usize,
6986    rank: usize,
6987) -> Result<PackedE4m3ExpertBankRank, String> {
6988    bank.validate()?;
6989    validate_row_bank_shape(bank, tp)?;
6990    if rank >= tp {
6991        return Err(format!("TP rank {rank} outside 0..{tp}"));
6992    }
6993    let local_in = bank.in_features / tp;
6994    let full_code_stride = bank.out_features * bank.in_features;
6995    let local_code_stride = bank.out_features * local_in;
6996    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
6997    let local_scale_cols = local_in / FP8_BLOCK;
6998    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
6999    let full_scale_stride = scale_rows * full_scale_cols;
7000    let local_scale_stride = scale_rows * local_scale_cols;
7001    let global_block_start = rank * local_scale_cols;
7002    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7003    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7004    for expert in 0..bank.expert_count {
7005        let expert_code_start = expert * full_code_stride;
7006        let expert_scale_start = expert * full_scale_stride;
7007        for local_block in 0..local_scale_cols {
7008            let global_block = global_block_start + local_block;
7009            let column_start = global_block * FP8_BLOCK;
7010            for row in 0..bank.out_features {
7011                let start = expert_code_start + row * bank.in_features + column_start;
7012                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
7013            }
7014            for row in 0..scale_rows {
7015                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
7016            }
7017        }
7018    }
7019    Ok(PackedE4m3ExpertBankRank {
7020        codes,
7021        scales,
7022        expert_range: 0..bank.expert_count,
7023        out_features: bank.out_features,
7024        in_features: local_in,
7025        code_stride: local_code_stride,
7026        scale_stride: local_scale_stride,
7027        k_blocks: Some(local_scale_cols),
7028    })
7029}
7030
7031fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
7032    if engines.len() != ranks.len() {
7033        return Err(format!(
7034            "resident TP rank count {} != runtime rank count {}",
7035            ranks.len(),
7036            engines.len()
7037        ));
7038    }
7039    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7040        let device = engine.ctx().ordinal();
7041        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
7042            return Err(format!(
7043                "resident TP rank {rank} is not owned by runtime device {device}"
7044            ));
7045        }
7046    }
7047    Ok(())
7048}
7049
7050fn validate_tp_bank_residency(
7051    engines: &[Engine],
7052    experts: &ResidentTpExpertBank,
7053) -> Result<(), String> {
7054    if engines.len() != experts.gate.len()
7055        || engines.len() != experts.up.len()
7056        || engines.len() != experts.down.len()
7057    {
7058        return Err(format!(
7059            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
7060            experts.gate.len(),
7061            experts.up.len(),
7062            experts.down.len(),
7063            engines.len()
7064        ));
7065    }
7066    for (rank, engine) in engines.iter().enumerate() {
7067        let device = engine.ctx().ordinal();
7068        for (projection, bank) in [
7069            ("gate", &experts.gate[rank]),
7070            ("up", &experts.up[rank]),
7071            ("down", &experts.down[rank]),
7072        ] {
7073            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7074                return Err(format!(
7075                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
7076                     {device}"
7077                ));
7078            }
7079        }
7080    }
7081    Ok(())
7082}
7083
7084fn validate_ep_residency(
7085    engines: &[Engine],
7086    experts: &ResidentExpertParallel,
7087) -> Result<(), String> {
7088    if engines.len() != experts.ranks.len() {
7089        return Err(format!(
7090            "resident EP rank count {} != runtime rank count {}",
7091            experts.ranks.len(),
7092            engines.len()
7093        ));
7094    }
7095    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
7096        let device = engine.ctx().ordinal();
7097        for (projection, bank) in [
7098            ("gate", &resident.gate),
7099            ("up", &resident.up),
7100            ("down", &resident.down),
7101        ] {
7102            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7103                return Err(format!(
7104                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
7105                     {device}"
7106                ));
7107            }
7108        }
7109    }
7110    Ok(())
7111}
7112
7113fn run_rank(
7114    engine: &Engine,
7115    matrix: E4m3BlockMatrix<'_>,
7116    activations: &[f32],
7117    tokens: usize,
7118) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7119    let _main = engine.gpu.enter_main()?;
7120    let codes = engine.htod_bytes(matrix.codes)?;
7121    let scales = engine.htod(matrix.scales)?;
7122    let activations = engine.htod(activations)?;
7123    let output = engine.qmatvec_mmq_fp8_blk(
7124        &codes,
7125        &scales,
7126        &activations,
7127        tokens,
7128        matrix.in_features,
7129        matrix.out_features,
7130    )?;
7131    engine.dtoh(&output)
7132}
7133
7134fn run_resident_rank(
7135    engine: &Engine,
7136    matrix: &ResidentE4m3Rank,
7137    activations: &[f32],
7138    tokens: usize,
7139) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7140    let _main = engine.gpu.enter_main()?;
7141    let activations = engine.htod(activations)?;
7142    let output = engine.qmatvec_mmq_fp8_blk(
7143        &matrix.codes,
7144        &matrix.scales,
7145        &activations,
7146        tokens,
7147        matrix.in_features,
7148        matrix.out_features,
7149    )?;
7150    engine.dtoh(&output)
7151}
7152
7153fn run_resident_bf16_rank(
7154    engine: &Engine,
7155    matrix: &ResidentBf16Rank,
7156    activations: &[f32],
7157    tokens: usize,
7158    canonical_chunk_rows: Option<usize>,
7159) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7160    let _main = engine.gpu.enter_main()?;
7161    let activations = engine.htod(activations)?;
7162    let output = run_resident_bf16_rank_device(
7163        engine,
7164        matrix,
7165        &activations,
7166        tokens,
7167        canonical_chunk_rows,
7168        false,
7169    )?;
7170    engine.dtoh(&output)
7171}
7172
7173fn run_resident_bf16_rank_device(
7174    engine: &Engine,
7175    matrix: &ResidentBf16Rank,
7176    activations: &CudaSlice<f32>,
7177    tokens: usize,
7178    canonical_chunk_rows: Option<usize>,
7179    strided_chunk_output: bool,
7180) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7181    let _main = engine.gpu.enter_main()?;
7182    if activations.ordinal() != engine.ctx().ordinal() {
7183        return Err(format!(
7184            "resident BF16 activation device {} != rank device {}",
7185            activations.ordinal(),
7186            engine.ctx().ordinal()
7187        )
7188        .into());
7189    }
7190    if activations.len() != tokens * matrix.in_features {
7191        return Err(format!(
7192            "resident BF16 activation count {} != {tokens}x{}",
7193            activations.len(),
7194            matrix.in_features
7195        )
7196        .into());
7197    }
7198    match (&matrix.weight, canonical_chunk_rows) {
7199        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
7200            .linear_bf16_resident_canonical_rows(
7201                activations,
7202                bytes,
7203                tokens,
7204                matrix.in_features,
7205                matrix.out_features,
7206                rows,
7207            ),
7208        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
7209            activations,
7210            bytes,
7211            tokens,
7212            matrix.in_features,
7213            matrix.out_features,
7214        ),
7215        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
7216            .linear_f32_resident_canonical_rows_strided(
7217                activations,
7218                values,
7219                tokens,
7220                matrix.in_features,
7221                matrix.out_features,
7222                rows,
7223            ),
7224        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
7225            activations,
7226            values,
7227            tokens,
7228            matrix.in_features,
7229            matrix.out_features,
7230            rows,
7231        ),
7232        (ResidentBf16Weight::F32(values), None) => engine.linear(
7233            activations,
7234            values,
7235            tokens,
7236            matrix.in_features,
7237            matrix.out_features,
7238        ),
7239    }
7240}
7241
7242fn validate_resident_bf16_ranks(
7243    engines: &[Engine],
7244    ranks: &[ResidentBf16Rank],
7245) -> Result<(), String> {
7246    if engines.len() != ranks.len() {
7247        return Err(format!(
7248            "resident BF16 TP rank count {} != runtime rank count {}",
7249            ranks.len(),
7250            engines.len(),
7251        ));
7252    }
7253    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7254        let device = engine.ctx().ordinal();
7255        if matrix.weight.ordinal() != device {
7256            return Err(format!(
7257                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
7258            ));
7259        }
7260    }
7261    Ok(())
7262}
7263
7264fn validate_step_bf16_row_residency(
7265    engines: &[Engine],
7266    matrix: &ResidentStepBf16RowParallel,
7267) -> Result<(), String> {
7268    if engines.len() != matrix.ranks.len() {
7269        return Err(format!(
7270            "resident Step BF16 row rank count {} != runtime rank count {}",
7271            matrix.ranks.len(),
7272            engines.len(),
7273        ));
7274    }
7275    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
7276    if matrix.canonical_chunk_cols != canonical_cols {
7277        return Err(format!(
7278            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
7279            matrix.canonical_chunk_cols
7280        ));
7281    }
7282    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
7283    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
7284        if blocks.len() != blocks_per_rank {
7285            return Err(format!(
7286                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
7287                blocks.len()
7288            ));
7289        }
7290        let device = engine.ctx().ordinal();
7291        for (block, resident) in blocks.iter().enumerate() {
7292            if resident.weight.ordinal() != device
7293                || resident.in_features != canonical_cols
7294                || resident.out_features != matrix.out_features
7295            {
7296                return Err(format!(
7297                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
7298                     device or geometry"
7299                ));
7300            }
7301        }
7302    }
7303    Ok(())
7304}
7305
7306fn validate_replicated_device_rows(
7307    engines: &[Engine],
7308    rows: &ResidentReplicatedDeviceRows,
7309) -> Result<(), String> {
7310    let rank_lengths = rows
7311        .ranks
7312        .iter()
7313        .map(|rank_rows| rank_rows.len())
7314        .collect::<Vec<_>>();
7315    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
7316    if rows
7317        .ranks
7318        .iter()
7319        .zip(engines)
7320        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
7321    {
7322        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
7323    }
7324    Ok(())
7325}
7326
7327fn replicated_device_row_values(
7328    tokens: usize,
7329    width: usize,
7330    expected_ranks: usize,
7331    rank_lengths: &[usize],
7332) -> Result<usize, String> {
7333    let values = tokens
7334        .checked_mul(width)
7335        .ok_or("replicated device row size overflow")?;
7336    if tokens == 0
7337        || width == 0
7338        || expected_ranks == 0
7339        || rank_lengths.len() != expected_ranks
7340        || rank_lengths.iter().any(|&rank_len| rank_len != values)
7341    {
7342        return Err(format!(
7343            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
7344            tokens,
7345            width,
7346            rank_lengths.len(),
7347            expected_ranks
7348        ));
7349    }
7350    Ok(values)
7351}
7352
7353fn replicated_device_row_source_values(
7354    tokens: usize,
7355    width: usize,
7356    source_len: usize,
7357    source_device: usize,
7358    root_device: usize,
7359) -> Result<usize, String> {
7360    let values = tokens
7361        .checked_mul(width)
7362        .ok_or("replicated device row size overflow")?;
7363    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
7364        return Err(format!(
7365            "replicated device row source has inconsistent geometry/device \
7366             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
7367        ));
7368    }
7369    Ok(values)
7370}
7371
7372fn bf16_column_shard(
7373    matrix: Bf16Matrix<'_>,
7374    tp: usize,
7375    rank: usize,
7376) -> Result<Bf16Matrix<'_>, String> {
7377    matrix.validate()?;
7378    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
7379        return Err(format!(
7380            "invalid BF16 column shard out={} TP={tp} rank={rank}",
7381            matrix.out_features
7382        ));
7383    }
7384    let local_out = matrix.out_features / tp;
7385    let row_bytes = matrix.in_features * 2;
7386    let start = rank * local_out * row_bytes;
7387    Ok(Bf16Matrix {
7388        bytes: &matrix.bytes[start..start + local_out * row_bytes],
7389        out_features: local_out,
7390        in_features: matrix.in_features,
7391    })
7392}
7393
7394fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
7395    matrix.validate()?;
7396    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
7397        return Err(format!(
7398            "invalid BF16 row shard in={} TP={tp} rank={rank}",
7399            matrix.in_features
7400        ));
7401    }
7402    let local_in = matrix.in_features / tp;
7403    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
7404    for row in 0..matrix.out_features {
7405        let start = (row * matrix.in_features + rank * local_in) * 2;
7406        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
7407    }
7408    Ok(bytes)
7409}
7410
7411fn bf16_row_block(
7412    matrix: Bf16Matrix<'_>,
7413    col_start: usize,
7414    block_cols: usize,
7415) -> Result<Vec<u8>, String> {
7416    matrix.validate()?;
7417    let col_end = col_start
7418        .checked_add(block_cols)
7419        .ok_or("BF16 row block column overflow")?;
7420    if block_cols == 0 || col_end > matrix.in_features {
7421        return Err(format!(
7422            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
7423            matrix.in_features
7424        ));
7425    }
7426    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
7427    for row in 0..matrix.out_features {
7428        let start = (row * matrix.in_features + col_start) * 2;
7429        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
7430    }
7431    Ok(bytes)
7432}
7433
7434fn run_resident_bank_expert(
7435    engine: &Engine,
7436    bank: &ResidentE4m3ExpertBankRank,
7437    local_expert: usize,
7438    activations: &[f32],
7439    tokens: usize,
7440) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7441    let _main = engine.gpu.enter_main()?;
7442    if bank.k_blocks.is_some() {
7443        return Err("block-major TP row bank requires canonical block execution".into());
7444    }
7445    let local_count = bank.expert_range.end - bank.expert_range.start;
7446    if local_expert >= local_count {
7447        return Err(format!(
7448            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
7449            bank.expert_range
7450        )
7451        .into());
7452    }
7453    validate_activations(activations, tokens, bank.in_features)?;
7454    let activations = engine.htod(activations)?;
7455    let weight = bank
7456        .codes
7457        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
7458    let scales = bank
7459        .scales
7460        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
7461    let input = activations.slice(0..activations.len());
7462    let output = engine.qmatvec_mmq_fp8_blk_view(
7463        &weight,
7464        &scales,
7465        &input,
7466        tokens,
7467        bank.in_features,
7468        bank.out_features,
7469    )?;
7470    engine.dtoh(&output)
7471}
7472
7473fn run_resident_bank_expert_block(
7474    engine: &Engine,
7475    bank: &ResidentE4m3ExpertBankRank,
7476    local_expert: usize,
7477    block: usize,
7478    activations: &[f32],
7479) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7480    let _main = engine.gpu.enter_main()?;
7481    let local_count = bank.expert_range.end - bank.expert_range.start;
7482    if local_expert >= local_count {
7483        return Err(format!(
7484            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7485            bank.expert_range
7486        )
7487        .into());
7488    }
7489    let blocks = bank
7490        .k_blocks
7491        .ok_or("TP row bank is not packed in native K-block order")?;
7492    if block >= blocks {
7493        return Err(format!("TP row block {block} outside 0..{blocks}").into());
7494    }
7495    validate_activations(activations, 1, FP8_BLOCK)?;
7496    let block_code_stride = bank.out_features * FP8_BLOCK;
7497    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
7498    if bank.in_features != blocks * FP8_BLOCK
7499        || bank.code_stride != blocks * block_code_stride
7500        || bank.scale_stride != blocks * block_scale_stride
7501    {
7502        return Err("TP row bank block-major geometry is inconsistent".into());
7503    }
7504
7505    let expert_code_start = local_expert * bank.code_stride;
7506    let expert_scale_start = local_expert * bank.scale_stride;
7507    let weight = bank.codes.slice(
7508        expert_code_start + block * block_code_stride
7509            ..expert_code_start + (block + 1) * block_code_stride,
7510    );
7511    let scales = bank.scales.slice(
7512        expert_scale_start + block * block_scale_stride
7513            ..expert_scale_start + (block + 1) * block_scale_stride,
7514    );
7515    let activations = engine.htod(activations)?;
7516    let input = activations.slice(0..activations.len());
7517    let output = engine.qmatvec_mmq_fp8_blk_view(
7518        &weight,
7519        &scales,
7520        &input,
7521        1,
7522        FP8_BLOCK,
7523        bank.out_features,
7524    )?;
7525    engine.dtoh(&output)
7526}
7527
7528fn run_resident_bank_expert_device(
7529    engine: &Engine,
7530    bank: &ResidentE4m3ExpertBankRank,
7531    local_expert: usize,
7532    activations: &CudaSlice<f32>,
7533    tokens: usize,
7534) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7535    let _main = engine.gpu.enter_main()?;
7536    if bank.k_blocks.is_some() {
7537        return Err("block-major TP row bank requires canonical block execution".into());
7538    }
7539    let local_count = bank.expert_range.end - bank.expert_range.start;
7540    if local_expert >= local_count {
7541        return Err(format!(
7542            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7543            bank.expert_range
7544        )
7545        .into());
7546    }
7547    let expected = tokens
7548        .checked_mul(bank.in_features)
7549        .ok_or("native TP activation size overflow")?;
7550    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
7551        return Err(format!(
7552            "native TP activation len/device {}/{} != expected {expected}/{}",
7553            activations.len(),
7554            activations.ordinal(),
7555            engine.ctx().ordinal()
7556        )
7557        .into());
7558    }
7559    let weight = bank
7560        .codes
7561        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
7562    let scales = bank
7563        .scales
7564        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
7565    let input = activations.slice(0..activations.len());
7566    engine.qmatvec_mmq_fp8_blk_view(
7567        &weight,
7568        &scales,
7569        &input,
7570        tokens,
7571        bank.in_features,
7572        bank.out_features,
7573    )
7574}
7575
7576fn run_resident_bank_expert_block_device(
7577    engine: &Engine,
7578    bank: &ResidentE4m3ExpertBankRank,
7579    local_expert: usize,
7580    block: usize,
7581    activations: &cudarc::driver::CudaView<'_, f32>,
7582) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7583    let _main = engine.gpu.enter_main()?;
7584    let local_count = bank.expert_range.end - bank.expert_range.start;
7585    if local_expert >= local_count {
7586        return Err(format!(
7587            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
7588            bank.expert_range
7589        )
7590        .into());
7591    }
7592    let blocks = bank
7593        .k_blocks
7594        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
7595    if block >= blocks {
7596        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
7597    }
7598    let activation_device = activations.stream().context().ordinal();
7599    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
7600        return Err(format!(
7601            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
7602            activations.len(),
7603            activation_device,
7604            engine.ctx().ordinal()
7605        )
7606        .into());
7607    }
7608    let block_code_stride = bank.out_features * FP8_BLOCK;
7609    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
7610    if bank.in_features != blocks * FP8_BLOCK
7611        || bank.code_stride != blocks * block_code_stride
7612        || bank.scale_stride != blocks * block_scale_stride
7613    {
7614        return Err("native TP row bank block-major geometry is inconsistent".into());
7615    }
7616    let expert_code_start = local_expert * bank.code_stride;
7617    let expert_scale_start = local_expert * bank.scale_stride;
7618    let weight = bank.codes.slice(
7619        expert_code_start + block * block_code_stride
7620            ..expert_code_start + (block + 1) * block_code_stride,
7621    );
7622    let scales = bank.scales.slice(
7623        expert_scale_start + block * block_scale_stride
7624            ..expert_scale_start + (block + 1) * block_scale_stride,
7625    );
7626    engine.qmatvec_mmq_fp8_blk_view(
7627        &weight,
7628        &scales,
7629        activations,
7630        1,
7631        FP8_BLOCK,
7632        bank.out_features,
7633    )
7634}
7635
7636fn configure_native_p2p(
7637    ranks: &[Engine],
7638    devices: &[usize],
7639) -> Result<(), Box<dyn std::error::Error>> {
7640    if ranks.len() != devices.len() || ranks.len() < 2 {
7641        return Err("native TP P2P setup requires matching multi-rank devices".into());
7642    }
7643    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
7644        if engine.ctx().ordinal() != device {
7645            return Err(format!(
7646                "native TP rank {rank} context device {} != requested device {device}",
7647                engine.ctx().ordinal()
7648            )
7649            .into());
7650        }
7651    }
7652
7653    for src in 0..ranks.len() {
7654        for dst in 0..ranks.len() {
7655            if src == dst {
7656                continue;
7657            }
7658            let mut can_access = 0;
7659            unsafe {
7660                cudarc::driver::sys::cuDeviceCanAccessPeer(
7661                    &mut can_access,
7662                    ranks[src].ctx().cu_device(),
7663                    ranks[dst].ctx().cu_device(),
7664                )
7665                .result()?;
7666            }
7667            if can_access == 0 {
7668                return Err(format!(
7669                    "native TP requires P2P, but dev{} cannot access dev{}",
7670                    devices[src], devices[dst]
7671                )
7672                .into());
7673            }
7674            ranks[src].ctx().bind_to_thread()?;
7675            let rc =
7676                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
7677            use cudarc::driver::sys::cudaError_enum as E;
7678            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
7679                return Err(format!(
7680                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
7681                    devices[src], devices[dst]
7682                )
7683                .into());
7684            }
7685        }
7686    }
7687
7688    for &owner in devices {
7689        for &accessor in devices {
7690            if owner == accessor {
7691                continue;
7692            }
7693            let device = cudarc::driver::result::device::get(owner as i32)?;
7694            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
7695            unsafe {
7696                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
7697            }
7698            let desc = cudarc::driver::sys::CUmemAccessDesc {
7699                location: cudarc::driver::sys::CUmemLocation {
7700                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
7701                    id: accessor as i32,
7702                },
7703                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
7704            };
7705            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
7706            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
7707                return Err(format!(
7708                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
7709                     {rc:?}"
7710                )
7711                .into());
7712            }
7713        }
7714    }
7715
7716    for src in 0..ranks.len() {
7717        for dst in 0..ranks.len() {
7718            if src == dst {
7719                continue;
7720            }
7721            let expected = (0..NATIVE_P2P_PROBE_WORDS)
7722                .map(|index| {
7723                    (index as u32)
7724                        .wrapping_mul(0x9e37_79b9)
7725                        .wrapping_add(((src as u32) << 16) | dst as u32)
7726                })
7727                .collect::<Vec<_>>();
7728            let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
7729            let source = ranks[src].htod_u32_v(&expected)?;
7730            let mut destination = ranks[dst].htod_u32_v(&poison)?;
7731            ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
7732            let actual = ranks[dst].dtoh_u32(&destination)?;
7733            if actual != expected {
7734                let mismatches = actual
7735                    .iter()
7736                    .zip(&expected)
7737                    .filter(|(actual, expected)| actual != expected)
7738                    .count();
7739                return Err(format!(
7740                    "native TP peer probe dev{}->dev{} failed: {mismatches}/{} words differ",
7741                    devices[src],
7742                    devices[dst],
7743                    expected.len()
7744                )
7745                .into());
7746            }
7747        }
7748    }
7749    ranks[0].ctx().bind_to_thread()?;
7750    eprintln!(
7751        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
7752         directions={} bytes={} mismatches=0",
7753        ranks.len() * (ranks.len() - 1),
7754        NATIVE_P2P_PROBE_WORDS * std::mem::size_of::<u32>(),
7755    );
7756    Ok(())
7757}
7758
7759fn validate_activations(
7760    activations: &[f32],
7761    tokens: usize,
7762    in_features: usize,
7763) -> Result<(), String> {
7764    let expected = tokens
7765        .checked_mul(in_features)
7766        .ok_or_else(|| "activation size overflow".to_string())?;
7767    if activations.len() != expected {
7768        return Err(format!(
7769            "activation count {} != {tokens}x{in_features} ({expected})",
7770            activations.len()
7771        ));
7772    }
7773    if !activations.iter().all(|value| value.is_finite()) {
7774        return Err("activations contain a non-finite value".to_string());
7775    }
7776    Ok(())
7777}
7778
7779fn column_shard(
7780    matrix: E4m3BlockMatrix<'_>,
7781    tp: usize,
7782    rank: usize,
7783) -> Result<E4m3BlockMatrix<'_>, String> {
7784    let local_out = matrix.out_features / tp;
7785    let row_start = rank * local_out;
7786    let code_start = row_start * matrix.in_features;
7787    let code_end = code_start + local_out * matrix.in_features;
7788    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
7789    let local_scale_rows = local_out / FP8_BLOCK;
7790    let scale_start = rank * local_scale_rows * scale_cols;
7791    let scale_end = scale_start + local_scale_rows * scale_cols;
7792    Ok(E4m3BlockMatrix {
7793        codes: &matrix.codes[code_start..code_end],
7794        scales: &matrix.scales[scale_start..scale_end],
7795        out_features: local_out,
7796        in_features: matrix.in_features,
7797    })
7798}
7799
7800fn row_shard(
7801    matrix: E4m3BlockMatrix<'_>,
7802    tp: usize,
7803    rank: usize,
7804) -> Result<(Vec<u8>, Vec<f32>), String> {
7805    let local_in = matrix.in_features / tp;
7806    let col_start = rank * local_in;
7807    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
7808    for row in 0..matrix.out_features {
7809        let start = row * matrix.in_features + col_start;
7810        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
7811    }
7812
7813    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
7814    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
7815    let local_scale_cols = local_in / FP8_BLOCK;
7816    let scale_col_start = rank * local_scale_cols;
7817    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
7818    for row in 0..scale_rows {
7819        let start = row * scale_cols + scale_col_start;
7820        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
7821    }
7822    Ok((codes, scales))
7823}
7824
7825fn activation_shard(
7826    activations: &[f32],
7827    tokens: usize,
7828    in_features: usize,
7829    tp: usize,
7830    rank: usize,
7831) -> Vec<f32> {
7832    let local_in = in_features / tp;
7833    let col_start = rank * local_in;
7834    let mut shard = Vec::with_capacity(tokens * local_in);
7835    for token in 0..tokens {
7836        let start = token * in_features + col_start;
7837        shard.extend_from_slice(&activations[start..start + local_in]);
7838    }
7839    shard
7840}
7841
7842// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
7843//
7844// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
7845// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
7846// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
7847// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
7848// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
7849// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
7850//
7851// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
7852// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
7853// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
7854// TP1-vs-TP2 bit gate. Every entry point below follows this order.
7855//
7856// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
7857// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
7858// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
7859
7860/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
7861#[derive(Clone, Copy)]
7862pub struct Nvfp4BlockMatrix<'a> {
7863    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
7864    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
7865    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
7866    pub out_features: usize,
7867    pub in_features: usize,
7868}
7869
7870impl Nvfp4BlockMatrix<'_> {
7871    pub fn validate(&self) -> Result<(), String> {
7872        if self.in_features == 0 || self.out_features == 0 {
7873            return Err("NVFP4 matrix has a zero dimension".to_string());
7874        }
7875        if self.in_features % 64 != 0 {
7876            return Err(format!(
7877                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
7878                self.in_features
7879            ));
7880        }
7881        if self.codes.len() != self.out_features * self.in_features / 2 {
7882            return Err(format!(
7883                "NVFP4 code bytes {} != {}x{}/2",
7884                self.codes.len(),
7885                self.out_features,
7886                self.in_features
7887            ));
7888        }
7889        if self.scales.len() != self.out_features * self.in_features / 16 {
7890            return Err(format!(
7891                "NVFP4 scale bytes {} != {}x{}/16",
7892                self.scales.len(),
7893                self.out_features,
7894                self.in_features
7895            ));
7896        }
7897        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
7898            return Err(format!(
7899                "NVFP4 macro scale {} is not finite-positive",
7900                self.macro_scale
7901            ));
7902        }
7903        Ok(())
7904    }
7905}
7906
7907/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
7908#[derive(Clone, Copy)]
7909pub struct Nvfp4ExpertBank<'a> {
7910    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
7911    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
7912    pub macros: &'a [f32], // [expert_count] weight_scale_2
7913    pub expert_count: usize,
7914    pub out_features: usize,
7915    pub in_features: usize,
7916}
7917
7918impl Nvfp4ExpertBank<'_> {
7919    pub fn validate(&self) -> Result<(), String> {
7920        if self.expert_count == 0 {
7921            return Err("NVFP4 expert bank is empty".to_string());
7922        }
7923        if self.macros.len() != self.expert_count {
7924            return Err(format!(
7925                "NVFP4 bank macros {} != expert count {}",
7926                self.macros.len(),
7927                self.expert_count
7928            ));
7929        }
7930        self.expert(0).map(|_| ())
7931    }
7932
7933    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
7934        if expert >= self.expert_count {
7935            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
7936        }
7937        let code_stride = self.out_features * self.in_features / 2;
7938        let scale_stride = self.out_features * self.in_features / 16;
7939        if self.codes.len() != self.expert_count * code_stride
7940            || self.scales.len() != self.expert_count * scale_stride
7941        {
7942            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
7943        }
7944        let matrix = Nvfp4BlockMatrix {
7945            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
7946            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
7947            macro_scale: self.macros[expert],
7948            out_features: self.out_features,
7949            in_features: self.in_features,
7950        };
7951        matrix.validate()?;
7952        Ok(matrix)
7953    }
7954}
7955
7956/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
7957pub struct ResidentNvfp4Rank {
7958    blocks: crate::CudaSlice<u8>,
7959    macro_scale: f32,
7960    out_features: usize,
7961    in_features: usize,
7962    row_bytes: usize,
7963}
7964
7965pub struct ResidentNvfp4ColumnParallel {
7966    ranks: Vec<ResidentNvfp4Rank>,
7967    pub out_features: usize,
7968    pub in_features: usize,
7969}
7970
7971pub struct ResidentNvfp4RowParallel {
7972    ranks: Vec<ResidentNvfp4Rank>,
7973    pub out_features: usize,
7974    pub in_features: usize,
7975}
7976
7977pub struct ResidentTpNvfp4Expert {
7978    gate: ResidentNvfp4ColumnParallel,
7979    up: ResidentNvfp4ColumnParallel,
7980    down: ResidentNvfp4RowParallel,
7981    pub input_width: usize,
7982    pub expert_width: usize,
7983}
7984
7985/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
7986/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
7987/// later perf rung, mirroring the FP8 bank's history).
7988pub struct ResidentNvfp4ColumnBankRank {
7989    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
7990    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
7991    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
7992    bank: crate::CudaSlice<u8>,
7993    expert_bytes: usize,
7994    local_out: usize,
7995    in_features: usize,
7996    row_bytes: usize,
7997}
7998
7999impl ResidentNvfp4ColumnBankRank {
8000    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8001        self.bank
8002            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8003    }
8004}
8005
8006/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
8007/// as exactly this many input-column windows summed in shard order, at every world size: a
8008/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
8009/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
8010/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
8011pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
8012
8013pub struct ResidentNvfp4RowBankRank {
8014    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
8015    bank: crate::CudaSlice<u8>,
8016    expert_bytes: usize,
8017    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
8018    out_features: usize,
8019    local_in: usize,
8020    row_bytes: usize,
8021}
8022
8023impl ResidentNvfp4RowBankRank {
8024    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8025        self.bank
8026            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8027    }
8028}
8029
8030impl ResidentNvfp4TensorParallel {
8031    pub(crate) fn device_workspace_handle(
8032        &self,
8033    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
8034        &self.device_workspace
8035    }
8036}
8037
8038pub struct ResidentNvfp4TensorParallel {
8039    gate: Vec<ResidentNvfp4ColumnBankRank>,
8040    up: Vec<ResidentNvfp4ColumnBankRank>,
8041    down: Vec<ResidentNvfp4RowBankRank>,
8042    macros_gate: Vec<f32>,
8043    macros_up: Vec<f32>,
8044    macros_down: Vec<f32>,
8045    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
8046    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
8047    /// into the route-weight axpy scalar.
8048    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
8049    macros_up_dev: Vec<crate::CudaSlice<f32>>,
8050    macros_down_dev: Vec<crate::CudaSlice<f32>>,
8051    pub expert_count: usize,
8052    pub input_width: usize,
8053    pub expert_width: usize,
8054    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
8055    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
8056    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
8057    /// Lazily-built spec-verify t=2 workspace (MEMRA_TCOL_FFN): the two-column routed
8058    /// sweep's slabs and events, kept apart from the serving workspace so the verify walk
8059    /// never perturbs serving state.
8060    t2_workspace: std::sync::Mutex<Option<Nvfp4T2Workspace>>,
8061}
8062
8063/// Persistent buffers for the two-column (spec verify) NVFP4 device-routed program: every
8064/// slab is the t=1 workspace shape doubled along the pair axis, plus per-column
8065/// accumulators. One per expert bank, reused every (round, layer) call.
8066pub struct Nvfp4T2Workspace {
8067    input2: Vec<crate::CudaSlice<f32>>,
8068    in_q2: Vec<crate::CudaSlice<i8>>,
8069    in_d2: Vec<crate::CudaSlice<f32>>,
8070    sel2: Vec<crate::CudaSlice<i32>>,
8071    route_w2: Vec<crate::CudaSlice<f32>>,
8072    gate_out2: Vec<crate::CudaSlice<f32>>,
8073    up_out2: Vec<crate::CudaSlice<f32>>,
8074    act_q2: Vec<crate::CudaSlice<i8>>,
8075    act_d2: Vec<crate::CudaSlice<f32>>,
8076    partial2: Vec<crate::CudaSlice<f32>>,
8077    /// Per-rank per-column combine accumulators ([width] each).
8078    acc_a: Vec<crate::CudaSlice<f32>>,
8079    acc_b: Vec<crate::CudaSlice<f32>>,
8080    /// Root-side pulls of rank1's accumulators and the joined columns.
8081    peer_a: crate::CudaSlice<f32>,
8082    peer_b: crate::CudaSlice<f32>,
8083    omix_a: crate::CudaSlice<f32>,
8084    omix_b: crate::CudaSlice<f32>,
8085    ev_entry: CudaEvent,
8086    ev_rank: Vec<CudaEvent>,
8087    ev_root: CudaEvent,
8088    n_sel: usize,
8089    e_device: usize,
8090}
8091
8092/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
8093/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
8094/// (token, layer) call so the decode loop performs zero output allocations.
8095/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
8096/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
8097/// conservatively) and the persistent e-context input staging its copies read.
8098struct RoutesGraph {
8099    exec: cudarc::driver::sys::CUgraphExec,
8100    parent: cudarc::driver::sys::CUgraph,
8101    _children: Vec<cudarc::driver::CudaGraph>,
8102}
8103// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
8104// context-agnostic process handles.
8105unsafe impl Send for RoutesGraph {}
8106
8107impl Drop for RoutesGraph {
8108    fn drop(&mut self) {
8109        unsafe {
8110            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
8111            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
8112        }
8113    }
8114}
8115
8116impl Nvfp4DeviceRoutesWorkspace {
8117    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
8118        self.in_stage_e.as_ref()
8119    }
8120    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8121        self.in_stage_e.as_mut()
8122    }
8123    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8124        self.out_stage_e.as_mut()
8125    }
8126    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
8127    pub(crate) fn arm_stages(
8128        &mut self,
8129        e: &Engine,
8130        width: usize,
8131        n_sel: usize,
8132    ) -> Result<(), Box<dyn std::error::Error>> {
8133        let _main = e.gpu.enter_main()?;
8134        if self.in_stage_e.is_none() {
8135            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8136            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8137        }
8138        if self.dev_route_e.is_none() {
8139            self.dev_route_e = Some((
8140                e.htod_i32(&vec![0i32; n_sel])?,
8141                e.htod(&vec![0.0f32; n_sel])?,
8142            ));
8143        }
8144        Ok(())
8145    }
8146
8147    /// Split-borrow: the routes input (shared) + output (mut) stages together.
8148    pub(crate) fn in_and_out_stages_mut(
8149        &mut self,
8150    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
8151        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
8152            (Some(input), Some(output)) => Some((input, output)),
8153            _ => None,
8154        }
8155    }
8156    pub(crate) fn dev_route_e_mut(
8157        &mut self,
8158    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
8159        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
8160    }
8161}
8162
8163pub struct Nvfp4DeviceRoutesWorkspace {
8164    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
8165    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
8166    gate_out: Vec<crate::CudaSlice<f32>>,
8167    up_out: Vec<crate::CudaSlice<f32>>,
8168    act_q: Vec<crate::CudaSlice<i8>>,
8169    act_d: Vec<crate::CudaSlice<f32>>,
8170    sel: Vec<crate::CudaSlice<i32>>,
8171    partial: Vec<crate::CudaSlice<f32>>,
8172    accumulator: Vec<crate::CudaSlice<f32>>,
8173    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
8174    combine_w: Vec<crate::CudaSlice<f32>>,
8175    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
8176    /// in-kernel via sel + macros_down_dev).
8177    route_w: Vec<crate::CudaSlice<f32>>,
8178    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
8179    /// per-call allocation).
8180    in_q: Vec<crate::CudaSlice<i8>>,
8181    in_d: Vec<crate::CudaSlice<f32>>,
8182    /// e-context staging for the device router outputs (persistent — rank streams peer-read
8183    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
8184    /// never-free discipline).
8185    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
8186    /// Prestage door state: input pull + quantize already issued for this layer's call
8187    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
8188    prestaged: bool,
8189    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
8190    /// the routed run skips rank1's sel pull. Reset per call.
8191    rank1_routed: bool,
8192    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
8193    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
8194    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
8195    fence_flags_raw: u64,
8196    fence_ticket: u32,
8197    /// Prestage input fence, recorded on e after the input's producer.
8198    ev_input: Option<(CudaEvent, usize)>,
8199    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
8200    /// captured copies read/write), and the per-layer stitched parent.
8201    in_stage_e: Option<crate::CudaSlice<f32>>,
8202    out_stage_e: Option<crate::CudaSlice<f32>>,
8203    routes_graph: Option<RoutesGraph>,
8204    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
8205    raw_dev_route_e: Option<(u64, u64)>,
8206    raw_combine: Option<(u64, u64, u64, u64)>,
8207    raw_input: Vec<u64>,
8208    raw_sel: Vec<u64>,
8209    raw_route_w: Vec<u64>,
8210    remote: crate::CudaSlice<f32>,
8211    combined: crate::CudaSlice<f32>,
8212    n_sel: usize,
8213    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
8214    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
8215    /// BoundarySlot discipline, same as the v2 attention workspace.
8216    input: Vec<crate::CudaSlice<f32>>,
8217    ev_rank: Vec<CudaEvent>,
8218    ev_done: Option<CudaEvent>,
8219    ev_entry: Option<(CudaEvent, usize)>,
8220}
8221
8222/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
8223struct ResidentNvfp4EpRank {
8224    gate: Vec<crate::CudaSlice<u8>>,
8225    up: Vec<crate::CudaSlice<u8>>,
8226    down: Vec<crate::CudaSlice<u8>>,
8227    #[allow(dead_code)]
8228    expert_range: Range<usize>,
8229}
8230
8231pub struct ResidentNvfp4ExpertParallel {
8232    ranks: Vec<ResidentNvfp4EpRank>,
8233    macros_gate: Vec<f32>,
8234    macros_up: Vec<f32>,
8235    macros_down: Vec<f32>,
8236    pub expert_count: usize,
8237    pub input_width: usize,
8238    pub expert_width: usize,
8239    gate_row_bytes: usize,
8240    down_row_bytes: usize,
8241}
8242
8243fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8244    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
8245        matrix.codes,
8246        matrix.scales,
8247        matrix.out_features,
8248        matrix.in_features,
8249    )
8250}
8251
8252fn nvfp4_row_bytes(in_features: usize) -> usize {
8253    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
8254}
8255
8256/// MEMRA_NVFP4_BANK_V2=1: store the contiguous expert banks in the slot-major layout the
8257/// coalesced `*_v2` kernels read (see qmatvec.cu). Pure byte permutation — value-exact.
8258/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
8259/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
8260/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
8261/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
8262/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
8263pub(crate) fn fuse_rope_append_on() -> bool {
8264    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8265    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
8266}
8267
8268pub(crate) fn no_local_shadow_on() -> bool {
8269    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8270    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
8271}
8272
8273pub(crate) fn nvfp4_bank_v2_on() -> bool {
8274    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8275    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_BANK_V2").as_deref() == Ok("1"))
8276}
8277
8278/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
8279/// into the slot-major v2 row layout: per row, slot g's 16 qs bytes at g*16, then the two
8280/// UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count unchanged.
8281fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
8282    let row_bytes = nvfp4_row_bytes(in_features);
8283    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
8284    let n_slots = in_features / 32;
8285    let mut out = Vec::with_capacity(v1.len());
8286    for row in 0..out_features {
8287        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
8288        for g in 0..n_slots {
8289            let (sblk, h) = (g / 2, g % 2);
8290            let b = &r[sblk * 36..sblk * 36 + 36];
8291            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
8292        }
8293        for g in 0..n_slots {
8294            let (sblk, h) = (g / 2, g % 2);
8295            let b = &r[sblk * 36..sblk * 36 + 36];
8296            out.push(b[2 * h]);
8297            out.push(b[2 * h + 1]);
8298        }
8299    }
8300    out
8301}
8302
8303/// Repack + (optionally) v2-permute one expert shard for the contiguous banks.
8304fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8305    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
8306    let v1 = nvfp4_repack_matrix(matrix);
8307    if nvfp4_bank_v2_on() {
8308        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
8309    } else {
8310        v1
8311    }
8312}
8313
8314/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
8315/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
8316fn nvfp4_column_shard<'a>(
8317    matrix: Nvfp4BlockMatrix<'a>,
8318    tp: usize,
8319    rank: usize,
8320) -> Result<Nvfp4BlockMatrix<'a>, String> {
8321    if matrix.out_features % tp != 0 {
8322        return Err(format!(
8323            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
8324            matrix.out_features
8325        ));
8326    }
8327    let local_out = matrix.out_features / tp;
8328    let code_row = matrix.in_features / 2;
8329    let scale_row = matrix.in_features / 16;
8330    Ok(Nvfp4BlockMatrix {
8331        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
8332        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
8333        macro_scale: matrix.macro_scale,
8334        out_features: local_out,
8335        in_features: matrix.in_features,
8336    })
8337}
8338
8339/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
8340/// row contributes one contiguous byte window, gathered across rows.
8341fn nvfp4_row_shard(
8342    matrix: Nvfp4BlockMatrix<'_>,
8343    tp: usize,
8344    rank: usize,
8345) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
8346    if matrix.in_features % tp != 0 {
8347        return Err(format!(
8348            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
8349            matrix.in_features
8350        ));
8351    }
8352    let local_in = matrix.in_features / tp;
8353    if local_in % 64 != 0 {
8354        return Err(format!(
8355            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
8356        ));
8357    }
8358    let code_row = matrix.in_features / 2;
8359    let scale_row = matrix.in_features / 16;
8360    let local_code = local_in / 2;
8361    let local_scale = local_in / 16;
8362    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
8363    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
8364    for row in 0..matrix.out_features {
8365        let code_start = row * code_row + rank * local_code;
8366        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
8367        let scale_start = row * scale_row + rank * local_scale;
8368        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
8369    }
8370    Ok((codes, scales, local_in))
8371}
8372
8373/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
8374/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
8375/// point (see the section header).
8376fn run_rank_nvfp4(
8377    engine: &Engine,
8378    matrix: Nvfp4BlockMatrix<'_>,
8379    activations: &[f32],
8380    tokens: usize,
8381) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8382    matrix.validate()?;
8383    validate_activations(activations, tokens, matrix.in_features)?;
8384    let _main = engine.gpu.enter_main()?;
8385    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
8386    let activations = engine.htod(activations)?;
8387    let output = engine.qmatvec_nvfp4_fast(
8388        &blocks.slice(0..blocks.len()),
8389        &activations,
8390        tokens,
8391        matrix.in_features,
8392        matrix.out_features,
8393        nvfp4_row_bytes(matrix.in_features),
8394    )?;
8395    engine.dtoh(&output)
8396}
8397
8398fn upload_rank_nvfp4(
8399    engine: &Engine,
8400    matrix: Nvfp4BlockMatrix<'_>,
8401) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
8402    matrix.validate()?;
8403    let _main = engine.gpu.enter_main()?;
8404    Ok(ResidentNvfp4Rank {
8405        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
8406        macro_scale: matrix.macro_scale,
8407        out_features: matrix.out_features,
8408        in_features: matrix.in_features,
8409        row_bytes: nvfp4_row_bytes(matrix.in_features),
8410    })
8411}
8412
8413fn run_resident_rank_nvfp4(
8414    engine: &Engine,
8415    rank: &ResidentNvfp4Rank,
8416    activations: &[f32],
8417    tokens: usize,
8418) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8419    validate_activations(activations, tokens, rank.in_features)?;
8420    let _main = engine.gpu.enter_main()?;
8421    let activations = engine.htod(activations)?;
8422    let output = engine.qmatvec_nvfp4_fast(
8423        &rank.blocks.slice(0..rank.blocks.len()),
8424        &activations,
8425        tokens,
8426        rank.in_features,
8427        rank.out_features,
8428        rank.row_bytes,
8429    )?;
8430    engine.dtoh(&output)
8431}
8432
8433fn apply_macro(values: &mut [f32], macro_scale: f32) {
8434    for value in values.iter_mut() {
8435        *value *= macro_scale;
8436    }
8437}
8438
8439impl TpE4m3HostBounce {
8440    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
8441    pub fn full_nvfp4(
8442        &self,
8443        matrix: Nvfp4BlockMatrix<'_>,
8444        activations: &[f32],
8445        tokens: usize,
8446    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8447        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
8448        apply_macro(&mut output, matrix.macro_scale);
8449        Ok(output)
8450    }
8451
8452    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
8453    /// order, macro applied ONCE post-gather.
8454    pub fn column_parallel_nvfp4(
8455        &self,
8456        matrix: Nvfp4BlockMatrix<'_>,
8457        activations: &[f32],
8458        tokens: usize,
8459    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
8460        matrix.validate()?;
8461        validate_activations(activations, tokens, matrix.in_features)?;
8462        let tp = self.ranks.len();
8463        let local_out = matrix.out_features / tp;
8464        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
8465        let mut rank_outputs = Vec::with_capacity(tp);
8466        for (rank_index, rank) in self.ranks.iter().enumerate() {
8467            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
8468            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
8469            let row_start = rank_index * local_out;
8470            for token in 0..tokens {
8471                gathered[token * matrix.out_features + row_start
8472                    ..token * matrix.out_features + row_start + local_out]
8473                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
8474            }
8475            rank_outputs.push(output);
8476        }
8477        apply_macro(&mut gathered, matrix.macro_scale);
8478        Ok(ColumnParallelResult {
8479            gathered,
8480            rank_outputs,
8481        })
8482    }
8483
8484    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
8485    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
8486    pub fn row_parallel_nvfp4(
8487        &self,
8488        matrix: Nvfp4BlockMatrix<'_>,
8489        activations: &[f32],
8490        tokens: usize,
8491    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
8492        matrix.validate()?;
8493        validate_activations(activations, tokens, matrix.in_features)?;
8494        let tp = self.ranks.len();
8495        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
8496        let mut rank_partials = Vec::with_capacity(tp);
8497        for (rank_index, rank) in self.ranks.iter().enumerate() {
8498            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
8499            let local_activations =
8500                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
8501            let shard = Nvfp4BlockMatrix {
8502                codes: &codes,
8503                scales: &scales,
8504                macro_scale: matrix.macro_scale,
8505                out_features: matrix.out_features,
8506                in_features: local_in,
8507            };
8508            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
8509            for (sum, value) in reduced.iter_mut().zip(&partial) {
8510                *sum += *value;
8511            }
8512            rank_partials.push(partial);
8513        }
8514        apply_macro(&mut reduced, matrix.macro_scale);
8515        Ok(RowParallelResult {
8516            reduced,
8517            rank_partials,
8518        })
8519    }
8520
8521    pub fn upload_expert_nvfp4(
8522        &self,
8523        gate: Nvfp4BlockMatrix<'_>,
8524        up: Nvfp4BlockMatrix<'_>,
8525        down: Nvfp4BlockMatrix<'_>,
8526    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
8527        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8528            return Err("NVFP4 TP expert gate/up dimensions differ".into());
8529        }
8530        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8531            return Err(format!(
8532                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
8533                down.out_features, down.in_features, gate.out_features, gate.in_features
8534            )
8535            .into());
8536        }
8537        let tp = self.ranks.len();
8538        let mut gate_ranks = Vec::with_capacity(tp);
8539        let mut up_ranks = Vec::with_capacity(tp);
8540        let mut down_ranks = Vec::with_capacity(tp);
8541        for (rank_index, engine) in self.ranks.iter().enumerate() {
8542            gate_ranks.push(upload_rank_nvfp4(
8543                engine,
8544                nvfp4_column_shard(gate, tp, rank_index)?,
8545            )?);
8546            up_ranks.push(upload_rank_nvfp4(
8547                engine,
8548                nvfp4_column_shard(up, tp, rank_index)?,
8549            )?);
8550            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
8551            down_ranks.push(upload_rank_nvfp4(
8552                engine,
8553                Nvfp4BlockMatrix {
8554                    codes: &codes,
8555                    scales: &scales,
8556                    macro_scale: down.macro_scale,
8557                    out_features: down.out_features,
8558                    in_features: local_in,
8559                },
8560            )?);
8561        }
8562        Ok(ResidentTpNvfp4Expert {
8563            gate: ResidentNvfp4ColumnParallel {
8564                ranks: gate_ranks,
8565                out_features: gate.out_features,
8566                in_features: gate.in_features,
8567            },
8568            up: ResidentNvfp4ColumnParallel {
8569                ranks: up_ranks,
8570                out_features: up.out_features,
8571                in_features: up.in_features,
8572            },
8573            down: ResidentNvfp4RowParallel {
8574                ranks: down_ranks,
8575                out_features: down.out_features,
8576                in_features: down.in_features,
8577            },
8578            input_width: gate.in_features,
8579            expert_width: gate.out_features,
8580        })
8581    }
8582
8583    fn column_parallel_resident_nvfp4(
8584        &self,
8585        matrix: &ResidentNvfp4ColumnParallel,
8586        activations: &[f32],
8587        tokens: usize,
8588    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8589        validate_activations(activations, tokens, matrix.in_features)?;
8590        let local_out = matrix.out_features / self.ranks.len();
8591        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
8592        let mut macro_scale = None;
8593        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
8594            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
8595            let row_start = rank_index * local_out;
8596            for token in 0..tokens {
8597                gathered[token * matrix.out_features + row_start
8598                    ..token * matrix.out_features + row_start + local_out]
8599                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
8600            }
8601            macro_scale = Some(shard.macro_scale);
8602        }
8603        apply_macro(
8604            &mut gathered,
8605            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
8606        );
8607        Ok(gathered)
8608    }
8609
8610    fn row_parallel_resident_nvfp4(
8611        &self,
8612        matrix: &ResidentNvfp4RowParallel,
8613        activations: &[f32],
8614        tokens: usize,
8615    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8616        validate_activations(activations, tokens, matrix.in_features)?;
8617        let tp = self.ranks.len();
8618        let local_in = matrix.in_features / tp;
8619        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
8620        let mut macro_scale = None;
8621        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
8622            if shard.in_features != local_in {
8623                return Err(format!(
8624                    "NVFP4 resident row shard in_features {} != expected {local_in}",
8625                    shard.in_features
8626                )
8627                .into());
8628            }
8629            let local_activations =
8630                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
8631            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
8632            for (sum, value) in reduced.iter_mut().zip(&partial) {
8633                *sum += *value;
8634            }
8635            macro_scale = Some(shard.macro_scale);
8636        }
8637        apply_macro(
8638            &mut reduced,
8639            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
8640        );
8641        Ok(reduced)
8642    }
8643
8644    pub fn run_expert_nvfp4(
8645        &self,
8646        expert: &ResidentTpNvfp4Expert,
8647        input: &[f32],
8648        tokens: usize,
8649    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8650        validate_activations(input, tokens, expert.input_width)?;
8651        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
8652        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
8653        let activated: Vec<f32> = gate
8654            .iter()
8655            .zip(&up)
8656            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
8657            .collect();
8658        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
8659        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
8660    }
8661
8662    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
8663    pub fn upload_tensor_parallel_nvfp4(
8664        &self,
8665        gate: Nvfp4ExpertBank<'_>,
8666        up: Nvfp4ExpertBank<'_>,
8667        down: Nvfp4ExpertBank<'_>,
8668    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
8669        gate.validate()?;
8670        up.validate()?;
8671        down.validate()?;
8672        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
8673            return Err("NVFP4 TP gate/up/down expert counts differ".into());
8674        }
8675        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8676            return Err("NVFP4 TP gate/up dimensions differ".into());
8677        }
8678        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8679            return Err(format!(
8680                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
8681                down.out_features, down.in_features, gate.out_features, gate.in_features
8682            )
8683            .into());
8684        }
8685        let tp = self.ranks.len();
8686        if gate.out_features % tp != 0 {
8687            return Err(format!(
8688                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
8689                gate.out_features
8690            )
8691            .into());
8692        }
8693        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
8694            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
8695        {
8696            return Err(format!(
8697                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
8698                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
8699                down.in_features
8700            )
8701            .into());
8702        }
8703        if tp > NVFP4_CANONICAL_ROW_SHARDS {
8704            return Err(format!(
8705                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
8706                 ({NVFP4_CANONICAL_ROW_SHARDS})"
8707            )
8708            .into());
8709        }
8710
8711        let mut gate_ranks = Vec::with_capacity(tp);
8712        let mut up_ranks = Vec::with_capacity(tp);
8713        let mut macros_gate_dev = Vec::with_capacity(tp);
8714        let mut macros_up_dev = Vec::with_capacity(tp);
8715        let mut macros_down_dev = Vec::with_capacity(tp);
8716        for (rank_index, engine) in self.ranks.iter().enumerate() {
8717            let _main = engine.gpu.enter_main()?;
8718            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
8719            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
8720            // are unchanged (same repack).
8721            let mut gate_host: Vec<u8> = Vec::new();
8722            let mut up_host: Vec<u8> = Vec::new();
8723            for expert in 0..gate.expert_count {
8724                let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
8725                gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard));
8726                let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
8727                up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard));
8728            }
8729            let gate_expert_bytes = gate_host.len() / gate.expert_count;
8730            let up_expert_bytes = up_host.len() / up.expert_count;
8731            gate_ranks.push(ResidentNvfp4ColumnBankRank {
8732                bank: engine.htod_bytes(&gate_host)?,
8733                expert_bytes: gate_expert_bytes,
8734                local_out: gate.out_features / tp,
8735                in_features: gate.in_features,
8736                row_bytes: nvfp4_row_bytes(gate.in_features),
8737            });
8738            up_ranks.push(ResidentNvfp4ColumnBankRank {
8739                bank: engine.htod_bytes(&up_host)?,
8740                expert_bytes: up_expert_bytes,
8741                local_out: up.out_features / tp,
8742                in_features: up.in_features,
8743                row_bytes: nvfp4_row_bytes(up.in_features),
8744            });
8745            macros_gate_dev.push(engine.htod(gate.macros)?);
8746            macros_up_dev.push(engine.htod(up.macros)?);
8747            macros_down_dev.push(engine.htod(down.macros)?);
8748        }
8749        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
8750        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
8751        // execution and reduction order stay identical.
8752        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
8753        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
8754            let device_rank = shard_index % tp;
8755            let engine = &self.ranks[device_rank];
8756            let _main = engine.gpu.enter_main()?;
8757            let mut down_host: Vec<u8> = Vec::new();
8758            for expert in 0..down.expert_count {
8759                let down_matrix = down.expert(expert)?;
8760                let (codes, scales, local_in) =
8761                    nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
8762                down_host.extend_from_slice(&nvfp4_repack_bank_matrix(Nvfp4BlockMatrix {
8763                    codes: &codes,
8764                    scales: &scales,
8765                    macro_scale: down_matrix.macro_scale,
8766                    out_features: down_matrix.out_features,
8767                    in_features: local_in,
8768                }));
8769            }
8770            let down_expert_bytes = down_host.len() / down.expert_count;
8771            down_ranks.push(ResidentNvfp4RowBankRank {
8772                bank: engine.htod_bytes(&down_host)?,
8773                expert_bytes: down_expert_bytes,
8774                device_rank,
8775                out_features: down.out_features,
8776                local_in: down.in_features / NVFP4_CANONICAL_ROW_SHARDS,
8777                row_bytes: nvfp4_row_bytes(down.in_features / NVFP4_CANONICAL_ROW_SHARDS),
8778            });
8779        }
8780        Ok(ResidentNvfp4TensorParallel {
8781            gate: gate_ranks,
8782            up: up_ranks,
8783            down: down_ranks,
8784            macros_gate: gate.macros.to_vec(),
8785            macros_up: up.macros.to_vec(),
8786            macros_down: down.macros.to_vec(),
8787            macros_gate_dev,
8788            macros_up_dev,
8789            macros_down_dev,
8790            expert_count: gate.expert_count,
8791            input_width: gate.in_features,
8792            expert_width: gate.out_features,
8793            device_workspace: std::sync::Mutex::new(None),
8794            t2_workspace: std::sync::Mutex::new(None),
8795        })
8796    }
8797
8798    fn run_column_bank_expert_nvfp4(
8799        &self,
8800        ranks: &[ResidentNvfp4ColumnBankRank],
8801        macros: &[f32],
8802        expert: usize,
8803        input: &[f32],
8804    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8805        let local_out = ranks
8806            .first()
8807            .ok_or("NVFP4 TP column bank has no ranks")?
8808            .local_out;
8809        let mut gathered = vec![0.0f32; local_out * ranks.len()];
8810        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
8811            let _main = engine.gpu.enter_main()?;
8812            let activations = engine.htod(input)?;
8813            let output = if nvfp4_bank_v2_on() {
8814                engine.qmatvec_nvfp4_fast_v2(
8815                    &bank.expert(expert),
8816                    &activations,
8817                    1,
8818                    bank.in_features,
8819                    bank.local_out,
8820                    bank.row_bytes,
8821                )?
8822            } else {
8823                engine.qmatvec_nvfp4_fast(
8824                    &bank.expert(expert),
8825                    &activations,
8826                    1,
8827                    bank.in_features,
8828                    bank.local_out,
8829                    bank.row_bytes,
8830                )?
8831            };
8832            let output = engine.dtoh(&output)?;
8833            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
8834        }
8835        apply_macro(&mut gathered, macros[expert]);
8836        Ok(gathered)
8837    }
8838
8839    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
8840    /// executes on its owning rank engine), so the reduction parenthesization is identical at
8841    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
8842    fn run_row_bank_expert_nvfp4(
8843        &self,
8844        shards: &[ResidentNvfp4RowBankRank],
8845        macros: &[f32],
8846        expert: usize,
8847        input: &[f32],
8848    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8849        let out_features = shards
8850            .first()
8851            .ok_or("NVFP4 TP row bank has no canonical shards")?
8852            .out_features;
8853        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
8854        let mut reduced = vec![0.0f32; out_features];
8855        for (shard_index, shard) in shards.iter().enumerate() {
8856            let engine = self
8857                .ranks
8858                .get(shard.device_rank)
8859                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
8860            let _main = engine.gpu.enter_main()?;
8861            let local_activations =
8862                activation_shard(input, 1, in_features, shards.len(), shard_index);
8863            let activations = engine.htod(&local_activations)?;
8864            let output = if nvfp4_bank_v2_on() {
8865                engine.qmatvec_nvfp4_fast_v2(
8866                    &shard.expert(expert),
8867                    &activations,
8868                    1,
8869                    shard.local_in,
8870                    shard.out_features,
8871                    shard.row_bytes,
8872                )?
8873            } else {
8874                engine.qmatvec_nvfp4_fast(
8875                    &shard.expert(expert),
8876                    &activations,
8877                    1,
8878                    shard.local_in,
8879                    shard.out_features,
8880                    shard.row_bytes,
8881                )?
8882            };
8883            let partial = engine.dtoh(&output)?;
8884            for (sum, value) in reduced.iter_mut().zip(&partial) {
8885                *sum += *value;
8886            }
8887        }
8888        apply_macro(&mut reduced, macros[expert]);
8889        Ok(reduced)
8890    }
8891
8892    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
8893    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
8894    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
8895    pub fn upload_expert_parallel_nvfp4(
8896        &self,
8897        gate: Nvfp4ExpertBank<'_>,
8898        up: Nvfp4ExpertBank<'_>,
8899        down: Nvfp4ExpertBank<'_>,
8900    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
8901        gate.validate()?;
8902        up.validate()?;
8903        down.validate()?;
8904        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
8905            return Err("NVFP4 EP gate/up/down expert counts differ".into());
8906        }
8907        if gate.in_features != up.in_features || gate.out_features != up.out_features {
8908            return Err("NVFP4 EP gate/up dimensions differ".into());
8909        }
8910        if down.in_features != gate.out_features || down.out_features != gate.in_features {
8911            return Err(format!(
8912                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
8913                down.out_features, down.in_features, gate.out_features, gate.in_features
8914            )
8915            .into());
8916        }
8917        let world = self.ranks.len();
8918        if gate.expert_count % world != 0 {
8919            return Err(format!(
8920                "NVFP4 EP expert count {} is not divisible by {world} ranks",
8921                gate.expert_count
8922            )
8923            .into());
8924        }
8925        let experts_per_rank = gate.expert_count / world;
8926        let mut ranks = Vec::with_capacity(world);
8927        for (rank_index, engine) in self.ranks.iter().enumerate() {
8928            let _main = engine.gpu.enter_main()?;
8929            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
8930            let mut gate_experts = Vec::with_capacity(experts_per_rank);
8931            let mut up_experts = Vec::with_capacity(experts_per_rank);
8932            let mut down_experts = Vec::with_capacity(experts_per_rank);
8933            for expert in expert_range.clone() {
8934                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
8935                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
8936                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
8937            }
8938            ranks.push(ResidentNvfp4EpRank {
8939                gate: gate_experts,
8940                up: up_experts,
8941                down: down_experts,
8942                expert_range,
8943            });
8944        }
8945        Ok(ResidentNvfp4ExpertParallel {
8946            ranks,
8947            macros_gate: gate.macros.to_vec(),
8948            macros_up: up.macros.to_vec(),
8949            macros_down: down.macros.to_vec(),
8950            expert_count: gate.expert_count,
8951            input_width: gate.in_features,
8952            expert_width: gate.out_features,
8953            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
8954            down_row_bytes: nvfp4_row_bytes(down.in_features),
8955        })
8956    }
8957
8958    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
8959    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
8960    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
8961    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
8962    /// contract. Exactness-first; no throughput claim.
8963    #[allow(clippy::too_many_arguments)]
8964    pub fn run_routed_experts_nvfp4(
8965        &self,
8966        experts: &ResidentNvfp4ExpertParallel,
8967        input: &[f32],
8968        tokens: usize,
8969        selected: &[usize],
8970        route_weights: &[f32],
8971        experts_per_token: usize,
8972        activation_limit: Option<f32>,
8973    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8974        validate_activations(input, tokens, experts.input_width)?;
8975        let pairs = tokens
8976            .checked_mul(experts_per_token)
8977            .ok_or("NVFP4 EP route count overflow")?;
8978        if selected.len() != pairs || route_weights.len() != pairs {
8979            return Err(format!(
8980                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
8981                 {experts_per_token} ({pairs})",
8982                selected.len(),
8983                route_weights.len(),
8984            )
8985            .into());
8986        }
8987        if !route_weights.iter().all(|weight| weight.is_finite()) {
8988            return Err("NVFP4 EP route weights contain a non-finite value".into());
8989        }
8990        let experts_per_rank = experts.expert_count / experts.ranks.len();
8991        let mut output = vec![0.0f32; tokens * experts.input_width];
8992        for token in 0..tokens {
8993            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
8994            for slot in 0..experts_per_token {
8995                let pair = token * experts_per_token + slot;
8996                let expert = selected[pair];
8997                if expert >= experts.expert_count {
8998                    return Err(format!(
8999                        "NVFP4 EP selected expert {expert} outside 0..{}",
9000                        experts.expert_count
9001                    )
9002                    .into());
9003                }
9004                let owner = expert / experts_per_rank;
9005                let local = expert - owner * experts_per_rank;
9006                let rank = &experts.ranks[owner];
9007                let engine = &self.ranks[owner];
9008                let _main = engine.gpu.enter_main()?;
9009                let device_input = engine.htod(input_row)?;
9010                let gate_out = engine.qmatvec_nvfp4_fast(
9011                    &rank.gate[local].slice(0..rank.gate[local].len()),
9012                    &device_input,
9013                    1,
9014                    experts.input_width,
9015                    experts.expert_width,
9016                    experts.gate_row_bytes,
9017                )?;
9018                let up_out = engine.qmatvec_nvfp4_fast(
9019                    &rank.up[local].slice(0..rank.up[local].len()),
9020                    &device_input,
9021                    1,
9022                    experts.input_width,
9023                    experts.expert_width,
9024                    experts.gate_row_bytes,
9025                )?;
9026                let mut gate_host = engine.dtoh(&gate_out)?;
9027                let mut up_host = engine.dtoh(&up_out)?;
9028                apply_macro(&mut gate_host, experts.macros_gate[expert]);
9029                apply_macro(&mut up_host, experts.macros_up[expert]);
9030                let activated: Vec<f32> = gate_host
9031                    .iter()
9032                    .zip(&up_host)
9033                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
9034                    .collect();
9035                let device_activated = engine.htod(&activated)?;
9036                let down_out = engine.qmatvec_nvfp4_fast(
9037                    &rank.down[local].slice(0..rank.down[local].len()),
9038                    &device_activated,
9039                    1,
9040                    experts.expert_width,
9041                    experts.input_width,
9042                    experts.down_row_bytes,
9043                )?;
9044                let mut down_host = engine.dtoh(&down_out)?;
9045                apply_macro(&mut down_host, experts.macros_down[expert]);
9046                let weight = route_weights[pair];
9047                for (sum, value) in output
9048                    [token * experts.input_width..(token + 1) * experts.input_width]
9049                    .iter_mut()
9050                    .zip(down_host)
9051                {
9052                    *sum += weight * value;
9053                }
9054            }
9055        }
9056        Ok(output)
9057    }
9058
9059    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
9060    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
9061    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
9062    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
9063    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
9064    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
9065    ///
9066    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
9067    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
9068    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
9069    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
9070    /// host-canonical oracle, and with repeat determinism against itself.
9071    /// Clamped layers refuse (they stay on the EP program).
9072    pub fn run_tensor_parallel_routes_nvfp4_device(
9073        &self,
9074        experts: &ResidentNvfp4TensorParallel,
9075        input: &[f32],
9076        selected: &[usize],
9077        route_weights: &[f32],
9078        experts_per_token: usize,
9079        activation_limit: Option<f32>,
9080    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9081        validate_activations(input, 1, experts.input_width)?;
9082        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9083            return Err(format!(
9084                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
9085                selected.len(),
9086                route_weights.len(),
9087            )
9088            .into());
9089        }
9090        if !route_weights.iter().all(|weight| weight.is_finite()) {
9091            return Err("NVFP4 device route weights contain a non-finite value".into());
9092        }
9093        let world = self.ranks.len();
9094        if world != NVFP4_CANONICAL_ROW_SHARDS {
9095            return Err(format!(
9096                "NVFP4 device routes require world == canonical shard grid \
9097                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
9098            )
9099            .into());
9100        }
9101        let local_out = experts.expert_width / world;
9102
9103        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
9104        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
9105        // everything else without Nsight.
9106        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9107        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9108        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9109        let started = timing.then(std::time::Instant::now);
9110
9111        let n_sel = experts_per_token;
9112        let mut workspace_guard = experts
9113            .device_workspace
9114            .lock()
9115            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9116        if workspace_guard.is_none() {
9117            let mut gate_out = Vec::with_capacity(world);
9118            let mut up_out = Vec::with_capacity(world);
9119            let mut act_q = Vec::with_capacity(world);
9120            let mut act_d = Vec::with_capacity(world);
9121            let mut sel = Vec::with_capacity(world);
9122            let mut partial = Vec::with_capacity(world);
9123            let mut accumulator = Vec::with_capacity(world);
9124            let mut combine_w = Vec::with_capacity(world);
9125            let mut route_w = Vec::with_capacity(world);
9126            let mut in_q = Vec::with_capacity(world);
9127            let mut in_d = Vec::with_capacity(world);
9128            let mut input = Vec::with_capacity(world);
9129            let mut ev_rank = Vec::with_capacity(world);
9130            let moe_direct = moe_direct_on();
9131            for (rank, engine) in self.ranks.iter().enumerate() {
9132                let _main = engine.gpu.enter_main()?;
9133                gate_out.push(engine.uninit(n_sel * local_out)?);
9134                up_out.push(engine.uninit(n_sel * local_out)?);
9135                act_q.push(engine.uninit_i8(n_sel * local_out)?);
9136                act_d.push(engine.uninit(n_sel * local_out / 32)?);
9137                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
9138                partial.push(engine.uninit(n_sel * experts.input_width)?);
9139                // Direct join: peer accumulators live on ROOT (single P2P store pass).
9140                if moe_direct && rank != 0 {
9141                    let root = &self.ranks[0];
9142                    let _root_main = root.gpu.enter_main()?;
9143                    accumulator.push(root.zeros(experts.input_width)?);
9144                } else {
9145                    accumulator.push(engine.zeros(experts.input_width)?);
9146                }
9147                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9148                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9149                in_q.push(engine.uninit_i8(experts.input_width)?);
9150                in_d.push(engine.uninit(experts.input_width / 32)?);
9151                input.push(engine.uninit(experts.input_width)?);
9152                ev_rank.push(engine.ctx().new_event(None)?);
9153            }
9154            let root = &self.ranks[0];
9155            let _main = root.gpu.enter_main()?;
9156            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
9157                prestaged: false,
9158                rank1_routed: false,
9159                ev_input: None,
9160                fence_flags_raw: 0,
9161                fence_ticket: 0,
9162                gate_out,
9163                up_out,
9164                act_q,
9165                act_d,
9166                sel,
9167                partial,
9168                accumulator,
9169                combine_w,
9170                route_w,
9171                in_q,
9172                in_d,
9173                dev_route_e: None,
9174                in_stage_e: None,
9175                out_stage_e: None,
9176                routes_graph: None,
9177                raw_dev_route_e: None,
9178                raw_combine: None,
9179                raw_input: Vec::new(),
9180                raw_sel: Vec::new(),
9181                raw_route_w: Vec::new(),
9182                remote: root.uninit(experts.input_width)?,
9183                combined: root.uninit(experts.input_width)?,
9184                n_sel,
9185                input,
9186                ev_rank,
9187                ev_done: Some(root.ctx().new_event(None)?),
9188                ev_entry: None,
9189            });
9190        }
9191        let workspace = workspace_guard
9192            .as_mut()
9193            .expect("NVFP4 device routes workspace initialized above");
9194        if workspace.n_sel != n_sel {
9195            return Err(format!(
9196                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
9197                workspace.n_sel
9198            )
9199            .into());
9200        }
9201        for &expert in selected {
9202            if expert >= experts.expert_count {
9203                return Err(format!(
9204                    "NVFP4 device selected expert {expert} outside 0..{}",
9205                    experts.expert_count
9206                )
9207                .into());
9208            }
9209        }
9210        let sel_i32 = selected
9211            .iter()
9212            .map(|&expert| expert as i32)
9213            .collect::<Vec<_>>();
9214
9215        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
9216        // down) covers every selected expert via the selection array and the contiguous bank —
9217        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
9218        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
9219        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
9220        // accumulation order — the program's values are unchanged.
9221        for (rank_index, engine) in self.ranks.iter().enumerate() {
9222            let _main = engine.gpu.enter_main()?;
9223            let device_input = engine.htod(input)?;
9224            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
9225            engine.quantize_q8_1_into(
9226                &device_input,
9227                1,
9228                experts.input_width,
9229                &mut in_q[rank_index],
9230                &mut in_d[rank_index],
9231            )?;
9232            // device_input frees on this rank's stream after the quantize — same-stream order.
9233        }
9234        self.nvfp4_routes_batched_sweeps(
9235            experts,
9236            workspace,
9237            selected,
9238            route_weights,
9239            &sel_i32,
9240            local_out,
9241            n_sel,
9242            activation_limit,
9243            false,
9244        )?;
9245
9246        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
9247        // reduce in canonical shard order, read back once.
9248        let root = &self.ranks[0];
9249        for engine in &self.ranks[1..] {
9250            let _main = engine.gpu.enter_main()?;
9251            engine.stream().synchronize()?;
9252        }
9253        let _main = root.gpu.enter_main()?;
9254        root.stream()
9255            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
9256        root.add(
9257            &workspace.accumulator[0],
9258            &workspace.remote,
9259            &mut workspace.combined,
9260            experts.input_width,
9261        )?;
9262        let output = root.dtoh(&workspace.combined)?;
9263        if let Some(started) = started {
9264            use std::sync::atomic::Ordering;
9265            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9266                + started.elapsed().as_nanos() as u64;
9267            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9268            if calls % 430 == 0 {
9269                eprintln!(
9270                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9271                    ns as f64 / 1.0e6,
9272                    ns as f64 / calls as f64 / 1.0e3,
9273                );
9274            }
9275        }
9276        Ok(output)
9277    }
9278
9279    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
9280    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
9281    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
9282    /// owning rank's stream; callers own input acquisition and the combine.
9283    #[allow(clippy::too_many_arguments)]
9284    fn nvfp4_routes_batched_sweeps(
9285        &self,
9286        experts: &ResidentNvfp4TensorParallel,
9287        workspace: &mut Nvfp4DeviceRoutesWorkspace,
9288        selected: &[usize],
9289        route_weights: &[f32],
9290        sel_i32: &[i32],
9291        local_out: usize,
9292        n_sel: usize,
9293        activation_limit: Option<f32>,
9294        device_routed: bool,
9295    ) -> Result<(), Box<dyn std::error::Error>> {
9296        for rank_index in 0..self.ranks.len() {
9297            self.nvfp4_routes_batched_sweeps_rank(
9298                experts,
9299                workspace,
9300                selected,
9301                route_weights,
9302                sel_i32,
9303                local_out,
9304                n_sel,
9305                activation_limit,
9306                device_routed,
9307                rank_index,
9308            )?;
9309        }
9310        Ok(())
9311    }
9312
9313    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
9314    /// the graph door can capture each rank's segment on its own stream.
9315    #[allow(clippy::too_many_arguments)]
9316    fn nvfp4_routes_batched_sweeps_rank(
9317        &self,
9318        experts: &ResidentNvfp4TensorParallel,
9319        workspace: &mut Nvfp4DeviceRoutesWorkspace,
9320        selected: &[usize],
9321        route_weights: &[f32],
9322        sel_i32: &[i32],
9323        local_out: usize,
9324        n_sel: usize,
9325        activation_limit: Option<f32>,
9326        device_routed: bool,
9327        rank_index: usize,
9328    ) -> Result<(), Box<dyn std::error::Error>> {
9329        {
9330            let engine = &self.ranks[rank_index];
9331            let _main = engine.gpu.enter_main()?;
9332            if !device_routed {
9333                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
9334                // Folded combine weights (route_weight x down macro) — one 40-byte upload
9335                // replaces the accumulator reset + n_sel sequential axpy launches below.
9336                let folded = (0..n_sel)
9337                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
9338                    .collect::<Vec<_>>();
9339                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
9340                engine.stream().memcpy_htod(&folded, &mut view)?;
9341            }
9342            let gate_bank = &experts.gate[rank_index];
9343            let up_bank = &experts.up[rank_index];
9344            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
9345            // FUSION #2a (v2 banks): the two sweeps share sel/aq/ad and identical geometry
9346            // — one launch, per-row bit-identical, double the grid fill.
9347            let gu_fused = nvfp4_bank_v2_on()
9348                && gate_bank.in_features == up_bank.in_features
9349                && gate_bank.local_out == up_bank.local_out
9350                && gate_bank.row_bytes == up_bank.row_bytes
9351                && gate_bank.expert_bytes == up_bank.expert_bytes;
9352            if gu_fused {
9353                let Nvfp4DeviceRoutesWorkspace {
9354                    sel,
9355                    gate_out,
9356                    up_out,
9357                    in_q,
9358                    in_d,
9359                    ..
9360                } = &mut *workspace;
9361                engine.qmatvec_nvfp4_sel_gu_into(
9362                    &gate_bank.bank,
9363                    &up_bank.bank,
9364                    &sel[rank_index],
9365                    &in_q[rank_index],
9366                    &in_d[rank_index],
9367                    &mut gate_out[rank_index],
9368                    &mut up_out[rank_index],
9369                    n_sel,
9370                    gate_bank.in_features,
9371                    gate_bank.local_out,
9372                    gate_bank.row_bytes,
9373                    gate_bank.expert_bytes,
9374                )?;
9375            } else {
9376                engine.qmatvec_nvfp4_sel_into(
9377                    &gate_bank.bank,
9378                    &workspace.sel[rank_index],
9379                    aq,
9380                    ad,
9381                    &mut workspace.gate_out[rank_index],
9382                    n_sel,
9383                    gate_bank.in_features,
9384                    gate_bank.local_out,
9385                    gate_bank.row_bytes,
9386                    gate_bank.expert_bytes,
9387                    0,
9388                    0,
9389                )?;
9390                engine.qmatvec_nvfp4_sel_into(
9391                    &up_bank.bank,
9392                    &workspace.sel[rank_index],
9393                    aq,
9394                    ad,
9395                    &mut workspace.up_out[rank_index],
9396                    n_sel,
9397                    up_bank.in_features,
9398                    up_bank.local_out,
9399                    up_bank.row_bytes,
9400                    up_bank.expert_bytes,
9401                    0,
9402                    0,
9403                )?;
9404            }
9405            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
9406            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
9407            // input-column window (the geometry gift; see the method doc).
9408            {
9409                let Nvfp4DeviceRoutesWorkspace {
9410                    gate_out,
9411                    up_out,
9412                    sel,
9413                    act_q,
9414                    act_d,
9415                    ..
9416                } = &mut *workspace;
9417                engine.silu_mul_scaled_q8_1_sel_into(
9418                    &gate_out[rank_index],
9419                    &up_out[rank_index],
9420                    &experts.macros_gate_dev[rank_index],
9421                    &experts.macros_up_dev[rank_index],
9422                    &sel[rank_index],
9423                    activation_limit,
9424                    &mut act_q[rank_index],
9425                    &mut act_d[rank_index],
9426                    local_out,
9427                    n_sel,
9428                )?;
9429            }
9430            let shard = &experts.down[rank_index];
9431            if shard.device_rank != rank_index || shard.local_in != local_out {
9432                return Err(
9433                    "NVFP4 device routes: down canonical shard placement drifted from \
9434                     the gate/up column split"
9435                        .into(),
9436                );
9437            }
9438            // MEMRA_SEL_DOWN8=1: down sweep + route-weight combine in ONE launch, one warp
9439            // per SLOT instead of one warp per (row, slot) — the q8 `down8 w8` occupancy arm
9440            // (cx-downkernel: waves/SM 0.91 -> 4.36) ported to the NVFP4 banks. Bit-identical
9441            // (same dot program, same reduce tree, same slot-ordered chain), and the
9442            // n_sel x out_f partial buffer round trip disappears. Device-routed only: the
9443            // host-routed arm folds the macro into combine_w instead of reading md on device.
9444            let down8 = device_routed && sel_down8_on() && (shard.local_in >> 5) <= 32;
9445            if down8 {
9446                let Nvfp4DeviceRoutesWorkspace {
9447                    sel,
9448                    act_q,
9449                    act_d,
9450                    route_w,
9451                    accumulator,
9452                    ..
9453                } = &mut *workspace;
9454                engine.qmatvec_nvfp4_sel_down8_into(
9455                    &shard.bank,
9456                    &sel[rank_index],
9457                    &act_q[rank_index],
9458                    &act_d[rank_index],
9459                    &route_w[rank_index],
9460                    &experts.macros_down_dev[rank_index],
9461                    &mut accumulator[rank_index],
9462                    n_sel,
9463                    shard.local_in,
9464                    shard.out_features,
9465                    shard.row_bytes,
9466                    shard.expert_bytes,
9467                    local_out,
9468                    local_out / 32,
9469                )?;
9470            } else {
9471                let Nvfp4DeviceRoutesWorkspace {
9472                    sel,
9473                    act_q,
9474                    act_d,
9475                    partial,
9476                    ..
9477                } = &mut *workspace;
9478                engine.qmatvec_nvfp4_sel_into(
9479                    &shard.bank,
9480                    &sel[rank_index],
9481                    &act_q[rank_index],
9482                    &act_d[rank_index],
9483                    &mut partial[rank_index],
9484                    n_sel,
9485                    shard.local_in,
9486                    shard.out_features,
9487                    shard.row_bytes,
9488                    shard.expert_bytes,
9489                    local_out,
9490                    local_out / 32,
9491                )?;
9492            }
9493            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
9494            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
9495            // fold the down macro in-kernel from the device selection. (down8 already
9496            // produced the accumulator inside the sweep.)
9497            if !down8 {
9498                let Nvfp4DeviceRoutesWorkspace {
9499                    partial,
9500                    combine_w,
9501                    route_w,
9502                    sel,
9503                    accumulator,
9504                    ..
9505                } = &mut *workspace;
9506                if device_routed {
9507                    engine.axpy_rows_seq_md_into(
9508                        &partial[rank_index],
9509                        &route_w[rank_index],
9510                        &experts.macros_down_dev[rank_index],
9511                        &sel[rank_index],
9512                        &mut accumulator[rank_index],
9513                        experts.input_width,
9514                        n_sel,
9515                    )?;
9516                } else {
9517                    engine.axpy_rows_seq_into(
9518                        &partial[rank_index],
9519                        &combine_w[rank_index],
9520                        &mut accumulator[rank_index],
9521                        experts.input_width,
9522                        n_sel,
9523                    )?;
9524                }
9525            }
9526        }
9527        Ok(())
9528    }
9529
9530    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
9531    /// a device row on the model engine `e` and the combined output returns as a fresh
9532    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
9533    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
9534    /// the input's producer; each rank waits it before its peer read; the root reduce waits
9535    /// every rank's done event; `e` waits the root's done event before copying out. The
9536    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
9537    pub fn run_tensor_parallel_routes_nvfp4_device_io(
9538        &self,
9539        experts: &ResidentNvfp4TensorParallel,
9540        e: &Engine,
9541        input_dev: &crate::CudaSlice<f32>,
9542        selected: &[usize],
9543        route_weights: &[f32],
9544        experts_per_token: usize,
9545        activation_limit: Option<f32>,
9546    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
9547        if input_dev.len() != experts.input_width {
9548            return Err(format!(
9549                "NVFP4 device-io routes input {} != width {}",
9550                input_dev.len(),
9551                experts.input_width
9552            )
9553            .into());
9554        }
9555        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9556            return Err(format!(
9557                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
9558                selected.len(),
9559                route_weights.len(),
9560            )
9561            .into());
9562        }
9563        if !route_weights.iter().all(|weight| weight.is_finite()) {
9564            return Err("NVFP4 device route weights contain a non-finite value".into());
9565        }
9566        let world = self.ranks.len();
9567        if world != NVFP4_CANONICAL_ROW_SHARDS {
9568            return Err(format!(
9569                "NVFP4 device routes require world == canonical shard grid \
9570                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
9571            )
9572            .into());
9573        }
9574        let local_out = experts.expert_width / world;
9575        let n_sel = experts_per_token;
9576
9577        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9578        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9579        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9580        let started = timing.then(std::time::Instant::now);
9581
9582        let mut workspace_guard = experts
9583            .device_workspace
9584            .lock()
9585            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9586        if workspace_guard.is_none() {
9587            drop(workspace_guard);
9588            // Build through the host-IO ensure path exactly once: run it with a zero input.
9589            // Cheaper than duplicating the init; the first real call overwrites everything.
9590            let zero = vec![0.0f32; experts.input_width];
9591            let zero_sel = vec![0usize; n_sel];
9592            let zero_w = vec![0.0f32; n_sel];
9593            let _ = self.run_tensor_parallel_routes_nvfp4_device(
9594                experts,
9595                &zero,
9596                &zero_sel,
9597                &zero_w,
9598                n_sel,
9599                activation_limit,
9600            )?;
9601            workspace_guard = experts
9602                .device_workspace
9603                .lock()
9604                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9605        }
9606        let workspace = workspace_guard
9607            .as_mut()
9608            .expect("NVFP4 device routes workspace initialized above");
9609        if workspace.n_sel != n_sel {
9610            return Err(format!(
9611                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
9612                workspace.n_sel
9613            )
9614            .into());
9615        }
9616        for &expert in selected {
9617            if expert >= experts.expert_count {
9618                return Err(format!(
9619                    "NVFP4 device selected expert {expert} outside 0..{}",
9620                    experts.expert_count
9621                )
9622                .into());
9623            }
9624        }
9625        let sel_i32 = selected
9626            .iter()
9627            .map(|&expert| expert as i32)
9628            .collect::<Vec<_>>();
9629
9630        // Entry fence: e's stream position covers the input's producer AND every consumer of
9631        // the previous layer's output (queued on e's stream before this call), guarding the
9632        // workspace reuse exactly like the v2 attention driver.
9633        if let Some((_, device)) = workspace.ev_entry.as_ref() {
9634            if *device != e.ctx().ordinal() {
9635                return Err("NVFP4 device-io routes engine changed".into());
9636            }
9637        } else {
9638            let _main = e.gpu.enter_main()?;
9639            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
9640        }
9641        {
9642            let _main = e.gpu.enter_main()?;
9643            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
9644            ev_entry.record(&e.stream())?;
9645        }
9646        for (rank_index, engine) in self.ranks.iter().enumerate() {
9647            let _main = engine.gpu.enter_main()?;
9648            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
9649            engine.stream().wait(ev_entry)?;
9650            {
9651                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
9652                engine
9653                    .stream()
9654                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
9655            }
9656            {
9657                let Nvfp4DeviceRoutesWorkspace {
9658                    input, in_q, in_d, ..
9659                } = &mut *workspace;
9660                engine.quantize_q8_1_into(
9661                    &input[rank_index],
9662                    1,
9663                    experts.input_width,
9664                    &mut in_q[rank_index],
9665                    &mut in_d[rank_index],
9666                )?;
9667            }
9668        }
9669        self.nvfp4_routes_batched_sweeps(
9670            experts,
9671            workspace,
9672            selected,
9673            route_weights,
9674            &sel_i32,
9675            local_out,
9676            n_sel,
9677            activation_limit,
9678            false,
9679        )?;
9680
9681        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
9682        // the root stream in canonical shard order, and e copies the combined row out behind
9683        // the root's done event.
9684        // rank0 == root: its own stream order already covers its sweep; only the PEER
9685        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
9686        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
9687            let _main = engine.gpu.enter_main()?;
9688            workspace.ev_rank[rank_index].record(&engine.stream())?;
9689        }
9690        if moe_direct_on() && self.ranks.len() == 2 {
9691            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
9692            // rank0's is root-stream-ordered. One root event + rank1's own event order
9693            // the model engine's single add — same operand order as root's add
9694            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
9695            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
9696            // hazard class does not apply).
9697            {
9698                let root = &self.ranks[0];
9699                let _main = root.gpu.enter_main()?;
9700                workspace
9701                    .ev_done
9702                    .as_ref()
9703                    .expect("device routes done event")
9704                    .record(&root.stream())?;
9705            }
9706            let _main = e.gpu.enter_main()?;
9707            e.stream().wait(
9708                workspace
9709                    .ev_done
9710                    .as_ref()
9711                    .expect("device routes done event"),
9712            )?;
9713            for ev in workspace.ev_rank.iter().skip(1) {
9714                e.stream().wait(ev)?;
9715            }
9716            let mut output = e.uninit(experts.input_width)?;
9717            e.add(
9718                &workspace.accumulator[0],
9719                &workspace.accumulator[1],
9720                &mut output,
9721                experts.input_width,
9722            )?;
9723            let output = output;
9724            if let Some(started) = started {
9725                use std::sync::atomic::Ordering;
9726                let ns = TIMING_NS
9727                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9728                    + started.elapsed().as_nanos() as u64;
9729                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9730                if calls % 430 == 0 {
9731                    eprintln!(
9732                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9733                        ns as f64 / 1.0e6,
9734                        ns as f64 / calls as f64 / 1.0e3,
9735                    );
9736                }
9737            }
9738            return Ok(output);
9739        }
9740        {
9741            let root = &self.ranks[0];
9742            let _main = root.gpu.enter_main()?;
9743            for ev in workspace.ev_rank.iter().skip(1) {
9744                root.stream().wait(ev)?;
9745            }
9746            root.stream()
9747                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
9748            {
9749                let Nvfp4DeviceRoutesWorkspace {
9750                    accumulator,
9751                    remote,
9752                    combined,
9753                    ..
9754                } = &mut *workspace;
9755                root.add(&accumulator[0], remote, combined, experts.input_width)?;
9756            }
9757            workspace
9758                .ev_done
9759                .as_ref()
9760                .expect("device routes done event")
9761                .record(&root.stream())?;
9762        }
9763        let output = {
9764            let _main = e.gpu.enter_main()?;
9765            e.stream().wait(
9766                workspace
9767                    .ev_done
9768                    .as_ref()
9769                    .expect("device routes done event"),
9770            )?;
9771            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
9772            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
9773            let mut output = e.uninit(experts.input_width)?;
9774            e.stream().memcpy_dtod(
9775                &workspace.combined.slice(0..experts.input_width),
9776                &mut output.slice_mut(0..experts.input_width),
9777            )?;
9778            output
9779        };
9780        if let Some(started) = started {
9781            use std::sync::atomic::Ordering;
9782            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
9783                + started.elapsed().as_nanos() as u64;
9784            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
9785            if calls % 430 == 0 {
9786                eprintln!(
9787                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
9788                    ns as f64 / 1.0e6,
9789                    ns as f64 / calls as f64 / 1.0e3,
9790                );
9791            }
9792        }
9793        Ok(output)
9794    }
9795
9796    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
9797    /// route weights arrive as the device router's e-context outputs — the per-layer host
9798    /// logits readback disappears. The fresh router outputs are staged into persistent
9799    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
9800    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
9801    #[allow(clippy::too_many_arguments)]
9802    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
9803    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
9804    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
9805    /// the routed run then does its own staging as before.
9806    pub fn nvfp4_routes_prestage(
9807        &self,
9808        experts: &ResidentNvfp4TensorParallel,
9809        e: &Engine,
9810        input_dev: &crate::CudaSlice<f32>,
9811    ) -> Result<bool, Box<dyn std::error::Error>> {
9812        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
9813    }
9814
9815    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
9816    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
9817    /// deterministic kernels on identical input bits produce identical sel/w, so the
9818    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
9819    /// routed run then skips rank1's sel pull.
9820    pub fn nvfp4_routes_prestage_with(
9821        &self,
9822        experts: &ResidentNvfp4TensorParallel,
9823        e: &Engine,
9824        input_dev: &crate::CudaSlice<f32>,
9825        rank1_router: impl FnOnce(
9826            &Engine,
9827            &crate::CudaSlice<f32>,
9828            &mut crate::CudaSlice<i32>,
9829            &mut crate::CudaSlice<f32>,
9830        ) -> Result<bool, Box<dyn std::error::Error>>,
9831    ) -> Result<bool, Box<dyn std::error::Error>> {
9832        if !routes_prestage_on() || step_tp_graph_enabled()? {
9833            return Ok(false);
9834        }
9835        if input_dev.len() != experts.input_width {
9836            return Err("NVFP4 prestage input width mismatch".into());
9837        }
9838        let mut workspace_guard = experts
9839            .device_workspace
9840            .lock()
9841            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9842        let Some(workspace) = workspace_guard.as_mut() else {
9843            return Ok(false);
9844        };
9845        if workspace.ev_input.is_none() {
9846            let _main = e.gpu.enter_main()?;
9847            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
9848        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
9849            return Err("NVFP4 prestage engine changed".into());
9850        }
9851        {
9852            let _main = e.gpu.enter_main()?;
9853            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
9854            ev.record(&e.stream())?;
9855        }
9856        for (rank_index, engine) in self.ranks.iter().enumerate() {
9857            let _main = engine.gpu.enter_main()?;
9858            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
9859            engine.stream().wait(ev)?;
9860            {
9861                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
9862                engine
9863                    .stream()
9864                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
9865            }
9866            {
9867                let Nvfp4DeviceRoutesWorkspace {
9868                    input, in_q, in_d, ..
9869                } = &mut *workspace;
9870                engine.quantize_q8_1_into(
9871                    &input[rank_index],
9872                    1,
9873                    experts.input_width,
9874                    &mut in_q[rank_index],
9875                    &mut in_d[rank_index],
9876                )?;
9877            }
9878        }
9879        if self.ranks.len() == 2 {
9880            let rank1 = &self.ranks[1];
9881            let _r1 = rank1.gpu.enter_main()?;
9882            let Nvfp4DeviceRoutesWorkspace {
9883                input,
9884                sel,
9885                route_w,
9886                ..
9887            } = &mut *workspace;
9888            let (in1, rest_sel) = (&input[1], &mut sel[1]);
9889            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
9890                workspace.rank1_routed = true;
9891            }
9892        }
9893        workspace.prestaged = true;
9894        Ok(true)
9895    }
9896
9897    /// TWO-COLUMN device-routed expert program (spec verify, MEMRA_TCOL_FFN): one gu_tcol
9898    /// sweep over 2*n_sel_col pairs (pair t reads activation row t/n_sel_col — weights the
9899    /// two columns share dedup through L2), the UNCHANGED silu/down kernels at n_sel=16
9900    /// (both already index per pair), and one offset-axpy combine per column (the exact
9901    /// t=1 sequential chain over that column's 8 pairs). No serving doors: no graph, no
9902    /// prestage, no shexp folding — plain evented ordering. Returns [2, input_width] on e.
9903    ///
9904    /// EXACTNESS: every kernel body is the t=1 program per (pair,row) or per element; the
9905    /// per-column combine order equals the t=1 combine; the cross-rank join adds the same
9906    /// operand values elementwise. Gated by the greedy tape like every verify arm.
9907    #[allow(clippy::too_many_arguments)]
9908    pub fn run_tensor_parallel_routes_nvfp4_device_routed_t2(
9909        &self,
9910        experts: &ResidentNvfp4TensorParallel,
9911        e: &Engine,
9912        z2: &crate::CudaSlice<f32>,
9913        sel_d: &crate::CudaSlice<i32>,
9914        w_d: &crate::CudaSlice<f32>,
9915        n_sel_col: usize,
9916        activation_limit: Option<f32>,
9917    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
9918        let world = self.ranks.len();
9919        if world != NVFP4_CANONICAL_ROW_SHARDS {
9920            return Err("NVFP4 t2 routes require the canonical 2-shard grid".into());
9921        }
9922        let width = experts.input_width;
9923        let n_sel = 2 * n_sel_col;
9924        if z2.len() < 2 * width || sel_d.len() < n_sel || w_d.len() < n_sel {
9925            return Err("NVFP4 t2 routes geometry".into());
9926        }
9927        if !nvfp4_bank_v2_on() {
9928            return Err("NVFP4 t2 routes require the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
9929        }
9930        let local_out = experts.expert_width / world;
9931        let mut guard = experts
9932            .t2_workspace
9933            .lock()
9934            .map_err(|_| "NVFP4 t2 workspace lock is poisoned")?;
9935        if guard.as_ref().is_none_or(|ws| ws.n_sel != n_sel) {
9936            let mut input2 = Vec::new();
9937            let mut in_q2 = Vec::new();
9938            let mut in_d2 = Vec::new();
9939            let mut sel2 = Vec::new();
9940            let mut route_w2 = Vec::new();
9941            let mut gate_out2 = Vec::new();
9942            let mut up_out2 = Vec::new();
9943            let mut act_q2 = Vec::new();
9944            let mut act_d2 = Vec::new();
9945            let mut partial2 = Vec::new();
9946            let mut acc_a = Vec::new();
9947            let mut acc_b = Vec::new();
9948            let mut ev_rank = Vec::new();
9949            for engine in &self.ranks {
9950                let _m = engine.gpu.enter_main()?;
9951                input2.push(engine.uninit(2 * width)?);
9952                in_q2.push(engine.alloc_i8_uninit(2 * width)?);
9953                in_d2.push(engine.uninit(2 * (width / 32))?);
9954                sel2.push(engine.htod_i32(&vec![0i32; n_sel])?);
9955                route_w2.push(engine.uninit(n_sel)?);
9956                gate_out2.push(engine.uninit(n_sel * local_out)?);
9957                up_out2.push(engine.uninit(n_sel * local_out)?);
9958                act_q2.push(engine.alloc_i8_uninit(n_sel * local_out)?);
9959                act_d2.push(engine.uninit(n_sel * (local_out / 32))?);
9960                partial2.push(engine.uninit(n_sel * width)?);
9961                acc_a.push(engine.uninit(width)?);
9962                acc_b.push(engine.uninit(width)?);
9963                ev_rank.push(engine.ctx().new_event(None)?);
9964            }
9965            let root = &self.ranks[0];
9966            let (peer_a, peer_b, omix_a, omix_b, ev_root) = {
9967                let _m = root.gpu.enter_main()?;
9968                (
9969                    root.uninit(width)?,
9970                    root.uninit(width)?,
9971                    root.uninit(width)?,
9972                    root.uninit(width)?,
9973                    root.ctx().new_event(None)?,
9974                )
9975            };
9976            let ev_entry = {
9977                let _m = e.gpu.enter_main()?;
9978                e.ctx().new_event(None)?
9979            };
9980            *guard = Some(Nvfp4T2Workspace {
9981                input2,
9982                in_q2,
9983                in_d2,
9984                sel2,
9985                route_w2,
9986                gate_out2,
9987                up_out2,
9988                act_q2,
9989                act_d2,
9990                partial2,
9991                acc_a,
9992                acc_b,
9993                peer_a,
9994                peer_b,
9995                omix_a,
9996                omix_b,
9997                ev_entry,
9998                ev_rank,
9999                ev_root,
10000                n_sel,
10001                e_device: e.ctx().ordinal(),
10002            });
10003        }
10004        let ws = guard.as_mut().expect("armed above");
10005        if ws.e_device != e.ctx().ordinal() {
10006            return Err("NVFP4 t2 routes engine changed".into());
10007        }
10008        {
10009            let _main = e.gpu.enter_main()?;
10010            ws.ev_entry.record(&e.stream())?;
10011        }
10012        for rank in 0..world {
10013            let engine = &self.ranks[rank];
10014            let _main = engine.gpu.enter_main()?;
10015            engine.stream().wait(&ws.ev_entry)?;
10016            {
10017                let mut dst = ws.input2[rank].slice_mut(0..2 * width);
10018                engine
10019                    .stream()
10020                    .memcpy_dtod(&z2.slice(0..2 * width), &mut dst)?;
10021            }
10022            {
10023                let mut dst = ws.sel2[rank].slice_mut(0..n_sel);
10024                engine
10025                    .stream()
10026                    .memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10027            }
10028            {
10029                let mut dst = ws.route_w2[rank].slice_mut(0..n_sel);
10030                engine
10031                    .stream()
10032                    .memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10033            }
10034            {
10035                let Nvfp4T2Workspace {
10036                    input2,
10037                    in_q2,
10038                    in_d2,
10039                    ..
10040                } = &mut *ws;
10041                engine.quantize_q8_1_into(
10042                    &input2[rank],
10043                    2,
10044                    width,
10045                    &mut in_q2[rank],
10046                    &mut in_d2[rank],
10047                )?;
10048            }
10049            let gate_bank = &experts.gate[rank];
10050            let up_bank = &experts.up[rank];
10051            if gate_bank.in_features != up_bank.in_features
10052                || gate_bank.local_out != up_bank.local_out
10053                || gate_bank.row_bytes != up_bank.row_bytes
10054                || gate_bank.expert_bytes != up_bank.expert_bytes
10055            {
10056                return Err("NVFP4 t2 routes need matched gate/up bank geometry".into());
10057            }
10058            {
10059                let Nvfp4T2Workspace {
10060                    sel2,
10061                    in_q2,
10062                    in_d2,
10063                    gate_out2,
10064                    up_out2,
10065                    ..
10066                } = &mut *ws;
10067                engine.qmatvec_nvfp4_sel_gu_tcol_into(
10068                    &gate_bank.bank,
10069                    &up_bank.bank,
10070                    &sel2[rank],
10071                    &in_q2[rank],
10072                    &in_d2[rank],
10073                    &mut gate_out2[rank],
10074                    &mut up_out2[rank],
10075                    n_sel,
10076                    n_sel_col,
10077                    gate_bank.in_features,
10078                    gate_bank.local_out,
10079                    gate_bank.row_bytes,
10080                    gate_bank.expert_bytes,
10081                    width,
10082                    width / 32,
10083                )?;
10084            }
10085            {
10086                let Nvfp4T2Workspace {
10087                    gate_out2,
10088                    up_out2,
10089                    sel2,
10090                    act_q2,
10091                    act_d2,
10092                    ..
10093                } = &mut *ws;
10094                engine.silu_mul_scaled_q8_1_sel_into(
10095                    &gate_out2[rank],
10096                    &up_out2[rank],
10097                    &experts.macros_gate_dev[rank],
10098                    &experts.macros_up_dev[rank],
10099                    &sel2[rank],
10100                    activation_limit,
10101                    &mut act_q2[rank],
10102                    &mut act_d2[rank],
10103                    local_out,
10104                    n_sel,
10105                )?;
10106            }
10107            let shard = &experts.down[rank];
10108            if shard.device_rank != rank || shard.local_in != local_out {
10109                return Err("NVFP4 t2 routes: down shard placement drifted".into());
10110            }
10111            {
10112                let Nvfp4T2Workspace {
10113                    sel2,
10114                    act_q2,
10115                    act_d2,
10116                    partial2,
10117                    ..
10118                } = &mut *ws;
10119                engine.qmatvec_nvfp4_sel_into(
10120                    &shard.bank,
10121                    &sel2[rank],
10122                    &act_q2[rank],
10123                    &act_d2[rank],
10124                    &mut partial2[rank],
10125                    n_sel,
10126                    shard.local_in,
10127                    shard.out_features,
10128                    shard.row_bytes,
10129                    shard.expert_bytes,
10130                    local_out,
10131                    local_out / 32,
10132                )?;
10133            }
10134            {
10135                let Nvfp4T2Workspace {
10136                    partial2,
10137                    route_w2,
10138                    sel2,
10139                    acc_a,
10140                    acc_b,
10141                    ..
10142                } = &mut *ws;
10143                engine.axpy_rows_seq_md_off_into(
10144                    &partial2[rank],
10145                    &route_w2[rank],
10146                    &experts.macros_down_dev[rank],
10147                    &sel2[rank],
10148                    &mut acc_a[rank],
10149                    width,
10150                    n_sel_col,
10151                    0,
10152                )?;
10153                engine.axpy_rows_seq_md_off_into(
10154                    &partial2[rank],
10155                    &route_w2[rank],
10156                    &experts.macros_down_dev[rank],
10157                    &sel2[rank],
10158                    &mut acc_b[rank],
10159                    width,
10160                    n_sel_col,
10161                    n_sel_col,
10162                )?;
10163            }
10164            if rank != 0 {
10165                ws.ev_rank[rank].record(&engine.stream())?;
10166            }
10167        }
10168        let root = &self.ranks[0];
10169        {
10170            let _main = root.gpu.enter_main()?;
10171            for ev in ws.ev_rank.iter().skip(1) {
10172                root.stream().wait(ev)?;
10173            }
10174            {
10175                let Nvfp4T2Workspace {
10176                    acc_a,
10177                    acc_b,
10178                    peer_a,
10179                    peer_b,
10180                    omix_a,
10181                    omix_b,
10182                    ..
10183                } = &mut *ws;
10184                {
10185                    let mut dst = peer_a.slice_mut(0..width);
10186                    root.stream()
10187                        .memcpy_dtod(&acc_a[1].slice(0..width), &mut dst)?;
10188                }
10189                {
10190                    let mut dst = peer_b.slice_mut(0..width);
10191                    root.stream()
10192                        .memcpy_dtod(&acc_b[1].slice(0..width), &mut dst)?;
10193                }
10194                root.add(&acc_a[0], peer_a, omix_a, width)?;
10195                root.add(&acc_b[0], peer_b, omix_b, width)?;
10196            }
10197            ws.ev_root.record(&root.stream())?;
10198        }
10199        let _main = e.gpu.enter_main()?;
10200        e.stream().wait(&ws.ev_root)?;
10201        let mut out = e.uninit(2 * width)?;
10202        e.stream()
10203            .memcpy_dtod(&ws.omix_a.slice(0..width), &mut out.slice_mut(0..width))?;
10204        e.stream().memcpy_dtod(
10205            &ws.omix_b.slice(0..width),
10206            &mut out.slice_mut(width..2 * width),
10207        )?;
10208        Ok(out)
10209    }
10210
10211    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
10212        &self,
10213        experts: &ResidentNvfp4TensorParallel,
10214        e: &Engine,
10215        input_dev: &crate::CudaSlice<f32>,
10216        sel_d: &crate::CudaSlice<i32>,
10217        w_d: &crate::CudaSlice<f32>,
10218        experts_per_token: usize,
10219        activation_limit: Option<f32>,
10220    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10221        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
10222            experts,
10223            e,
10224            input_dev,
10225            sel_d,
10226            w_d,
10227            experts_per_token,
10228            activation_limit,
10229            || Ok(()),
10230        )
10231    }
10232
10233    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
10234    /// runs on the host right before the join wait is enqueued on e's stream — work it
10235    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
10236    /// sweep, instead of after the join. Value-neutral by construction (the hook only
10237    /// reorders independent host issue).
10238    #[allow(clippy::too_many_arguments)]
10239    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
10240        &self,
10241        experts: &ResidentNvfp4TensorParallel,
10242        e: &Engine,
10243        input_dev: &crate::CudaSlice<f32>,
10244        sel_d: &crate::CudaSlice<i32>,
10245        w_d: &crate::CudaSlice<f32>,
10246        experts_per_token: usize,
10247        activation_limit: Option<f32>,
10248        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
10249    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10250        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
10251            experts,
10252            e,
10253            input_dev,
10254            sel_d,
10255            w_d,
10256            experts_per_token,
10257            activation_limit,
10258            pre_join,
10259            None,
10260        )
10261    }
10262
10263    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
10264    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
10265    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
10266    /// its apply launch. Raw UVA pointers so no lock is held across the call.
10267    #[allow(clippy::too_many_arguments)]
10268    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
10269        &self,
10270        experts: &ResidentNvfp4TensorParallel,
10271        e: &Engine,
10272        input_dev: &crate::CudaSlice<f32>,
10273        sel_d: &crate::CudaSlice<i32>,
10274        w_d: &crate::CudaSlice<f32>,
10275        experts_per_token: usize,
10276        activation_limit: Option<f32>,
10277        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
10278        post_add: Option<(u64, u64)>,
10279    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10280        if input_dev.len() != experts.input_width {
10281            return Err(format!(
10282                "NVFP4 device-routed input {} != width {}",
10283                input_dev.len(),
10284                experts.input_width
10285            )
10286            .into());
10287        }
10288        let n_sel = experts_per_token;
10289        if sel_d.len() < n_sel || w_d.len() < n_sel {
10290            return Err(format!(
10291                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
10292                sel_d.len(),
10293                w_d.len()
10294            )
10295            .into());
10296        }
10297        let world = self.ranks.len();
10298        if world != NVFP4_CANONICAL_ROW_SHARDS {
10299            return Err(format!(
10300                "NVFP4 device routes require world == canonical shard grid \
10301                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10302            )
10303            .into());
10304        }
10305        let local_out = experts.expert_width / world;
10306
10307        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10308        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10309        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10310        let started = timing.then(std::time::Instant::now);
10311
10312        let mut workspace_guard = experts
10313            .device_workspace
10314            .lock()
10315            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10316        if workspace_guard.is_none() {
10317            drop(workspace_guard);
10318            let zero = vec![0.0f32; experts.input_width];
10319            let zero_sel = vec![0usize; n_sel];
10320            let zero_w = vec![0.0f32; n_sel];
10321            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10322                experts,
10323                &zero,
10324                &zero_sel,
10325                &zero_w,
10326                n_sel,
10327                activation_limit,
10328            )?;
10329            workspace_guard = experts
10330                .device_workspace
10331                .lock()
10332                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10333        }
10334        let workspace = workspace_guard
10335            .as_mut()
10336            .expect("NVFP4 device routes workspace initialized above");
10337        if workspace.n_sel != n_sel {
10338            return Err(format!(
10339                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10340                workspace.n_sel
10341            )
10342            .into());
10343        }
10344
10345        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
10346        // stitched multi-device parent launched on e's stream — no events, no per-token node
10347        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
10348        // the children replay exactly the same kernel/copy sequence.
10349        if step_tp_graph_enabled()? {
10350            if workspace.dev_route_e.is_none() {
10351                let _main = e.gpu.enter_main()?;
10352                workspace.dev_route_e = Some((
10353                    e.htod_i32(&vec![0i32; n_sel])?,
10354                    e.htod(&vec![0.0f32; n_sel])?,
10355                ));
10356            }
10357            if workspace.in_stage_e.is_none() {
10358                let _main = e.gpu.enter_main()?;
10359                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
10360                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
10361            }
10362            if workspace.routes_graph.is_none() {
10363                let graph = self.nvfp4_routes_build_graph(
10364                    experts,
10365                    workspace,
10366                    local_out,
10367                    n_sel,
10368                    activation_limit,
10369                )?;
10370                workspace.routes_graph = Some(graph);
10371                eprintln!(
10372                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
10373                     children=3 updates=none performance_claim=false"
10374                );
10375            }
10376            let output = {
10377                let _main = e.gpu.enter_main()?;
10378                {
10379                    let (sel_e, w_e) = workspace
10380                        .dev_route_e
10381                        .as_mut()
10382                        .expect("device route staging set above");
10383                    {
10384                        let mut dst = sel_e.slice_mut(0..n_sel);
10385                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10386                    }
10387                    {
10388                        let mut dst = w_e.slice_mut(0..n_sel);
10389                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10390                    }
10391                }
10392                {
10393                    let in_stage = workspace
10394                        .in_stage_e
10395                        .as_mut()
10396                        .expect("graph staging set above");
10397                    let mut dst = in_stage.slice_mut(0..experts.input_width);
10398                    e.stream()
10399                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
10400                }
10401                unsafe {
10402                    let r = cudarc::driver::sys::cuGraphLaunch(
10403                        workspace
10404                            .routes_graph
10405                            .as_ref()
10406                            .expect("routes graph built above")
10407                            .exec,
10408                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
10409                    );
10410                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
10411                        return Err(format!("routes graph launch: {r:?}").into());
10412                    }
10413                }
10414                let mut output = e.uninit(experts.input_width)?;
10415                {
10416                    let out_stage = workspace
10417                        .out_stage_e
10418                        .as_ref()
10419                        .expect("graph staging set above");
10420                    e.stream().memcpy_dtod(
10421                        &out_stage.slice(0..experts.input_width),
10422                        &mut output.slice_mut(0..experts.input_width),
10423                    )?;
10424                }
10425                output
10426            };
10427            if let Some(started) = started {
10428                use std::sync::atomic::Ordering;
10429                let ns = TIMING_NS
10430                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10431                    + started.elapsed().as_nanos() as u64;
10432                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10433                if calls % 430 == 0 {
10434                    eprintln!(
10435                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10436                        ns as f64 / 1.0e6,
10437                        ns as f64 / calls as f64 / 1.0e3,
10438                    );
10439                }
10440            }
10441            return Ok(output);
10442        }
10443
10444        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
10445        // copied into the persistent e-context pair, then the event is recorded — the caller's
10446        // sel_d/w_d can free on e's stream with no cross-stream reader.
10447        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10448            if *device != e.ctx().ordinal() {
10449                return Err("NVFP4 device-routed routes engine changed".into());
10450            }
10451        } else {
10452            let _main = e.gpu.enter_main()?;
10453            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10454        }
10455        if workspace.dev_route_e.is_none() {
10456            let _main = e.gpu.enter_main()?;
10457            workspace.dev_route_e = Some((
10458                e.htod_i32(&vec![0i32; n_sel])?,
10459                e.htod(&vec![0.0f32; n_sel])?,
10460            ));
10461        }
10462        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
10463        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
10464        // selection rows), so when every consuming rank shares e's device the ranks can read
10465        // them directly and this hop disappears. The graph door keeps the staging (its
10466        // captured copies read the fixed addresses).
10467        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
10468        let e_device = e.ctx().ordinal();
10469        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
10470        let rank1_routed_peek = workspace.rank1_routed;
10471        let stage_needed = !mirror
10472            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
10473                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
10474            });
10475        {
10476            let _main = e.gpu.enter_main()?;
10477            if stage_needed {
10478                let (sel_e, w_e) = workspace
10479                    .dev_route_e
10480                    .as_mut()
10481                    .expect("device route staging set above");
10482                {
10483                    let mut dst = sel_e.slice_mut(0..n_sel);
10484                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10485                }
10486                {
10487                    let mut dst = w_e.slice_mut(0..n_sel);
10488                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10489                }
10490            }
10491            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10492            ev_entry.record(&e.stream())?;
10493        }
10494        // Prestage door: input pull + quantize were already issued on the rank streams
10495        // (before the router) — the rank stream order suffices, skip them here.
10496        let prestaged = std::mem::take(&mut workspace.prestaged);
10497        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
10498        for (rank_index, engine) in self.ranks.iter().enumerate() {
10499            let _main = engine.gpu.enter_main()?;
10500            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10501            engine.stream().wait(ev_entry)?;
10502            if !prestaged {
10503                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10504                engine
10505                    .stream()
10506                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10507            }
10508            if !(rank1_routed && rank_index == 1) {
10509                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
10510                // the caller's persistent rows when this rank shares e's device (UVA, ordered
10511                // by ev_entry), else the staged e-context pair.
10512                let same_dev = engine.ctx().ordinal() == e_device;
10513                if mirror {
10514                    // Split the workspace borrow so the source (the staged pair, when this
10515                    // rank is off-device) and the destination rows coexist.
10516                    let Nvfp4DeviceRoutesWorkspace {
10517                        sel,
10518                        route_w,
10519                        dev_route_e,
10520                        ..
10521                    } = &mut *workspace;
10522                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
10523                        if same_dev {
10524                            (sel_d, w_d)
10525                        } else {
10526                            let (sel_e, w_e) = dev_route_e
10527                                .as_ref()
10528                                .expect("device route staging set above");
10529                            (sel_e, w_e)
10530                        };
10531                    engine.moe_sel_w_mirror(
10532                        src_sel,
10533                        src_w,
10534                        &mut sel[rank_index],
10535                        &mut route_w[rank_index],
10536                        n_sel,
10537                    )?;
10538                } else {
10539                    let (sel_e, w_e) = workspace
10540                        .dev_route_e
10541                        .as_ref()
10542                        .expect("device route staging set above");
10543                    {
10544                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
10545                        engine
10546                            .stream()
10547                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
10548                    }
10549                    {
10550                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
10551                        engine
10552                            .stream()
10553                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
10554                    }
10555                }
10556            }
10557            if !prestaged {
10558                let Nvfp4DeviceRoutesWorkspace {
10559                    input, in_q, in_d, ..
10560                } = &mut *workspace;
10561                engine.quantize_q8_1_into(
10562                    &input[rank_index],
10563                    1,
10564                    experts.input_width,
10565                    &mut in_q[rank_index],
10566                    &mut in_d[rank_index],
10567                )?;
10568            }
10569        }
10570        self.nvfp4_routes_batched_sweeps(
10571            experts,
10572            workspace,
10573            &[],
10574            &[],
10575            &[],
10576            local_out,
10577            n_sel,
10578            activation_limit,
10579            true,
10580        )?;
10581
10582        // rank0 == root: its own stream order already covers its sweep; only the PEER
10583        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
10584        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
10585            let _main = engine.gpu.enter_main()?;
10586            workspace.ev_rank[rank_index].record(&engine.stream())?;
10587        }
10588        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
10589        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
10590        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
10591        let mut ticket = 0u32;
10592        if memops {
10593            use cudarc::driver::sys;
10594            if workspace.fence_flags_raw == 0 {
10595                let root = &self.ranks[0];
10596                let _main = root.gpu.enter_main()?;
10597                let mut ptr: sys::CUdeviceptr = 0;
10598                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
10599                if r != sys::CUresult::CUDA_SUCCESS {
10600                    return Err(format!("fence flag alloc: {r:?}").into());
10601                }
10602                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
10603                if r != sys::CUresult::CUDA_SUCCESS {
10604                    return Err(format!("fence flag memset: {r:?}").into());
10605                }
10606                workspace.fence_flags_raw = ptr as u64;
10607            }
10608            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
10609            ticket = workspace.fence_ticket;
10610            let base = workspace.fence_flags_raw;
10611            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
10612            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
10613            // root memory is legal — the direct join already relies on it. Under
10614            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
10615            // replacing the cross-device event wait below.
10616            if fence_rank1_on() {
10617                let peer = &self.ranks[1];
10618                let _pmain = peer.gpu.enter_main()?;
10619                peer.ring_flag_raw(base, ticket)?;
10620            }
10621            {
10622                let root = &self.ranks[0];
10623                let _main = root.gpu.enter_main()?;
10624                let r = unsafe {
10625                    sys::cuStreamWriteValue32_v2(
10626                        root.stream().cu_stream() as sys::CUstream,
10627                        (base + 4) as sys::CUdeviceptr,
10628                        ticket,
10629                        0,
10630                    )
10631                };
10632                if r != sys::CUresult::CUDA_SUCCESS {
10633                    return Err(format!("fence write root: {r:?}").into());
10634                }
10635            }
10636        }
10637        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
10638        // kernels queued here execute while the peer rank drains its sweep.
10639        pre_join()?;
10640
10641        if moe_direct_on() && self.ranks.len() == 2 {
10642            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
10643            // rank0's is root-stream-ordered. One root event + rank1's own event order
10644            // the model engine's single add — same operand order as root's add
10645            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
10646            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
10647            // hazard class does not apply).
10648            let _main = e.gpu.enter_main()?;
10649            if memops {
10650                use cudarc::driver::sys;
10651                let base = workspace.fence_flags_raw;
10652                let r = unsafe {
10653                    sys::cuStreamWaitValue32_v2(
10654                        e.stream().cu_stream() as sys::CUstream,
10655                        (base + 4) as sys::CUdeviceptr,
10656                        ticket,
10657                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
10658                    )
10659                };
10660                if r != sys::CUresult::CUDA_SUCCESS {
10661                    return Err(format!("fence wait: {r:?}").into());
10662                }
10663                if fence_rank1_on() {
10664                    // Same-device wait on the flag rank1 rang over P2P.
10665                    let r = unsafe {
10666                        sys::cuStreamWaitValue32_v2(
10667                            e.stream().cu_stream() as sys::CUstream,
10668                            base as sys::CUdeviceptr,
10669                            ticket,
10670                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
10671                        )
10672                    };
10673                    if r != sys::CUresult::CUDA_SUCCESS {
10674                        return Err(format!("fence wait rank1: {r:?}").into());
10675                    }
10676                } else {
10677                    for ev in workspace.ev_rank.iter().skip(1) {
10678                        e.stream().wait(ev)?;
10679                    }
10680                }
10681            } else {
10682                {
10683                    let root = &self.ranks[0];
10684                    let _rmain = root.gpu.enter_main()?;
10685                    workspace
10686                        .ev_done
10687                        .as_ref()
10688                        .expect("device routes done event")
10689                        .record(&root.stream())?;
10690                }
10691                e.stream().wait(
10692                    workspace
10693                        .ev_done
10694                        .as_ref()
10695                        .expect("device routes done event"),
10696                )?;
10697                for ev in workspace.ev_rank.iter().skip(1) {
10698                    e.stream().wait(ev)?;
10699                }
10700            }
10701            let mut output = e.uninit(experts.input_width)?;
10702            if let Some((sh_raw, scale_raw)) = post_add {
10703                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
10704                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
10705                e.add3_raw(
10706                    &workspace.accumulator[0],
10707                    &workspace.accumulator[1],
10708                    sh_raw,
10709                    scale_raw,
10710                    &mut output,
10711                    experts.input_width,
10712                )?;
10713            } else {
10714                e.add(
10715                    &workspace.accumulator[0],
10716                    &workspace.accumulator[1],
10717                    &mut output,
10718                    experts.input_width,
10719                )?;
10720            }
10721            let output = output;
10722            if let Some(started) = started {
10723                use std::sync::atomic::Ordering;
10724                let ns = TIMING_NS
10725                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10726                    + started.elapsed().as_nanos() as u64;
10727                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10728                if calls % 430 == 0 {
10729                    eprintln!(
10730                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10731                        ns as f64 / 1.0e6,
10732                        ns as f64 / calls as f64 / 1.0e3,
10733                    );
10734                }
10735            }
10736            return Ok(output);
10737        }
10738        {
10739            let root = &self.ranks[0];
10740            let _main = root.gpu.enter_main()?;
10741            for ev in workspace.ev_rank.iter().skip(1) {
10742                root.stream().wait(ev)?;
10743            }
10744            root.stream()
10745                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10746            {
10747                let Nvfp4DeviceRoutesWorkspace {
10748                    accumulator,
10749                    remote,
10750                    combined,
10751                    ..
10752                } = &mut *workspace;
10753                root.add(&accumulator[0], remote, combined, experts.input_width)?;
10754            }
10755            workspace
10756                .ev_done
10757                .as_ref()
10758                .expect("device routes done event")
10759                .record(&root.stream())?;
10760        }
10761        let output = {
10762            let _main = e.gpu.enter_main()?;
10763            e.stream().wait(
10764                workspace
10765                    .ev_done
10766                    .as_ref()
10767                    .expect("device routes done event"),
10768            )?;
10769            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
10770            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
10771            let mut output = e.uninit(experts.input_width)?;
10772            e.stream().memcpy_dtod(
10773                &workspace.combined.slice(0..experts.input_width),
10774                &mut output.slice_mut(0..experts.input_width),
10775            )?;
10776            output
10777        };
10778        if let Some(started) = started {
10779            use std::sync::atomic::Ordering;
10780            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10781                + started.elapsed().as_nanos() as u64;
10782            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10783            if calls % 430 == 0 {
10784                eprintln!(
10785                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10786                    ns as f64 / 1.0e6,
10787                    ns as f64 / calls as f64 / 1.0e3,
10788                );
10789            }
10790        }
10791        Ok(output)
10792    }
10793
10794    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
10795    /// caller wraps it with rank-event waits + the done record; the token graph captures it
10796    /// verbatim (parent edges provide the ordering).
10797    pub(crate) fn decode_v2_finish_root_fused(
10798        &self,
10799        ws: &mut StepTpDecodeV2Ws,
10800    ) -> Result<(), Box<dyn std::error::Error>> {
10801        let root = &self.ranks[0];
10802        let _main = root.gpu.enter_main()?;
10803        if ws.raw_peer_partial != 0 {
10804            // Capture-safe raw seams (arming happened in the stage flow).
10805            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
10806        } else {
10807            root.stream()
10808                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
10809        }
10810        {
10811            let StepTpDecodeV2Ws {
10812                o_partials,
10813                peer_partial,
10814                reduce_a,
10815                o_out,
10816                ..
10817            } = &mut *ws;
10818            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
10819        }
10820        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
10821        if shadows {
10822            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
10823            // raw when armed.
10824            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
10825            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
10826            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
10827            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
10828        }
10829        if shadows && ws.raw_peer_partial != 0 {
10830            raw_copy_bytes(
10831                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
10832                ws.raw_k1,
10833                ws.local_kv_dim * 4,
10834                root,
10835            )?;
10836            raw_copy_bytes(
10837                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
10838                ws.raw_v1,
10839                ws.local_kv_dim * 4,
10840                root,
10841            )?;
10842        } else if shadows {
10843            let start = ws.local_kv_dim;
10844            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
10845            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
10846            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
10847            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
10848        }
10849        if ws.raw_mixed_stage_e != 0 {
10850            // Token-graph mirrors: the e-glue children read same-context copies of the
10851            // root-produced rows.
10852            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
10853            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
10854            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
10855            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
10856        }
10857        Ok(())
10858    }
10859
10860    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
10861    /// reduce_a's own pointer.
10862    pub(crate) fn decode_v2_arm_token_mirrors(
10863        &self,
10864        ws: &mut StepTpDecodeV2Ws,
10865        mixed_stage_e: u64,
10866        shadow_stage_e: (u64, u64),
10867    ) -> Result<(), Box<dyn std::error::Error>> {
10868        use cudarc::driver::DevicePtr;
10869        let root = &self.ranks[0];
10870        let _main = root.gpu.enter_main()?;
10871        let stream = root.stream();
10872        let (a, _g) = ws.reduce_a.device_ptr(&stream);
10873        ws.raw_reduce_a = a as u64;
10874        ws.raw_mixed_stage_e = mixed_stage_e;
10875        ws.raw_shadow_stage_e = shadow_stage_e;
10876        Ok(())
10877    }
10878
10879    /// Build one layer's stitched routes graph: per-rank children captured on their own
10880    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
10881    /// capture-illegal there), a root combine child, and a multi-device parent with
10882    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
10883    /// nodes touch is persistent workspace/staging.
10884    fn nvfp4_routes_build_graph(
10885        &self,
10886        experts: &ResidentNvfp4TensorParallel,
10887        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10888        local_out: usize,
10889        n_sel: usize,
10890        activation_limit: Option<f32>,
10891    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
10892        use cudarc::driver::DevicePtr;
10893        use cudarc::driver::sys;
10894        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
10895            if r == sys::CUresult::CUDA_SUCCESS {
10896                Ok(())
10897            } else {
10898                Err(format!("{what}: {r:?}").into())
10899            }
10900        }
10901        let world = self.ranks.len();
10902        if world != 2 {
10903            return Err("routes graph door is built for the TP2 pair".into());
10904        }
10905        let width = experts.input_width;
10906
10907        // Raw pointers cached before capture (each read with its owner's stream).
10908        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
10909            let stream = engine.stream();
10910            let (ptr, _g) = buf.device_ptr(&stream);
10911            ptr as u64
10912        };
10913        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
10914            let stream = engine.stream();
10915            let (ptr, _g) = buf.device_ptr(&stream);
10916            ptr as u64
10917        };
10918        let (sel_e, w_e) = workspace
10919            .dev_route_e
10920            .as_ref()
10921            .expect("device route staging set before graph build");
10922        let root_engine = &self.ranks[0];
10923        let p_in_stage = ptr_f32(
10924            workspace.in_stage_e.as_ref().expect("graph staging"),
10925            root_engine,
10926        );
10927        let p_out_stage = ptr_f32(
10928            workspace.out_stage_e.as_ref().expect("graph staging"),
10929            root_engine,
10930        );
10931        let p_sel_e = ptr_i32(sel_e, root_engine);
10932        let p_w_e = ptr_f32(w_e, root_engine);
10933        let p_input: Vec<u64> = (0..world)
10934            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
10935            .collect();
10936        let p_sel: Vec<u64> = (0..world)
10937            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
10938            .collect();
10939        let p_route_w: Vec<u64> = (0..world)
10940            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
10941            .collect();
10942        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
10943        let p_remote = ptr_f32(&workspace.remote, root_engine);
10944        let p_combined = ptr_f32(&workspace.combined, root_engine);
10945
10946        let raw_copy = |dst: u64,
10947                        src: u64,
10948                        bytes: usize,
10949                        engine: &Engine|
10950         -> Result<(), Box<dyn std::error::Error>> {
10951            unsafe {
10952                cu_try(
10953                    sys::cuMemcpyAsync(
10954                        dst as sys::CUdeviceptr,
10955                        src as sys::CUdeviceptr,
10956                        bytes,
10957                        engine.stream().cu_stream() as sys::CUstream,
10958                    ),
10959                    "routes graph cuMemcpyAsync",
10960                )
10961            }
10962        };
10963
10964        let mut children = Vec::with_capacity(3);
10965        for rank in 0..world {
10966            let engine = &self.ranks[rank];
10967            let _main = engine.gpu.enter_main()?;
10968            let (child, _retained) = engine.capture_graph_retained(|_| {
10969                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
10970                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
10971                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
10972                {
10973                    let Nvfp4DeviceRoutesWorkspace {
10974                        input, in_q, in_d, ..
10975                    } = &mut *workspace;
10976                    engine.quantize_q8_1_into(
10977                        &input[rank],
10978                        1,
10979                        width,
10980                        &mut in_q[rank],
10981                        &mut in_d[rank],
10982                    )?;
10983                }
10984                self.nvfp4_routes_batched_sweeps_rank(
10985                    experts,
10986                    workspace,
10987                    &[],
10988                    &[],
10989                    &[],
10990                    local_out,
10991                    n_sel,
10992                    activation_limit,
10993                    true,
10994                    rank,
10995                )?;
10996                Ok(())
10997            })?;
10998            children.push(child);
10999        }
11000        {
11001            let root = &self.ranks[0];
11002            let _main = root.gpu.enter_main()?;
11003            let (child, _retained) = root.capture_graph_retained(|_| {
11004                raw_copy(p_remote, p_acc1, width * 4, root)?;
11005                {
11006                    let Nvfp4DeviceRoutesWorkspace {
11007                        accumulator,
11008                        remote,
11009                        combined,
11010                        ..
11011                    } = &mut *workspace;
11012                    root.add(&accumulator[0], remote, combined, width)?;
11013                }
11014                raw_copy(p_out_stage, p_combined, width * 4, root)?;
11015                Ok(())
11016            })?;
11017            children.push(child);
11018        }
11019
11020        let mut parent: sys::CUgraph = std::ptr::null_mut();
11021        unsafe {
11022            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
11023        }
11024        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
11025        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
11026        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
11027        unsafe {
11028            cu_try(
11029                sys::cuGraphAddChildGraphNode(
11030                    &mut n0,
11031                    parent,
11032                    std::ptr::null(),
11033                    0,
11034                    children[0].cu_graph(),
11035                ),
11036                "routes child r0",
11037            )?;
11038            cu_try(
11039                sys::cuGraphAddChildGraphNode(
11040                    &mut n1,
11041                    parent,
11042                    std::ptr::null(),
11043                    0,
11044                    children[1].cu_graph(),
11045                ),
11046                "routes child r1",
11047            )?;
11048            let deps = [n0, n1];
11049            cu_try(
11050                sys::cuGraphAddChildGraphNode(
11051                    &mut n2,
11052                    parent,
11053                    deps.as_ptr(),
11054                    2,
11055                    children[2].cu_graph(),
11056                ),
11057                "routes child root",
11058            )?;
11059        }
11060        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
11061        unsafe {
11062            cu_try(
11063                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
11064                "routes instantiate",
11065            )?;
11066        }
11067        Ok(RoutesGraph {
11068            exec,
11069            parent,
11070            _children: children,
11071        })
11072    }
11073
11074    /// One rank's routes section for the token graph (event-free): staged input copy (raw
11075    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
11076    /// Eager device_routed wraps it with the entry-event wait.
11077    #[allow(clippy::too_many_arguments)]
11078    pub(crate) fn routes_rank_section(
11079        &self,
11080        experts: &ResidentNvfp4TensorParallel,
11081        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11082        raw_input_src: u64,
11083        local_out: usize,
11084        n_sel: usize,
11085        activation_limit: Option<f32>,
11086        rank_index: usize,
11087    ) -> Result<(), Box<dyn std::error::Error>> {
11088        let engine = &self.ranks[rank_index];
11089        {
11090            let _main = engine.gpu.enter_main()?;
11091            // sel/route_w land via raw copies from the e staging (fixed addresses).
11092            let (sel_e_ptr, w_e_ptr) = workspace
11093                .raw_dev_route_e
11094                .ok_or("routes rank section requires armed staging pointers")?;
11095            raw_copy_bytes(
11096                workspace.raw_input[rank_index],
11097                raw_input_src,
11098                experts.input_width * 4,
11099                engine,
11100            )?;
11101            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
11102            raw_copy_bytes(
11103                workspace.raw_route_w[rank_index],
11104                w_e_ptr,
11105                n_sel * 4,
11106                engine,
11107            )?;
11108            {
11109                let Nvfp4DeviceRoutesWorkspace {
11110                    input, in_q, in_d, ..
11111                } = &mut *workspace;
11112                engine.quantize_q8_1_into(
11113                    &input[rank_index],
11114                    1,
11115                    experts.input_width,
11116                    &mut in_q[rank_index],
11117                    &mut in_d[rank_index],
11118                )?;
11119            }
11120        }
11121        self.nvfp4_routes_batched_sweeps_rank(
11122            experts,
11123            workspace,
11124            &[],
11125            &[],
11126            &[],
11127            local_out,
11128            n_sel,
11129            activation_limit,
11130            true,
11131            rank_index,
11132        )
11133    }
11134
11135    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
11136    /// add, combined row raw-copied into the fixed e-context out stage.
11137    pub(crate) fn routes_root_section(
11138        &self,
11139        experts: &ResidentNvfp4TensorParallel,
11140        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11141    ) -> Result<(), Box<dyn std::error::Error>> {
11142        let root = &self.ranks[0];
11143        let _main = root.gpu.enter_main()?;
11144        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
11145            .raw_combine
11146            .ok_or("routes root section requires armed combine pointers")?;
11147        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
11148        {
11149            let Nvfp4DeviceRoutesWorkspace {
11150                accumulator,
11151                remote,
11152                combined,
11153                ..
11154            } = &mut *workspace;
11155            root.add(&accumulator[0], remote, combined, experts.input_width)?;
11156        }
11157        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
11158        Ok(())
11159    }
11160
11161    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
11162    /// combine set. Requires dev_route_e + in/out stages already allocated.
11163    pub(crate) fn routes_arm_raw(
11164        &self,
11165        experts: &ResidentNvfp4TensorParallel,
11166        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11167    ) -> Result<(), Box<dyn std::error::Error>> {
11168        use cudarc::driver::DevicePtr;
11169        if workspace.raw_dev_route_e.is_some() {
11170            return Ok(());
11171        }
11172        let _ = experts;
11173        let (sel_e, w_e) = workspace
11174            .dev_route_e
11175            .as_ref()
11176            .ok_or("routes staging not armed")?;
11177        let root = &self.ranks[0];
11178        {
11179            let _main = root.gpu.enter_main()?;
11180            let stream = root.stream();
11181            let (a, _g) = sel_e.device_ptr(&stream);
11182            let (b, _g) = w_e.device_ptr(&stream);
11183            workspace.raw_dev_route_e = Some((a as u64, b as u64));
11184            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
11185            let (d, _g) = workspace.remote.device_ptr(&stream);
11186            let (f, _g) = workspace.combined.device_ptr(&stream);
11187            let out_stage = workspace
11188                .out_stage_e
11189                .as_ref()
11190                .ok_or("routes out stage not armed")?;
11191            let (g_, _g) = out_stage.device_ptr(&stream);
11192            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
11193        }
11194        for rank in 0..self.ranks.len() {
11195            let engine = &self.ranks[rank];
11196            let _main = engine.gpu.enter_main()?;
11197            let stream = engine.stream();
11198            let (a, _g) = workspace.input[rank].device_ptr(&stream);
11199            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
11200            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
11201            workspace.raw_input.push(a as u64);
11202            workspace.raw_sel.push(b as u64);
11203            workspace.raw_route_w.push(c as u64);
11204        }
11205        Ok(())
11206    }
11207
11208    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
11209    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
11210    /// throughput claim.
11211    pub fn run_tensor_parallel_routes_nvfp4(
11212        &self,
11213        experts: &ResidentNvfp4TensorParallel,
11214        input: &[f32],
11215        tokens: usize,
11216        selected: &[usize],
11217        route_weights: &[f32],
11218        experts_per_token: usize,
11219        activation_limit: Option<f32>,
11220    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
11221        validate_activations(input, tokens, experts.input_width)?;
11222        let pairs = tokens
11223            .checked_mul(experts_per_token)
11224            .ok_or("NVFP4 TP route count overflow")?;
11225        if selected.len() != pairs || route_weights.len() != pairs {
11226            return Err(format!(
11227                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
11228                 {experts_per_token} ({pairs})",
11229                selected.len(),
11230                route_weights.len(),
11231            )
11232            .into());
11233        }
11234        if !route_weights.iter().all(|weight| weight.is_finite()) {
11235            return Err("NVFP4 TP route weights contain a non-finite value".into());
11236        }
11237
11238        let mut output = vec![0.0f32; tokens * experts.input_width];
11239        for token in 0..tokens {
11240            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
11241            for slot in 0..experts_per_token {
11242                let pair = token * experts_per_token + slot;
11243                let expert = selected[pair];
11244                if expert >= experts.expert_count {
11245                    return Err(format!(
11246                        "NVFP4 TP selected expert {expert} outside 0..{}",
11247                        experts.expert_count
11248                    )
11249                    .into());
11250                }
11251                let gate = self.run_column_bank_expert_nvfp4(
11252                    &experts.gate,
11253                    &experts.macros_gate,
11254                    expert,
11255                    input_row,
11256                )?;
11257                let up = self.run_column_bank_expert_nvfp4(
11258                    &experts.up,
11259                    &experts.macros_up,
11260                    expert,
11261                    input_row,
11262                )?;
11263                let activated: Vec<f32> = gate
11264                    .iter()
11265                    .zip(&up)
11266                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
11267                    .collect();
11268                debug_assert_eq!(activated.len(), experts.expert_width);
11269                let down = self.run_row_bank_expert_nvfp4(
11270                    &experts.down,
11271                    &experts.macros_down,
11272                    expert,
11273                    &activated,
11274                )?;
11275                let weight = route_weights[pair];
11276                for (sum, value) in output
11277                    [token * experts.input_width..(token + 1) * experts.input_width]
11278                    .iter_mut()
11279                    .zip(down)
11280                {
11281                    *sum += weight * value;
11282                }
11283            }
11284        }
11285        Ok(output)
11286    }
11287}
11288
11289#[cfg(test)]
11290mod tests {
11291    use super::*;
11292
11293    #[test]
11294    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
11295        let limit = Some(7.0);
11296        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
11297        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
11298        assert!(
11299            step_expert_activation_host(-20.0, 9.0, limit).abs()
11300                < step_expert_activation_host(-20.0, 9.0, None).abs()
11301        );
11302        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
11303        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
11304        assert!(validate_step_expert_activation_limit(limit).is_ok());
11305    }
11306
11307    #[test]
11308    fn moe_residual_host_preserves_official_add_order() {
11309        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
11310        assert_eq!(output, [0.0]);
11311        assert_eq!(
11312            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
11313            "MoE residual lengths residual=1 routed=2 shared=1"
11314        );
11315    }
11316
11317    #[test]
11318    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
11319        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
11320        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
11321        assert_eq!(owners.len(), 4);
11322        for (rank, owner) in owners.iter().enumerate() {
11323            assert_eq!(owner.rank, rank);
11324            assert_eq!(owner.selected, vec![0, 36]);
11325            assert_eq!(owner.token_rows, vec![0, 0]);
11326            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
11327        }
11328    }
11329
11330    #[test]
11331    fn expert_owner_routes_validate_geometry_and_selected_experts() {
11332        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
11333        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
11334        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
11335        assert!(error.contains("outside 0..288"));
11336    }
11337
11338    #[test]
11339    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
11340        let selected = [
11341            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
11342        ];
11343        assert_eq!(
11344            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
11345            16
11346        );
11347        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
11348        assert_eq!(
11349            owners
11350                .iter()
11351                .map(|owner| owner.selected.len())
11352                .collect::<Vec<_>>(),
11353            vec![2, 4, 6, 4]
11354        );
11355        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
11356        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
11357        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
11358    }
11359
11360    #[test]
11361    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
11362        let owner0 = [0usize, 3];
11363        let owner1 = [1usize, 2];
11364        let owners = [owner0.as_slice(), owner1.as_slice()];
11365        assert_eq!(
11366            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
11367                .unwrap(),
11368            WeightedRouteCombineShape {
11369                pairs: 4,
11370                max_pairs: 12,
11371            }
11372        );
11373        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
11374        assert!(
11375            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
11376                .is_err()
11377        );
11378        assert!(
11379            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
11380                .is_err()
11381        );
11382        assert!(
11383            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
11384                .is_err()
11385        );
11386    }
11387
11388    #[test]
11389    fn native_p2p_door_is_strict_and_default_off() {
11390        assert!(!parse_step_tp_native_p2p(None).unwrap());
11391        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
11392        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
11393        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
11394        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
11395        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
11396    }
11397
11398    #[test]
11399    fn bulk_p2p_door_is_strict_and_default_off() {
11400        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
11401        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
11402        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
11403        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
11404        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
11405        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
11406    }
11407
11408    #[test]
11409    fn ep_device_arithmetic_door_is_strict_and_default_off() {
11410        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
11411        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
11412        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
11413        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
11414        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
11415        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
11416    }
11417
11418    #[test]
11419    fn f32_mirror_door_is_strict_and_default_off() {
11420        assert!(!parse_step_tp_f32_mirror(None).unwrap());
11421        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
11422        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
11423        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
11424        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
11425        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
11426    }
11427
11428    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
11429        let codes = (0..out_features * in_features)
11430            .map(|index| (index % 251) as u8)
11431            .collect();
11432        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
11433            .map(|index| index as f32 + 1.0)
11434            .collect();
11435        (codes, scales)
11436    }
11437
11438    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
11439        (0..out_features * in_features)
11440            .flat_map(|value| (value as u16).to_le_bytes())
11441            .collect()
11442    }
11443
11444    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
11445        bytes
11446            .chunks_exact(2)
11447            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
11448            .collect()
11449    }
11450
11451    #[test]
11452    fn bf16_matrix_rejects_wrong_byte_count() {
11453        let bytes = vec![0u8; 4 * 4 * 2 - 1];
11454        let matrix = Bf16Matrix {
11455            bytes: &bytes,
11456            out_features: 4,
11457            in_features: 4,
11458        };
11459        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
11460    }
11461
11462    #[test]
11463    fn replicated_device_rows_require_exact_rank_local_shapes() {
11464        assert_eq!(
11465            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
11466            12_288
11467        );
11468        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
11469        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
11470        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
11471        assert!(
11472            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
11473        );
11474        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
11475    }
11476
11477    #[test]
11478    fn replicated_device_row_refresh_requires_exact_root_source() {
11479        assert_eq!(
11480            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
11481            12_288
11482        );
11483        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
11484        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
11485        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
11486        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
11487        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
11488    }
11489
11490    #[test]
11491    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
11492        for tp in [1, 2, 4, 8] {
11493            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
11494            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
11495            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
11496            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
11497            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
11498        }
11499        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
11500        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
11501        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
11502        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
11503    }
11504
11505    #[test]
11506    fn cache_rows_split_by_token_then_rank() {
11507        let rows = (0u8..24).collect::<Vec<_>>();
11508        assert_eq!(
11509            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
11510            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
11511        );
11512        assert_eq!(
11513            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
11514            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
11515        );
11516        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
11517        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
11518    }
11519
11520    #[test]
11521    fn bf16_column_shard_preserves_contiguous_output_rows() {
11522        let bytes = bf16_matrix_bytes(4, 4);
11523        let matrix = Bf16Matrix {
11524            bytes: &bytes,
11525            out_features: 4,
11526            in_features: 4,
11527        };
11528        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
11529        assert_eq!(shard.out_features, 2);
11530        assert_eq!(shard.in_features, 4);
11531        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
11532    }
11533
11534    #[test]
11535    fn bf16_row_shard_preserves_each_input_column_window() {
11536        let bytes = bf16_matrix_bytes(3, 4);
11537        let matrix = Bf16Matrix {
11538            bytes: &bytes,
11539            out_features: 3,
11540            in_features: 4,
11541        };
11542        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
11543        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
11544    }
11545
11546    #[test]
11547    fn bf16_row_block_preserves_global_column_order() {
11548        let bytes = bf16_matrix_bytes(3, 8);
11549        let matrix = Bf16Matrix {
11550            bytes: &bytes,
11551            out_features: 3,
11552            in_features: 8,
11553        };
11554        let block = bf16_row_block(matrix, 2, 3).unwrap();
11555        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
11556    }
11557
11558    #[test]
11559    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
11560        let (codes, scales) = matrix(1280, 4096);
11561        let matrix = E4m3BlockMatrix {
11562            codes: &codes,
11563            scales: &scales,
11564            out_features: 1280,
11565            in_features: 4096,
11566        };
11567        let shard = column_shard(matrix, 2, 1).unwrap();
11568        assert_eq!(shard.out_features, 640);
11569        assert_eq!(shard.codes, &codes[640 * 4096..]);
11570        assert_eq!(shard.scales, &scales[5 * 32..]);
11571    }
11572
11573    #[test]
11574    fn row_shard_preserves_each_weight_and_scale_column_window() {
11575        let (codes, scales) = matrix(4096, 1280);
11576        let matrix = E4m3BlockMatrix {
11577            codes: &codes,
11578            scales: &scales,
11579            out_features: 4096,
11580            in_features: 1280,
11581        };
11582        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
11583        assert_eq!(shard_codes.len(), 4096 * 640);
11584        assert_eq!(&shard_codes[..640], &codes[640..1280]);
11585        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
11586        assert_eq!(shard_scales.len(), 32 * 5);
11587        assert_eq!(&shard_scales[..5], &scales[5..10]);
11588        assert_eq!(&shard_scales[5..10], &scales[15..20]);
11589    }
11590
11591    #[test]
11592    fn activation_shards_keep_token_rows_separate() {
11593        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
11594        assert_eq!(
11595            activation_shard(&activations, 2, 8, 2, 1),
11596            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
11597        );
11598    }
11599
11600    #[test]
11601    fn expert_bank_selects_expert_major_code_and_scale_planes() {
11602        let expert_count = 2;
11603        let out_features = 128;
11604        let in_features = 128;
11605        let code_stride = out_features * in_features;
11606        let codes: Vec<u8> = (0..expert_count * code_stride)
11607            .map(|index| (index % 251) as u8)
11608            .collect();
11609        let scales = vec![1.0f32, 2.0];
11610        let bank = E4m3ExpertBank {
11611            codes: &codes,
11612            scales: &scales,
11613            expert_count,
11614            out_features,
11615            in_features,
11616        };
11617        bank.validate().unwrap();
11618        let expert = bank.expert(1).unwrap();
11619        assert_eq!(expert.codes, &codes[code_stride..]);
11620        assert_eq!(expert.scales, &[2.0]);
11621    }
11622
11623    #[test]
11624    fn expert_bank_rejects_non_positive_scale() {
11625        let codes = vec![0u8; 128 * 128];
11626        let scales = vec![0.0f32];
11627        let bank = E4m3ExpertBank {
11628            codes: &codes,
11629            scales: &scales,
11630            expert_count: 1,
11631            out_features: 128,
11632            in_features: 128,
11633        };
11634        assert!(bank.validate().unwrap_err().contains("non-positive"));
11635    }
11636
11637    #[test]
11638    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
11639        let expert_count = 2;
11640        let out_features = 256;
11641        let in_features = 128;
11642        let code_stride = out_features * in_features;
11643        let scale_stride = 2;
11644        let codes = (0..expert_count * code_stride)
11645            .map(|index| (index % 251) as u8)
11646            .collect::<Vec<_>>();
11647        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
11648        let bank = E4m3ExpertBank {
11649            codes: &codes,
11650            scales: &scales,
11651            expert_count,
11652            out_features,
11653            in_features,
11654        };
11655
11656        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
11657        assert_eq!(rank.out_features, 128);
11658        assert_eq!(rank.in_features, 128);
11659        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
11660        assert_eq!(rank.scales, vec![11.0, 21.0]);
11661        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
11662        assert_eq!(
11663            &rank.codes[128 * 128..],
11664            &codes[code_stride + 128 * 128..2 * code_stride]
11665        );
11666        assert_eq!(scale_stride, scales.len() / expert_count);
11667    }
11668
11669    #[test]
11670    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
11671        let expert_count = 2;
11672        let out_features = 128;
11673        let in_features = 256;
11674        let code_stride = out_features * in_features;
11675        let codes = (0..expert_count * code_stride)
11676            .map(|index| (index % 251) as u8)
11677            .collect::<Vec<_>>();
11678        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
11679        let bank = E4m3ExpertBank {
11680            codes: &codes,
11681            scales: &scales,
11682            expert_count,
11683            out_features,
11684            in_features,
11685        };
11686
11687        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
11688        assert_eq!(rank.out_features, 128);
11689        assert_eq!(rank.in_features, 128);
11690        assert_eq!(rank.k_blocks, Some(1));
11691        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
11692        assert_eq!(rank.scales, vec![11.0, 21.0]);
11693        assert_eq!(&rank.codes[..128], &codes[128..256]);
11694        assert_eq!(
11695            &rank.codes[128 * 128..128 * 128 + 128],
11696            &codes[code_stride + 128..code_stride + 256]
11697        );
11698    }
11699
11700    #[test]
11701    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
11702        let expert_count = 2;
11703        let out_features = 256;
11704        let in_features = 512;
11705        let code_stride = out_features * in_features;
11706        let mut codes = vec![0u8; expert_count * code_stride];
11707        for expert in 0..expert_count {
11708            for row in 0..out_features {
11709                for block in 0..4 {
11710                    let value = (expert * 80 + block * 16 + row % 16) as u8;
11711                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
11712                    codes[start..start + FP8_BLOCK].fill(value);
11713                }
11714            }
11715        }
11716        let scales = vec![
11717            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,
11718            112.0, 113.0, 114.0,
11719        ];
11720        let bank = E4m3ExpertBank {
11721            codes: &codes,
11722            scales: &scales,
11723            expert_count,
11724            out_features,
11725            in_features,
11726        };
11727
11728        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
11729        assert_eq!(rank.out_features, out_features);
11730        assert_eq!(rank.in_features, 256);
11731        assert_eq!(rank.k_blocks, Some(2));
11732        assert_eq!(rank.code_stride, out_features * 256);
11733        assert_eq!(rank.scale_stride, 4);
11734        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
11735        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
11736
11737        let block_stride = out_features * FP8_BLOCK;
11738        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
11739        assert!(
11740            rank.codes[block_stride..block_stride + FP8_BLOCK]
11741                .iter()
11742                .all(|&code| code == 48)
11743        );
11744        assert!(
11745            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
11746                .iter()
11747                .all(|&code| code == 112)
11748        );
11749        assert!(
11750            rank.codes
11751                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
11752                .iter()
11753                .all(|&code| code == 128)
11754        );
11755    }
11756
11757    #[test]
11758    fn step_ep_layer_specs_are_literal_and_fail_closed() {
11759        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
11760        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
11761        assert_eq!(
11762            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
11763            vec![StepEpLayerSpec {
11764                layer: 24,
11765                devices: vec![1, 2],
11766            }]
11767        );
11768        assert_eq!(
11769            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
11770            vec![
11771                StepEpLayerSpec {
11772                    layer: 24,
11773                    devices: vec![1, 2],
11774                },
11775                StepEpLayerSpec {
11776                    layer: 25,
11777                    devices: vec![1, 2],
11778                },
11779                StepEpLayerSpec {
11780                    layer: 31,
11781                    devices: vec![0, 2],
11782                },
11783            ]
11784        );
11785        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
11786        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
11787        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
11788        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
11789        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
11790        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
11791        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
11792    }
11793
11794    #[test]
11795    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
11796        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
11797        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
11798        assert_eq!(
11799            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
11800            vec![
11801                StepTpLayerSpec {
11802                    layer: 24,
11803                    devices: vec![1, 2],
11804                },
11805                StepTpLayerSpec {
11806                    layer: 25,
11807                    devices: vec![1, 2],
11808                },
11809            ]
11810        );
11811        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
11812        assert!(error.contains("MEMRA_STEP_TP"));
11813        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
11814        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
11815
11816        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
11817        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
11818        assert_eq!(all.first().unwrap().layer, 0);
11819        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
11820        let devices = (0..8).collect::<Vec<_>>();
11821        assert!(all.iter().all(|spec| spec.devices == devices));
11822        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
11823    }
11824}
11825
11826// ===== Whole-token graph builder (increment B) ==================================================
11827//
11828// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
11829// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
11830// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
11831// device and records a child + its dependency edges. A token then assembles as ONE multi-device
11832// parent (children per section per layer), launched once per token — the launch-collapse the
11833// per-layer minis could not reach (routes-mini negative, 2026-08-21).
11834
11835/// One captured section: the child graph plus which parent node it became, and the CUDA
11836/// context it was captured under (exec memset updates need it).
11837struct TokenGraphChild {
11838    graph: cudarc::driver::CudaGraph,
11839    node: cudarc::driver::sys::CUgraphNode,
11840    ctx: cudarc::driver::sys::CUcontext,
11841}
11842
11843/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
11844/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
11845/// handles address the parent's CLONED child graphs (the M1-probed update path).
11846struct TokenGraphFaSite {
11847    ctx: cudarc::driver::sys::CUcontext,
11848    memset_o: cudarc::driver::sys::CUgraphNode,
11849    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
11850    fa: cudarc::driver::sys::CUgraphNode,
11851    combine: cudarc::driver::sys::CUgraphNode,
11852    window: usize,
11853    n_head: usize,
11854    n_head_kv: usize,
11855    head_dim: usize,
11856}
11857
11858pub struct TokenGraphBuilder {
11859    parent: cudarc::driver::sys::CUgraph,
11860    children: Vec<TokenGraphChild>,
11861    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
11862    /// several while a parallel group is open.
11863    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
11864    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
11865    /// non-group section (they never gate a parallel group merge — the SH1 shape).
11866    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
11867    /// Open parallel group: sections issued under the same group id fork from the SAME
11868    /// predecessor set and merge into the frontier together when the group closes.
11869    group: Option<(
11870        u32,
11871        Vec<cudarc::driver::sys::CUgraphNode>,
11872        Vec<cudarc::driver::sys::CUgraphNode>,
11873    )>,
11874}
11875
11876// SAFETY: single decode thread; graph handles are process handles.
11877unsafe impl Send for TokenGraphBuilder {}
11878
11879impl TokenGraphBuilder {
11880    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
11881        use cudarc::driver::sys;
11882        let mut parent: sys::CUgraph = std::ptr::null_mut();
11883        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
11884        if r != sys::CUresult::CUDA_SUCCESS {
11885            return Err(format!("token graph create: {r:?}").into());
11886        }
11887        Ok(Self {
11888            parent,
11889            children: Vec::new(),
11890            frontier: Vec::new(),
11891            pending_detached: Vec::new(),
11892            group: None,
11893        })
11894    }
11895
11896    fn push_child(
11897        &mut self,
11898        graph: cudarc::driver::CudaGraph,
11899        parallel_group: Option<u32>,
11900        detached: bool,
11901        absorb: bool,
11902        ctx: cudarc::driver::sys::CUcontext,
11903    ) -> Result<(), Box<dyn std::error::Error>> {
11904        use cudarc::driver::sys;
11905        // Resolve the dependency set: serial sections depend on the current frontier; a
11906        // parallel-group section depends on the frontier AS OF the group opening; a
11907        // DETACHED section forks like a group member but joins only the next serial section.
11908        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
11909            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
11910            (state, Some(group)) => {
11911                // opening a new group (closing any previous one first)
11912                if let Some((_, _, members)) = state.take() {
11913                    self.frontier = members;
11914                }
11915                let base = self.frontier.clone();
11916                *state = Some((group, base.clone(), Vec::new()));
11917                base
11918            }
11919            (state, None) if detached => match state.as_ref() {
11920                Some((_, base, _)) => base.clone(),
11921                None => self.frontier.clone(),
11922            },
11923            (state, None) => {
11924                if let Some((_, _, members)) = state.take() {
11925                    self.frontier = members;
11926                }
11927                let mut deps = self.frontier.clone();
11928                if absorb {
11929                    deps.append(&mut self.pending_detached);
11930                }
11931                deps
11932            }
11933        };
11934        let mut node: sys::CUgraphNode = std::ptr::null_mut();
11935        let r = unsafe {
11936            sys::cuGraphAddChildGraphNode(
11937                &mut node,
11938                self.parent,
11939                if deps.is_empty() {
11940                    std::ptr::null()
11941                } else {
11942                    deps.as_ptr()
11943                },
11944                deps.len(),
11945                graph.cu_graph(),
11946            )
11947        };
11948        if r != sys::CUresult::CUDA_SUCCESS {
11949            return Err(format!("token graph child: {r:?}").into());
11950        }
11951        match (&mut self.group, parallel_group, detached) {
11952            (_, None, true) => self.pending_detached.push(node),
11953            (Some((_, _, members)), Some(_), _) => members.push(node),
11954            _ => self.frontier = vec![node],
11955        }
11956        self.children.push(TokenGraphChild { graph, node, ctx });
11957        Ok(())
11958    }
11959
11960    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
11961        use cudarc::driver::sys;
11962        if let Some((_, _, members)) = self.group.take() {
11963            self.frontier = members;
11964        }
11965        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
11966        // the node handles the exec update path (M1) addresses.
11967        let mut fa_sites = Vec::new();
11968        for child in &self.children {
11969            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
11970                fa_sites.push(site);
11971            }
11972        }
11973        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
11974        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
11975        if r != sys::CUresult::CUDA_SUCCESS {
11976            return Err(format!("token graph instantiate: {r:?}").into());
11977        }
11978        Ok(TokenGraph {
11979            exec,
11980            parent: self.parent,
11981            _children: self.children,
11982            fa_sites,
11983        })
11984    }
11985}
11986
11987/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
11988/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
11989fn discover_fa_site(
11990    child_node: cudarc::driver::sys::CUgraphNode,
11991    ctx: cudarc::driver::sys::CUcontext,
11992) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
11993    use cudarc::driver::sys;
11994    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
11995        if r == sys::CUresult::CUDA_SUCCESS {
11996            Ok(())
11997        } else {
11998            Err(format!("{what}: {r:?}").into())
11999        }
12000    }
12001    let mut graph: sys::CUgraph = std::ptr::null_mut();
12002    unsafe {
12003        cu_try(
12004            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
12005            "fa-site child GetGraph",
12006        )?;
12007    }
12008    let mut count: usize = 0;
12009    unsafe {
12010        cu_try(
12011            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
12012            "fa-site GetNodes(count)",
12013        )?;
12014    }
12015    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
12016    unsafe {
12017        cu_try(
12018            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
12019            "fa-site GetNodes",
12020        )?;
12021    }
12022    nodes.truncate(count);
12023    let node_type =
12024        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
12025            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
12026            unsafe {
12027                cu_try(
12028                    sys::cuGraphNodeGetType(node, &mut ty),
12029                    "fa-site NodeGetType",
12030                )?;
12031            }
12032            Ok(ty)
12033        };
12034    let memsets: Vec<sys::CUgraphNode> = {
12035        let mut v = Vec::new();
12036        for &node in &nodes {
12037            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
12038                v.push(node);
12039            }
12040        }
12041        v
12042    };
12043    if memsets.len() != 3 {
12044        return Ok(None);
12045    }
12046    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
12047    let dependents =
12048        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
12049            let mut n: usize = 0;
12050            unsafe {
12051                cu_try(
12052                    sys::cuGraphNodeGetDependentNodes_v2(
12053                        node,
12054                        std::ptr::null_mut(),
12055                        std::ptr::null_mut(),
12056                        &mut n,
12057                    ),
12058                    "fa-site GetDependentNodes(count)",
12059                )?;
12060            }
12061            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
12062            unsafe {
12063                cu_try(
12064                    sys::cuGraphNodeGetDependentNodes_v2(
12065                        node,
12066                        v.as_mut_ptr(),
12067                        std::ptr::null_mut(),
12068                        &mut n,
12069                    ),
12070                    "fa-site GetDependentNodes",
12071                )?;
12072            }
12073            v.truncate(n);
12074            Ok(v)
12075        };
12076    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
12077    // ordered among themselves but interchangeable for width updates.
12078    let mut fa: Option<sys::CUgraphNode> = None;
12079    let mut last_memset: Option<sys::CUgraphNode> = None;
12080    for &ms in &memsets {
12081        for dep in dependents(ms)? {
12082            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
12083                fa = Some(dep);
12084                last_memset = Some(ms);
12085            }
12086        }
12087    }
12088    let (Some(fa), Some(_last)) = (fa, last_memset) else {
12089        return Ok(None);
12090    };
12091    let mut combine: Option<sys::CUgraphNode> = None;
12092    for dep in dependents(fa)? {
12093        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
12094            combine = Some(dep);
12095        }
12096    }
12097    let Some(combine) = combine else {
12098        return Ok(None);
12099    };
12100    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
12101    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
12102    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12103    unsafe {
12104        cu_try(
12105            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
12106            "fa-site KernelNodeGetParams",
12107        )?;
12108    }
12109    let arg_i32 =
12110        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
12111    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
12112    // Identify the o-partial memset (hd x wider than the m/l pair).
12113    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
12114        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
12115        unsafe {
12116            cu_try(
12117                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
12118                "fa-site MemsetNodeGetParams",
12119            )?;
12120        }
12121        Ok(mp.width)
12122    };
12123    let mut widest = memsets[0];
12124    for &ms in &memsets[1..] {
12125        if width_of(ms)? > width_of(widest)? {
12126            widest = ms;
12127        }
12128    }
12129    let memset_m: Vec<sys::CUgraphNode> =
12130        memsets.iter().copied().filter(|&m| m != widest).collect();
12131    Ok(Some(TokenGraphFaSite {
12132        ctx,
12133        memset_o: widest,
12134        memset_m: [memset_m[0], memset_m[1]],
12135        fa,
12136        combine,
12137        window: win as usize,
12138        n_head: nh as usize,
12139        n_head_kv: nhkv as usize,
12140        head_dim: hd as usize,
12141    }))
12142}
12143
12144pub struct TokenGraph {
12145    exec: cudarc::driver::sys::CUgraphExec,
12146    parent: cudarc::driver::sys::CUgraph,
12147    _children: Vec<TokenGraphChild>,
12148    fa_sites: Vec<TokenGraphFaSite>,
12149}
12150
12151unsafe impl Send for TokenGraph {}
12152
12153impl TokenGraph {
12154    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
12155    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
12156    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
12157    /// move together so the exec always matches what a fresh build at `bucket` would bake.
12158    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
12159        use cudarc::driver::sys;
12160        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
12161            if r == sys::CUresult::CUDA_SUCCESS {
12162                Ok(())
12163            } else {
12164                Err(format!("{what}: {r:?}").into())
12165            }
12166        }
12167        for site in &self.fa_sites {
12168            let layer_bucket = if site.window > 0 {
12169                bucket.min(site.window)
12170            } else {
12171                bucket
12172            };
12173            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
12174            let nsp = layer_bucket.div_ceil(sp).max(1);
12175            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
12176            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12177            unsafe {
12178                cu_try(
12179                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
12180                    "retarget fa GetParams",
12181                )?;
12182                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
12183                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
12184                params.gridDimY = nsp as u32;
12185                cu_try(
12186                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
12187                    "retarget fa SetParams",
12188                )?;
12189            }
12190            // combine: nsp (slot 6).
12191            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
12192            unsafe {
12193                cu_try(
12194                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
12195                    "retarget combine GetParams",
12196                )?;
12197                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
12198                cu_try(
12199                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
12200                    "retarget combine SetParams",
12201                )?;
12202            }
12203            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
12204            let set_width =
12205                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
12206                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
12207                    unsafe {
12208                        cu_try(
12209                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
12210                            "retarget memset GetParams",
12211                        )?;
12212                    }
12213                    mp.width = width;
12214                    unsafe {
12215                        cu_try(
12216                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
12217                            "retarget memset SetParams",
12218                        )?;
12219                    }
12220                    Ok(())
12221                };
12222            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
12223            set_width(site.memset_m[0], site.n_head * nsp)?;
12224            set_width(site.memset_m[1], site.n_head * nsp)?;
12225        }
12226        Ok(())
12227    }
12228
12229    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
12230        use cudarc::driver::sys;
12231        let _main = e.gpu.enter_main()?;
12232        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
12233        if r != sys::CUresult::CUDA_SUCCESS {
12234            return Err(format!("token graph launch: {r:?}").into());
12235        }
12236        Ok(())
12237    }
12238}
12239
12240impl Drop for TokenGraph {
12241    fn drop(&mut self) {
12242        unsafe {
12243            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
12244            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
12245        }
12246    }
12247}
12248
12249std::thread_local! {
12250    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
12251        const { std::cell::RefCell::new(None) };
12252}
12253
12254/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
12255pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
12256    let builder = TokenGraphBuilder::new()?;
12257    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
12258    Ok(())
12259}
12260
12261/// Take the finished parent (ends build mode).
12262pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
12263    let builder = TOKEN_GRAPH_BUILDER
12264        .with(|cell| cell.borrow_mut().take())
12265        .ok_or("token graph build was not begun")?;
12266    builder.finish()
12267}
12268
12269/// True while the thread-local builder is armed.
12270pub fn token_graph_building() -> bool {
12271    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
12272}
12273
12274/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
12275/// stream capture on `engine`'s stream and records the child. Sections sharing a
12276/// `parallel_group` id fork from the same predecessor set and merge together. The closure
12277/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
12278pub fn graph_section<F>(
12279    engine: &Engine,
12280    parallel_group: Option<u32>,
12281    f: F,
12282) -> Result<(), Box<dyn std::error::Error>>
12283where
12284    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12285{
12286    graph_section_opts(engine, parallel_group, false, false, f)
12287}
12288
12289/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
12290pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
12291where
12292    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12293{
12294    graph_section_opts(engine, None, false, true, f)
12295}
12296
12297/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
12298/// group base) and is joined only by the next serial section — never gates a group merge.
12299pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
12300where
12301    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12302{
12303    graph_section_opts(engine, None, true, false, f)
12304}
12305
12306pub fn graph_section_opts<F>(
12307    engine: &Engine,
12308    parallel_group: Option<u32>,
12309    detached: bool,
12310    absorb: bool,
12311    f: F,
12312) -> Result<(), Box<dyn std::error::Error>>
12313where
12314    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
12315{
12316    let building = token_graph_building();
12317    if !building {
12318        let mut f = f;
12319        return f();
12320    }
12321    let (child, ctx) = {
12322        let _main = engine.gpu.enter_main()?;
12323        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
12324        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
12325        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
12326            return Err(format!("graph section ctx query: {r:?}").into());
12327        }
12328        let mut f = f;
12329        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
12330        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
12331        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
12332        (child, ctx)
12333    };
12334    TOKEN_GRAPH_BUILDER.with(|cell| {
12335        cell.borrow_mut()
12336            .as_mut()
12337            .expect("builder checked above")
12338            .push_child(child, parallel_group, detached, absorb, ctx)
12339    })
12340}