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
194/// MEMRA_SPEC_FA2=1 (the DSpark verify lesson): the T=2 verify walk defers each column's
195/// ATTENTION CORE — the dcw arm appends the column's K/V and stashes its post-rope q and
196/// gate rows, then ONE fa_decode_dcw2 per rank walks the KV stream once for both columns
197/// (per-row causal bounds; bit-identical per row under the equal-partition guard), the
198/// per-row combine writes both gated rows, and the o_proj join runs on the TCOL slabs.
199pub(crate) fn spec_fa2_on() -> bool {
200    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
201    *ON.get_or_init(|| std::env::var("MEMRA_SPEC_FA2").as_deref() == Ok("1"))
202}
203thread_local! {
204    /// The verify driver arms the column before each per-column attention call; the dcw
205    /// arm takes it (once) and stashes q/gate instead of running fa+finish.
206    static SPEC_FA2_DEFER: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
207    static SPEC_FA2_STASHED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
208}
209pub(crate) fn set_spec_fa2_defer(c: Option<usize>) {
210    SPEC_FA2_DEFER.with(|x| x.set(c));
211}
212pub(crate) fn take_spec_fa2_defer() -> Option<usize> {
213    SPEC_FA2_DEFER.with(|x| x.take())
214}
215pub(crate) fn set_spec_fa2_stashed() {
216    SPEC_FA2_STASHED.with(|x| x.set(true));
217}
218pub(crate) fn take_spec_fa2_stashed() -> bool {
219    SPEC_FA2_STASHED.with(|x| x.replace(false))
220}
221
222pub(crate) fn sel_mirror_on() -> bool {
223    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
224    *ON.get_or_init(|| std::env::var("MEMRA_SEL_MIRROR").as_deref() == Ok("1"))
225}
226
227/// MEMRA_STEP_NVFP4_EP2=1: whole-expert (expert-parallel) NVFP4 banks at 2 ranks — expert e
228/// lives ENTIRE on rank (e & 1) at bank slot (e >> 1), replacing the TP column/row shards
229/// (same total VRAM; both sets cannot coexist). Decode rides owner-guarded full-width
230/// sweeps with per-rank slot-ordered partial sums; the cross-rank join is unchanged.
231/// NUMERIC-CLASS door (the slot chain regroups per rank): run-gen argmax gate + battery +
232/// fresh tape, the DEV_ROUTES acceptance class.
233pub(crate) fn step_nvfp4_ep2_on() -> bool {
234    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
235    *ON.get_or_init(|| std::env::var("MEMRA_STEP_NVFP4_EP2").as_deref() == Ok("1"))
236}
237
238pub(crate) fn sel_down8_on() -> bool {
239    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
240    *ON.get_or_init(|| std::env::var("MEMRA_SEL_DOWN8").as_deref() == Ok("1"))
241}
242
243pub(crate) fn oproj_direct_on() -> bool {
244    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
245    *ON.get_or_init(|| std::env::var("MEMRA_OPROJ_DIRECT").as_deref() == Ok("1"))
246}
247
248pub(crate) fn raw_copy_bytes(
249    dst: u64,
250    src: u64,
251    bytes: usize,
252    engine: &Engine,
253) -> Result<(), Box<dyn std::error::Error>> {
254    use cudarc::driver::sys;
255    let r = unsafe {
256        sys::cuMemcpyAsync(
257            dst as sys::CUdeviceptr,
258            src as sys::CUdeviceptr,
259            bytes,
260            engine.stream().cu_stream() as sys::CUstream,
261        )
262    };
263    if r == sys::CUresult::CUDA_SUCCESS {
264        Ok(())
265    } else {
266        // MEMRA_RAW_COPY_TRACE=1: a raw D2D failure carries no call site by itself, and
267        // every slab-width bug in the t-row family surfaces here. Operands + backtrace.
268        if std::env::var("MEMRA_RAW_COPY_TRACE").as_deref() == Ok("1") {
269            eprintln!(
270                "[raw-copy-fail] dst={dst:#x} src={src:#x} bytes={bytes} {r:?}\n{}",
271                std::backtrace::Backtrace::force_capture()
272            );
273        }
274        Err(format!("raw_copy_bytes: {r:?} bytes={bytes} dst={dst:#x} src={src:#x}").into())
275    }
276}
277
278pub fn step_expert_activation_host(gate: f32, up: f32, limit: Option<f32>) -> f32 {
279    let silu = gate / (1.0 + (-gate).exp());
280    match limit {
281        Some(limit) => silu.min(limit) * up.clamp(-limit, limit),
282        None => silu * up,
283    }
284}
285
286#[derive(Debug, Clone, PartialEq, Eq)]
287struct ExpertOwnerRoutes {
288    rank: usize,
289    selected: Vec<usize>,
290    token_rows: Vec<usize>,
291    global_pairs: Vec<usize>,
292}
293
294fn partition_expert_owner_routes(
295    expert_count: usize,
296    ranks: usize,
297    tokens: usize,
298    experts_per_token: usize,
299    selected: &[usize],
300) -> Result<Vec<ExpertOwnerRoutes>, String> {
301    if expert_count == 0
302        || ranks == 0
303        || tokens == 0
304        || experts_per_token == 0
305        || expert_count % ranks != 0
306    {
307        return Err(format!(
308            "invalid expert-owner route geometry experts={expert_count} ranks={ranks} \
309             tokens={tokens} experts_per_token={experts_per_token}"
310        ));
311    }
312    let pairs = tokens
313        .checked_mul(experts_per_token)
314        .ok_or("expert-owner route count overflow")?;
315    if selected.len() != pairs {
316        return Err(format!(
317            "expert-owner routes {} != {tokens}x{experts_per_token} ({pairs})",
318            selected.len()
319        ));
320    }
321    let per_rank = expert_count / ranks;
322    let mut owners = (0..ranks)
323        .map(|rank| ExpertOwnerRoutes {
324            rank,
325            selected: Vec::new(),
326            token_rows: Vec::new(),
327            global_pairs: Vec::new(),
328        })
329        .collect::<Vec<_>>();
330    for (pair, &expert) in selected.iter().enumerate() {
331        if expert >= expert_count {
332            return Err(format!(
333                "expert-owner route {pair} selects expert {expert} outside 0..{expert_count}"
334            ));
335        }
336        let rank = expert / per_rank;
337        owners[rank].selected.push(expert - rank * per_rank);
338        owners[rank].token_rows.push(pair / experts_per_token);
339        owners[rank].global_pairs.push(pair);
340    }
341    Ok(owners)
342}
343
344fn validate_step_grouped_owner_routes(
345    expert_count: usize,
346    tokens: usize,
347    selected: &[usize],
348) -> Result<usize, String> {
349    if expert_count != STEP_GROUPED_FP8_EXPERTS || tokens == 0 {
350        return Err(format!(
351            "official Step owner-grouped FP8 requires {} experts and nonzero tokens, got \
352             experts={expert_count} tokens={tokens}",
353            STEP_GROUPED_FP8_EXPERTS
354        ));
355    }
356    let pairs = tokens
357        .checked_mul(STEP_GROUPED_FP8_TOP_K)
358        .ok_or("official Step owner-grouped FP8 route count overflow")?;
359    if selected.len() != pairs {
360        return Err(format!(
361            "official Step owner-grouped FP8 routes {} != {tokens}x{} ({pairs})",
362            selected.len(),
363            STEP_GROUPED_FP8_TOP_K,
364        ));
365    }
366    for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
367        let mut unique = routes.to_vec();
368        unique.sort_unstable();
369        unique.dedup();
370        if unique.len() != STEP_GROUPED_FP8_TOP_K {
371            return Err(format!(
372                "official Step owner-grouped FP8 token {token} routes are not top-8 unique: \
373                 {routes:?}"
374            ));
375        }
376    }
377    Ok(pairs)
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq)]
381struct WeightedRouteCombineShape {
382    pairs: usize,
383    max_pairs: usize,
384}
385
386fn validate_weighted_route_combine(
387    width: usize,
388    experts_per_token: usize,
389    max_tokens: usize,
390    tokens: usize,
391    owner_global_pairs: &[&[usize]],
392    route_weights: &[f32],
393) -> Result<WeightedRouteCombineShape, String> {
394    if width == 0
395        || experts_per_token == 0
396        || max_tokens == 0
397        || tokens == 0
398        || tokens > max_tokens
399        || width > i32::MAX as usize
400        || experts_per_token > i32::MAX as usize
401        || tokens > i32::MAX as usize
402    {
403        return Err(format!(
404            "invalid weighted route combine geometry width={width} experts_per_token=\
405             {experts_per_token} tokens={tokens}/{max_tokens}"
406        ));
407    }
408    let pairs = tokens
409        .checked_mul(experts_per_token)
410        .ok_or("weighted route combine pair count overflow")?;
411    let max_pairs = max_tokens
412        .checked_mul(experts_per_token)
413        .ok_or("weighted route combine capacity overflow")?;
414    if route_weights.len() != pairs || !route_weights.iter().all(|weight| weight.is_finite()) {
415        return Err(format!(
416            "weighted route combine weights {} != pairs {pairs} or contain a non-finite value",
417            route_weights.len()
418        ));
419    }
420    let mut seen = vec![false; pairs];
421    let mut observed = 0usize;
422    for pairs_for_owner in owner_global_pairs {
423        observed = observed
424            .checked_add(pairs_for_owner.len())
425            .ok_or("weighted route combine observed pair count overflow")?;
426        for &pair in *pairs_for_owner {
427            if pair >= pairs || std::mem::replace(&mut seen[pair], true) {
428                return Err(format!(
429                    "weighted route combine pair {pair} is outside 0..{pairs} or duplicated"
430                ));
431            }
432        }
433    }
434    if observed != pairs || seen.iter().any(|present| !present) {
435        return Err(format!(
436            "weighted route combine owner schedules cover {observed} of {pairs} canonical pairs"
437        ));
438    }
439    Ok(WeightedRouteCombineShape { pairs, max_pairs })
440}
441
442fn cache_rank_rows(
443    rows: &[u8],
444    tokens: usize,
445    local_token_bytes: usize,
446    ranks: usize,
447    rank: usize,
448) -> Result<Vec<u8>, String> {
449    if ranks == 0 || rank >= ranks {
450        return Err(format!(
451            "TP cache rank {rank} is outside a {ranks}-rank layout"
452        ));
453    }
454    let global_token_bytes = local_token_bytes
455        .checked_mul(ranks)
456        .ok_or("TP cache global token-byte overflow")?;
457    let expected = tokens
458        .checked_mul(global_token_bytes)
459        .ok_or("TP cache row-byte overflow")?;
460    if rows.len() != expected {
461        return Err(format!(
462            "TP cache rows contain {} bytes, expected {tokens}x{global_token_bytes}={expected}",
463            rows.len()
464        ));
465    }
466    let mut shard = Vec::with_capacity(tokens * local_token_bytes);
467    for token in 0..tokens {
468        let start = token * global_token_bytes + rank * local_token_bytes;
469        shard.extend_from_slice(&rows[start..start + local_token_bytes]);
470    }
471    Ok(shard)
472}
473
474fn parse_step_tp_native_p2p(value: Option<&str>) -> Result<bool, String> {
475    match value {
476        None | Some("") | Some("0") => Ok(false),
477        Some("1") => Ok(true),
478        Some(value) => Err(format!(
479            "MEMRA_STEP_TP_NATIVE_P2P={value:?} is invalid; expected 0 or 1"
480        )),
481    }
482}
483
484pub fn step_tp_native_p2p_enabled() -> Result<bool, String> {
485    parse_step_tp_native_p2p(std::env::var("MEMRA_STEP_TP_NATIVE_P2P").ok().as_deref())
486}
487
488fn parse_step_tp_bulk_p2p(value: Option<&str>) -> Result<bool, String> {
489    match value {
490        None | Some("") | Some("0") => Ok(false),
491        Some("1") => Ok(true),
492        Some(value) => Err(format!(
493            "MEMRA_STEP_TP_BULK_P2P={value:?} is invalid; expected 0 or 1"
494        )),
495    }
496}
497
498pub fn step_tp_bulk_p2p_enabled() -> Result<bool, String> {
499    parse_step_tp_bulk_p2p(std::env::var("MEMRA_STEP_TP_BULK_P2P").ok().as_deref())
500}
501
502fn parse_step_ep_device_arithmetic(value: Option<&str>) -> Result<bool, String> {
503    match value {
504        None | Some("") | Some("0") => Ok(false),
505        Some("1") => Ok(true),
506        Some(value) => Err(format!(
507            "MEMRA_STEP_EP_DEVICE_ARITHMETIC={value:?} is invalid; expected 0 or 1"
508        )),
509    }
510}
511
512fn parse_step_nvfp4_dev_routes(value: Option<&str>) -> Result<bool, String> {
513    match value {
514        None | Some("") | Some("0") => Ok(false),
515        Some("1") => Ok(true),
516        Some(value) => Err(format!(
517            "MEMRA_STEP_NVFP4_DEV_ROUTES={value:?} is invalid; expected 0 or 1"
518        )),
519    }
520}
521
522/// Opt-in door for the device-resident NVFP4 TP routed-expert decode program. Default OFF; the
523/// host-canonical program remains the oracle until the device path carries its own gates.
524pub fn step_nvfp4_dev_routes_enabled() -> Result<bool, String> {
525    parse_step_nvfp4_dev_routes(std::env::var("MEMRA_STEP_NVFP4_DEV_ROUTES").ok().as_deref())
526}
527
528pub fn step_ep_device_arithmetic_enabled() -> Result<bool, String> {
529    parse_step_ep_device_arithmetic(
530        std::env::var("MEMRA_STEP_EP_DEVICE_ARITHMETIC")
531            .ok()
532            .as_deref(),
533    )
534}
535
536fn parse_step_tp_f32_mirror(value: Option<&str>) -> Result<bool, String> {
537    match value {
538        None | Some("") | Some("0") => Ok(false),
539        Some("1") => Ok(true),
540        Some(value) => Err(format!(
541            "MEMRA_STEP_TP_F32_MIRROR={value:?} is invalid; expected 0 or 1"
542        )),
543    }
544}
545
546pub fn step_tp_f32_mirror_enabled() -> Result<bool, String> {
547    parse_step_tp_f32_mirror(std::env::var("MEMRA_STEP_TP_F32_MIRROR").ok().as_deref())
548}
549
550fn parse_step_tp_decode_v2(value: Option<&str>) -> Result<bool, String> {
551    match value {
552        None | Some("") | Some("0") => Ok(false),
553        Some("1") => Ok(true),
554        Some(value) => Err(format!(
555            "MEMRA_STEP_TP_DECODE_V2={value:?} is invalid; expected 0 or 1"
556        )),
557    }
558}
559
560/// The v2 rank-local Step decode-attention driver: persistent workspaces, evented cross-stream
561/// ordering, and a root-device O reduction — same kernels, values, and canonical reduction order
562/// as the v1 driver (it requires the F32 mirror so no per-call weight expansion exists on either
563/// side of the comparison).
564pub fn step_tp_decode_v2_enabled() -> Result<bool, String> {
565    parse_step_tp_decode_v2(std::env::var("MEMRA_STEP_TP_DECODE_V2").ok().as_deref())
566}
567
568fn parse_step_tp_qkv_fused(value: Option<&str>) -> Result<bool, String> {
569    match value {
570        None | Some("") | Some("0") => Ok(false),
571        Some("1") => Ok(true),
572        Some(value) => Err(format!(
573            "MEMRA_STEP_TP_QKV_FUSED={value:?} is invalid; expected 0 or 1"
574        )),
575    }
576}
577
578fn parse_step_tp_dev_router(value: Option<&str>) -> Result<bool, String> {
579    match value {
580        None | Some("") | Some("0") => Ok(false),
581        Some("1") => Ok(true),
582        Some(value) => Err(format!(
583            "MEMRA_STEP_TP_DEV_ROUTER={value:?} is invalid; expected 0 or 1"
584        )),
585    }
586}
587
588/// Device-side sigmoid top-k routing for the TP device-IO expert program: the per-layer host
589/// logits readback (the last per-layer host sync) disappears. Selection tie-breaking may
590/// differ from the host router — NUMERIC-CLASS door, run-gen argmax gate + boot battery.
591pub fn step_tp_dev_router_enabled() -> Result<bool, String> {
592    parse_step_tp_dev_router(std::env::var("MEMRA_STEP_TP_DEV_ROUTER").ok().as_deref())
593}
594
595fn parse_step_tp_graph(value: Option<&str>) -> Result<bool, String> {
596    match value {
597        None | Some("") | Some("0") => Ok(false),
598        Some("1") => Ok(true),
599        Some(value) => Err(format!(
600            "MEMRA_STEP_TP_GRAPH={value:?} is invalid; expected 0 or 1"
601        )),
602    }
603}
604
605fn parse_step_tp_dcw(value: Option<&str>) -> Result<bool, String> {
606    match value {
607        None | Some("") | Some("0") => Ok(false),
608        Some("1") => Ok(true),
609        Some(value) => Err(format!(
610            "MEMRA_STEP_TP_DCW={value:?} is invalid; expected 0 or 1"
611        )),
612    }
613}
614
615/// Device-counter attention path (graph increment A run EAGERLY): append at len_d - base_d,
616/// inc_i32, fa over the counter-derived window — with bucket = the effective t_kv this is
617/// bit-identical to the host-row + kvmod path (the one-partition law), and it is the exact
618/// child content the capture wraps. Rebase tokens and sub-vec-floor contexts fall back.
619pub fn step_tp_dcw_enabled() -> Result<bool, String> {
620    parse_step_tp_dcw(std::env::var("MEMRA_STEP_TP_DCW").ok().as_deref())
621}
622
623/// CUDA-graph door for the shape-stable TP segments (first increment: the device-routed
624/// expert program — per-layer multi-device parents built from per-rank children, launched on
625/// the model engine's stream; zero per-token node updates). Mechanism proven by
626/// tp_graph_probe. VALUE-IDENTICAL: the graphs replay exactly the eager kernel/copy sequence.
627pub fn step_tp_graph_enabled() -> Result<bool, String> {
628    parse_step_tp_graph(std::env::var("MEMRA_STEP_TP_GRAPH").ok().as_deref())
629}
630
631/// Fused single-launch QKV projection inside the v2 decode driver — a NUMERIC-CLASS door
632/// (per-row deterministic tree reduce instead of the chunked cuBLASLt program), default OFF,
633/// gated by the run-gen argmax gate + boot battery like MEMRA_STEP_NVFP4_DEV_ROUTES.
634pub fn step_tp_qkv_fused_enabled() -> Result<bool, String> {
635    parse_step_tp_qkv_fused(std::env::var("MEMRA_STEP_TP_QKV_FUSED").ok().as_deref())
636}
637
638#[derive(Debug, Clone, PartialEq, Eq)]
639pub struct StepEpLayerSpec {
640    pub layer: usize,
641    pub devices: Vec<usize>,
642}
643
644pub type StepTpLayerSpec = StepEpLayerSpec;
645
646fn parse_step_layer_specs(
647    flag: &str,
648    value: Option<&str>,
649    allow_full_model: bool,
650) -> Result<Vec<StepEpLayerSpec>, String> {
651    let Some(value) = value else {
652        return Ok(Vec::new());
653    };
654    if value.is_empty() || value == "0" {
655        return Ok(Vec::new());
656    }
657
658    let mut specs = Vec::new();
659    for item in value.split(';') {
660        let (layers, devices) = item.split_once('@').ok_or_else(|| {
661            let layers = if allow_full_model {
662                "LAYER[-LAYER] or all"
663            } else {
664                "LAYER[-LAYER]"
665            };
666            format!("{flag} must be {layers}@DEVICE,DEVICE[;...]")
667        })?;
668        let (first, last) = if layers == "all" {
669            if !allow_full_model {
670                return Err(format!(
671                    "{flag} does not support the full-model shorthand; assign routed layers \
672                     explicitly"
673                ));
674            }
675            (0, STEP37_TRUNK_LAYERS - 1)
676        } else {
677            match layers.split_once('-') {
678                Some((first, last)) => {
679                    let first = first
680                        .parse::<usize>()
681                        .map_err(|_| format!("{flag} layer {first:?} is not an integer"))?;
682                    let last = last
683                        .parse::<usize>()
684                        .map_err(|_| format!("{flag} layer {last:?} is not an integer"))?;
685                    if first > last {
686                        return Err(format!("{flag} layer range {first}-{last} is reversed"));
687                    }
688                    if last - first + 1 > 128 {
689                        return Err(format!(
690                            "{flag} layer range {first}-{last} exceeds the 128-layer parser cap"
691                        ));
692                    }
693                    (first, last)
694                }
695                None => {
696                    let layer = layers
697                        .parse::<usize>()
698                        .map_err(|_| format!("{flag} layer {layers:?} is not an integer"))?;
699                    (layer, layer)
700                }
701            }
702        };
703        let devices = devices
704            .split(',')
705            .map(|device| {
706                device
707                    .parse::<usize>()
708                    .map_err(|_| format!("{flag} device {device:?} is not an integer"))
709            })
710            .collect::<Result<Vec<_>, _>>()?;
711        if !(2..=8).contains(&devices.len()) {
712            return Err(format!(
713                "{flag} requires 2..=8 devices, got {}",
714                devices.len()
715            ));
716        }
717        let mut unique = devices.clone();
718        unique.sort_unstable();
719        unique.dedup();
720        if unique.len() != devices.len() {
721            return Err(format!("{flag} devices must be distinct, got {devices:?}"));
722        }
723        for layer in first..=last {
724            if specs
725                .iter()
726                .any(|existing: &StepEpLayerSpec| existing.layer == layer)
727            {
728                return Err(format!("{flag} assigns layer {layer} more than once"));
729            }
730            specs.push(StepEpLayerSpec {
731                layer,
732                devices: devices.clone(),
733            });
734        }
735    }
736    Ok(specs)
737}
738
739pub fn parse_step_ep_layer_specs(value: Option<&str>) -> Result<Vec<StepEpLayerSpec>, String> {
740    parse_step_layer_specs("MEMRA_STEP_EP", value, false)
741}
742
743pub fn step_ep_layer_specs() -> Result<Vec<StepEpLayerSpec>, String> {
744    parse_step_ep_layer_specs(std::env::var("MEMRA_STEP_EP").ok().as_deref())
745}
746
747pub fn parse_step_tp_layer_specs(value: Option<&str>) -> Result<Vec<StepTpLayerSpec>, String> {
748    parse_step_layer_specs("MEMRA_STEP_TP", value, true)
749}
750
751pub fn step_tp_layer_specs() -> Result<Vec<StepTpLayerSpec>, String> {
752    parse_step_tp_layer_specs(std::env::var("MEMRA_STEP_TP").ok().as_deref())
753}
754
755#[derive(Clone, Copy)]
756pub struct E4m3BlockMatrix<'a> {
757    pub codes: &'a [u8],
758    pub scales: &'a [f32],
759    pub out_features: usize,
760    pub in_features: usize,
761}
762
763impl E4m3BlockMatrix<'_> {
764    fn validate(&self) -> Result<(), String> {
765        let code_count = self
766            .out_features
767            .checked_mul(self.in_features)
768            .ok_or_else(|| "E4M3 matrix size overflow".to_string())?;
769        if self.codes.len() != code_count {
770            return Err(format!(
771                "E4M3 code count {} != {}x{} ({code_count})",
772                self.codes.len(),
773                self.out_features,
774                self.in_features,
775            ));
776        }
777        let scale_count =
778            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
779        if self.scales.len() != scale_count {
780            return Err(format!(
781                "E4M3 scale count {} != {scale_count} for {}x{}",
782                self.scales.len(),
783                self.out_features,
784                self.in_features,
785            ));
786        }
787        if !self
788            .scales
789            .iter()
790            .all(|scale| scale.is_finite() && *scale > 0.0)
791        {
792            return Err("E4M3 scale grid contains a non-finite or non-positive value".to_string());
793        }
794        Ok(())
795    }
796}
797
798#[derive(Clone, Copy)]
799pub struct E4m3ExpertBank<'a> {
800    pub codes: &'a [u8],
801    pub scales: &'a [f32],
802    pub expert_count: usize,
803    pub out_features: usize,
804    pub in_features: usize,
805}
806
807impl E4m3ExpertBank<'_> {
808    fn validate(&self) -> Result<(), String> {
809        if self.expert_count == 0 {
810            return Err("E4M3 expert bank is empty".to_string());
811        }
812        let code_stride = self
813            .out_features
814            .checked_mul(self.in_features)
815            .ok_or_else(|| "E4M3 expert code stride overflow".to_string())?;
816        let code_count = self
817            .expert_count
818            .checked_mul(code_stride)
819            .ok_or_else(|| "E4M3 expert code count overflow".to_string())?;
820        if self.codes.len() != code_count {
821            return Err(format!(
822                "E4M3 expert code count {} != {}x{} ({code_count})",
823                self.codes.len(),
824                self.expert_count,
825                code_stride,
826            ));
827        }
828        let scale_stride =
829            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
830        let scale_count = self
831            .expert_count
832            .checked_mul(scale_stride)
833            .ok_or_else(|| "E4M3 expert scale count overflow".to_string())?;
834        if self.scales.len() != scale_count {
835            return Err(format!(
836                "E4M3 expert scale count {} != {}x{} ({scale_count})",
837                self.scales.len(),
838                self.expert_count,
839                scale_stride,
840            ));
841        }
842        if !self
843            .scales
844            .iter()
845            .all(|scale| scale.is_finite() && *scale > 0.0)
846        {
847            return Err(
848                "E4M3 expert scale grid contains a non-finite or non-positive value".to_string(),
849            );
850        }
851        Ok(())
852    }
853
854    pub fn expert(&self, expert: usize) -> Result<E4m3BlockMatrix<'_>, String> {
855        if expert >= self.expert_count {
856            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
857        }
858        let code_stride = self.out_features * self.in_features;
859        let scale_stride =
860            self.out_features.div_ceil(FP8_BLOCK) * self.in_features.div_ceil(FP8_BLOCK);
861        Ok(E4m3BlockMatrix {
862            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
863            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
864            out_features: self.out_features,
865            in_features: self.in_features,
866        })
867    }
868}
869
870pub struct ColumnParallelResult {
871    pub gathered: Vec<f32>,
872    pub rank_outputs: Vec<Vec<f32>>,
873}
874
875pub struct RowParallelResult {
876    pub reduced: Vec<f32>,
877    pub rank_partials: Vec<Vec<f32>>,
878}
879
880#[derive(Clone, Copy)]
881pub struct Bf16Matrix<'a> {
882    pub bytes: &'a [u8],
883    pub out_features: usize,
884    pub in_features: usize,
885}
886
887impl Bf16Matrix<'_> {
888    pub fn validate(&self) -> Result<(), String> {
889        if self.out_features == 0 || self.in_features == 0 {
890            return Err("BF16 matrix dimensions must be nonzero".into());
891        }
892        let expected = self
893            .out_features
894            .checked_mul(self.in_features)
895            .and_then(|values| values.checked_mul(2))
896            .ok_or("BF16 matrix byte count overflow")?;
897        if self.bytes.len() != expected {
898            return Err(format!(
899                "BF16 matrix bytes {} != {}x{}x2 ({expected})",
900                self.bytes.len(),
901                self.out_features,
902                self.in_features,
903            ));
904        }
905        Ok(())
906    }
907}
908
909struct ResidentE4m3Rank {
910    codes: CudaSlice<u8>,
911    scales: CudaSlice<f32>,
912    out_features: usize,
913    in_features: usize,
914}
915
916enum ResidentBf16Weight {
917    Bf16(CudaSlice<u8>),
918    F32(CudaSlice<f32>),
919}
920
921impl ResidentBf16Weight {
922    fn ordinal(&self) -> usize {
923        match self {
924            Self::Bf16(bytes) => bytes.ordinal(),
925            Self::F32(values) => values.ordinal(),
926        }
927    }
928}
929
930struct ResidentBf16Rank {
931    weight: ResidentBf16Weight,
932    out_features: usize,
933    in_features: usize,
934}
935
936pub struct ResidentColumnParallel {
937    ranks: Vec<ResidentE4m3Rank>,
938    out_features: usize,
939    in_features: usize,
940}
941
942pub struct ResidentRowParallel {
943    ranks: Vec<ResidentE4m3Rank>,
944    out_features: usize,
945    in_features: usize,
946}
947
948pub struct ResidentBf16ColumnParallel {
949    ranks: Vec<ResidentBf16Rank>,
950    out_features: usize,
951    in_features: usize,
952    canonical_chunk_rows: Option<usize>,
953}
954
955pub struct ResidentBf16RowParallel {
956    ranks: Vec<ResidentBf16Rank>,
957    out_features: usize,
958    in_features: usize,
959}
960
961pub struct ResidentStepBf16RowParallel {
962    ranks: Vec<Vec<ResidentBf16Rank>>,
963    out_features: usize,
964    in_features: usize,
965    canonical_chunk_cols: usize,
966}
967
968/// Root-owned BF16 sigmoid router with persistent F32 weight, bias, and active mask.
969pub struct ResidentSigmoidTopKRouter {
970    weight: CudaSlice<f32>,
971    correction_bias: CudaSlice<f32>,
972    active: CudaSlice<u8>,
973    root_device: usize,
974    input_width: usize,
975    expert_count: usize,
976    experts_per_token: usize,
977    active_count: usize,
978    scaling_factor: f32,
979    route_norm: bool,
980}
981
982pub struct SigmoidTopKHostOutput {
983    pub logits: Vec<f32>,
984    pub selected: Vec<u32>,
985    pub weights: Vec<f32>,
986}
987
988/// Full BF16 SwiGLU weights replicated independently on every runtime rank.
989pub struct ResidentReplicatedBf16SwiGlu {
990    gate: Vec<ResidentBf16Rank>,
991    up: Vec<ResidentBf16Rank>,
992    down: Vec<ResidentBf16Rank>,
993    input_width: usize,
994    intermediate_width: usize,
995}
996
997/// One token-major F32 batch replicated across a native-P2P rank group.
998///
999/// Every allocation is owned by its matching rank CUDA context. This is the generic handoff
1000/// substrate between independently sharded operators; it carries no model or topology claim.
1001pub struct ResidentReplicatedDeviceRows {
1002    ranks: Vec<CudaSlice<f32>>,
1003    tokens: usize,
1004    width: usize,
1005}
1006
1007impl ResidentReplicatedDeviceRows {
1008    pub fn tokens(&self) -> usize {
1009        self.tokens
1010    }
1011
1012    pub fn width(&self) -> usize {
1013        self.width
1014    }
1015
1016    pub fn ranks(&self) -> usize {
1017        self.ranks.len()
1018    }
1019}
1020
1021/// Canonical MoE output order: routed plus shared, then add the layer residual.
1022pub fn moe_residual_host(
1023    residual: &[f32],
1024    routed: &[f32],
1025    shared: &[f32],
1026) -> Result<Vec<f32>, String> {
1027    if residual.len() != routed.len() || residual.len() != shared.len() {
1028        return Err(format!(
1029            "MoE residual lengths residual={} routed={} shared={}",
1030            residual.len(),
1031            routed.len(),
1032            shared.len()
1033        ));
1034    }
1035    let ffn = routed
1036        .iter()
1037        .zip(shared)
1038        .map(|(&routed, &shared)| routed + shared)
1039        .collect::<Vec<_>>();
1040    Ok(residual
1041        .iter()
1042        .zip(ffn)
1043        .map(|(&residual, ffn)| residual + ffn)
1044        .collect())
1045}
1046
1047pub use memra_kv::{
1048    KvRingAppend, ResidentTpKvCache, ResidentTpKvCacheRank, TpKvAppendPlan, TpKvTransaction,
1049};
1050
1051/// Persistent TP2/TP4/TP8 routed-expert reference.
1052///
1053/// Rank-local checkpoint shards are uploaded once and remain tied to their owning CUDA context.
1054/// Activations and deterministic host-staged collectives remain per invocation. This is the
1055/// correctness substrate for serving TP/EP, not product-throughput evidence.
1056pub struct ResidentTpExpert {
1057    gate: ResidentColumnParallel,
1058    up: ResidentColumnParallel,
1059    down: ResidentRowParallel,
1060    input_width: usize,
1061    expert_width: usize,
1062}
1063
1064struct ResidentE4m3ExpertBankRank {
1065    codes: CudaSlice<u8>,
1066    scales: CudaSlice<f32>,
1067    expert_range: Range<usize>,
1068    out_features: usize,
1069    in_features: usize,
1070    code_stride: usize,
1071    scale_stride: usize,
1072    /// TP row banks are packed by native 128-wide K block so reduction can replay the
1073    /// checkpoint's global block order exactly. Other banks remain row-major.
1074    k_blocks: Option<usize>,
1075}
1076
1077struct PackedE4m3ExpertBankRank {
1078    codes: Vec<u8>,
1079    scales: Vec<f32>,
1080    expert_range: Range<usize>,
1081    out_features: usize,
1082    in_features: usize,
1083    code_stride: usize,
1084    scale_stride: usize,
1085    k_blocks: Option<usize>,
1086}
1087
1088struct ResidentEpRank {
1089    gate: ResidentE4m3ExpertBankRank,
1090    up: ResidentE4m3ExpertBankRank,
1091    down: ResidentE4m3ExpertBankRank,
1092}
1093
1094/// Persistent expert-parallel reference.
1095///
1096/// Every routed expert has exactly one owner rank. Shared experts are deliberately absent from
1097/// this object because Step replicates them per rank. Routes execute on the owner CUDA context.
1098/// The default oracle stages through host memory; the native path peer-dispatches inputs and
1099/// peer-returns owner outputs while preserving host-canonical activation and accumulation.
1100pub struct ResidentExpertParallel {
1101    ranks: Vec<ResidentEpRank>,
1102    expert_count: usize,
1103    input_width: usize,
1104    expert_width: usize,
1105}
1106
1107/// Projection-level output from the opt-in official Step grouped-FP8 gate.
1108///
1109/// Rows remain pair-major. Routing, weighted combine, and production integration are deliberately
1110/// outside this gate-only adapter.
1111pub struct StepGroupedFp8ProjectionOutput {
1112    pub gate: Vec<f32>,
1113    pub up: Vec<f32>,
1114    pub down: Vec<f32>,
1115}
1116
1117/// Prepared official Step grouped-FP8 projection gate.
1118///
1119/// The complete tensor banks, both CSR schedules, input, activation buffer, and three projection
1120/// workspaces are uploaded or allocated once. Repeated execution performs no device allocation.
1121pub struct PreparedStepGroupedFp8Gate {
1122    device: usize,
1123    gate: ResidentE4m3ExpertBankRank,
1124    up: ResidentE4m3ExpertBankRank,
1125    down: ResidentE4m3ExpertBankRank,
1126    input: CudaSlice<f32>,
1127    route_csr: DeviceExpertCsr,
1128    down_csr: DeviceExpertCsr,
1129    gate_workspace: Fp8GroupedWorkspace,
1130    up_workspace: Fp8GroupedWorkspace,
1131    down_workspace: Fp8GroupedWorkspace,
1132    activation: CudaSlice<f32>,
1133    activation_limit: Option<f32>,
1134    tokens: usize,
1135    pairs: usize,
1136}
1137
1138impl PreparedStepGroupedFp8Gate {
1139    pub fn tokens(&self) -> usize {
1140        self.tokens
1141    }
1142
1143    pub fn pairs(&self) -> usize {
1144        self.pairs
1145    }
1146}
1147
1148struct PreparedStepGroupedExpertOwner {
1149    rank: usize,
1150    global_pairs: Vec<usize>,
1151    route_csr: DeviceExpertCsr,
1152    down_csr: DeviceExpertCsr,
1153    gate_workspace: Fp8GroupedWorkspace,
1154    up_workspace: Fp8GroupedWorkspace,
1155    down_workspace: Fp8GroupedWorkspace,
1156    activation: CudaSlice<f32>,
1157}
1158
1159struct StepGroupedExpertOwnerSchedule {
1160    global_pairs: Vec<usize>,
1161    route_csr: ExpertCsr,
1162    down_csr: ExpertCsr,
1163}
1164
1165/// Prepared official Step expert-owner grouped-FP8 projection gate.
1166///
1167/// Route partitioning, owner-local CSR uploads, input dispatch, activation buffers, and grouped
1168/// workspaces are persistent. Projection rows are scattered back to canonical pair order only
1169/// after every owner has completed its rank-local program.
1170pub struct PreparedStepGroupedExpertParallelGate {
1171    rank_inputs: Vec<CudaSlice<f32>>,
1172    owners: Vec<PreparedStepGroupedExpertOwner>,
1173    activation_limit: Option<f32>,
1174    tokens: usize,
1175    pairs: usize,
1176    max_tokens: usize,
1177    max_pairs: usize,
1178    input_width: usize,
1179    expert_width: usize,
1180    generation: u64,
1181    executed_generation: Option<u64>,
1182    ready: bool,
1183}
1184
1185impl PreparedStepGroupedExpertParallelGate {
1186    pub fn tokens(&self) -> usize {
1187        self.tokens
1188    }
1189
1190    pub fn pairs(&self) -> usize {
1191        self.pairs
1192    }
1193
1194    pub fn max_tokens(&self) -> usize {
1195        self.max_tokens
1196    }
1197
1198    pub fn input_width(&self) -> usize {
1199        self.input_width
1200    }
1201
1202    pub fn expert_width(&self) -> usize {
1203        self.expert_width
1204    }
1205
1206    pub fn set_activation_limit(&mut self, limit: Option<f32>) -> Result<(), String> {
1207        validate_step_expert_activation_limit(limit)?;
1208        self.activation_limit = limit;
1209        self.executed_generation = None;
1210        Ok(())
1211    }
1212
1213    pub fn active_owners(&self) -> usize {
1214        self.owners
1215            .iter()
1216            .filter(|owner| !owner.global_pairs.is_empty())
1217            .count()
1218    }
1219
1220    pub fn owner_pair_counts(&self) -> Vec<usize> {
1221        self.owners
1222            .iter()
1223            .map(|owner| owner.global_pairs.len())
1224            .collect()
1225    }
1226
1227    pub fn generation(&self) -> u64 {
1228        self.generation
1229    }
1230}
1231
1232struct PreparedPeerWeightedRouteOwner {
1233    token_rows: CudaSlice<i32>,
1234    slots: CudaSlice<i32>,
1235    weights: CudaSlice<f32>,
1236    active_pairs: usize,
1237}
1238
1239/// Persistent root-side weighted combine for peer-owned canonical route rows.
1240///
1241/// Owner metadata, one reusable peer staging buffer, the canonical slot bank, weight bank, and
1242/// output are allocated once. Refreshes update metadata prefixes; execution peer-copies active
1243/// rows, scatters them by canonical token/slot, and reduces in the requested numeric order.
1244pub struct PreparedPeerWeightedRouteCombine {
1245    root_device: usize,
1246    owners: Vec<PreparedPeerWeightedRouteOwner>,
1247    peer_staging: CudaSlice<f32>,
1248    slots: CudaSlice<f32>,
1249    weights: CudaSlice<f32>,
1250    output: CudaSlice<f32>,
1251    peer_devices: Vec<usize>,
1252    peer_outputs: Vec<CudaSlice<f32>>,
1253    width: usize,
1254    experts_per_token: usize,
1255    max_tokens: usize,
1256    max_pairs: usize,
1257    tokens: usize,
1258    pairs: usize,
1259    projection_generation: u64,
1260    output_generation: Option<u64>,
1261    broadcast_generation: Option<u64>,
1262    ready: bool,
1263}
1264
1265impl PreparedPeerWeightedRouteCombine {
1266    pub fn tokens(&self) -> usize {
1267        self.tokens
1268    }
1269
1270    pub fn pairs(&self) -> usize {
1271        self.pairs
1272    }
1273
1274    pub fn owner_pair_counts(&self) -> Vec<usize> {
1275        self.owners.iter().map(|owner| owner.active_pairs).collect()
1276    }
1277
1278    pub fn distributed_ranks(&self) -> usize {
1279        1 + self.peer_outputs.len()
1280    }
1281}
1282
1283struct ResidentTpExpertBank {
1284    gate: Vec<ResidentE4m3ExpertBankRank>,
1285    up: Vec<ResidentE4m3ExpertBankRank>,
1286    down: Vec<ResidentE4m3ExpertBankRank>,
1287    expert_count: usize,
1288    input_width: usize,
1289    expert_width: usize,
1290}
1291
1292/// Persistent tensor-parallel expert bank.
1293///
1294/// Every rank owns a checkpoint-aligned output-row shard of every gate/up projection and an
1295/// input-column shard of every down projection. Activations cross deterministic host-staged
1296/// collectives on hosts where native peer copies are unavailable or corrupt.
1297pub struct ResidentTensorParallel {
1298    bank: ResidentTpExpertBank,
1299}
1300
1301/// Multi-context TP correctness runtime. Each rank owns an independent `Engine` and CUDA context.
1302///
1303/// Host bounce is the default oracle. Native P2P is opt-in and preserves the oracle's global
1304/// checkpoint-block reduction order; it remains a correctness path until serving gates and
1305/// repeated performance evidence qualify it.
1306pub struct TpE4m3HostBounce {
1307    devices: Vec<usize>,
1308    ranks: Vec<Engine>,
1309    native_p2p: bool,
1310    ep_device_arithmetic: bool,
1311    bulk_p2p: bool,
1312    /// v2 decode-attention workspace (MEMRA_STEP_TP_DECODE_V2). One per runtime, shared by
1313    /// every TP attention layer — the buffer shapes are geometry-constant across the trunk.
1314    decode_v2: std::sync::Mutex<Vec<StepTpDecodeV2Ws>>,
1315}
1316
1317/// Persistent workspace of the v2 rank-local decode-attention driver.
1318///
1319/// Buffers live in their producing rank's CUDA context, are never freed, and events are
1320/// re-recorded per call — the pp.rs `BoundarySlot` discipline — so the per-token path has no
1321/// cuMemAlloc, no cross-stream free, and no host round-trip. Every buffer is fully overwritten
1322/// before its consumers run in the same call; nothing carries state between tokens.
1323/// Per-rank attn_gate row shards for the fused QKV+gate kernel, in the weight class the
1324/// fused kernels read (F32 mirror or raw checkpoint bf16).
1325pub enum StepTpGateShards<'a> {
1326    F32(&'a [crate::CudaSlice<f32>]),
1327    Bf16(&'a [crate::CudaSlice<u8>]),
1328}
1329
1330pub struct StepTpDecodeV2Ws {
1331    /// T-COLUMN verify slabs (spec MTP): per-rank [t, local_dim] projections computed by
1332    /// the weight-amortized qkvg_tcol kernel; the col-select door copies one column into
1333    /// the single-row buffers and everything downstream runs the unmodified t=1 program.
1334    pub(crate) tcol_q: Vec<CudaSlice<f32>>,
1335    pub(crate) tcol_k: Vec<CudaSlice<f32>>,
1336    pub(crate) tcol_v: Vec<CudaSlice<f32>>,
1337    pub(crate) tcol_g: Vec<CudaSlice<f32>>,
1338    pub(crate) tcol_in: Vec<CudaSlice<f32>>,
1339    pub(crate) tcol_cap: usize,
1340    /// MEMRA_TCOL_OPROJ slabs: per-rank stashed `gated` rows ([8, local_q_dim]), per-rank
1341    /// b4_tcol partials ([8, o_out]), a root-side peer pull of rank1's partial slab, and
1342    /// the root-side joined `mixed` slab. Armed lazily by the first stash.
1343    /// MEMRA_SPEC_FA2 slabs: per-rank stashed post-rope q rows ([2, local_q_dim]), gate
1344    /// rows ([2, heads/ranks]) and the two gated outputs the per-row combine writes
1345    /// ([2, local_q_dim]). Armed lazily by the first stash.
1346    pub(crate) fa2_q: Vec<CudaSlice<f32>>,
1347    pub(crate) fa2_gate: Vec<CudaSlice<f32>>,
1348    pub(crate) fa2_gated: Vec<CudaSlice<f32>>,
1349    pub(crate) fa2_cap: usize,
1350    /// T-ROW rope/append twin scratch: per-rank roped-k rows ([8, local_kv]), per-row
1351    /// last-block counters ([8]) and the per-tick position slab ([8]). Armed with the
1352    /// fa2 slabs.
1353    rope_k_t: Vec<CudaSlice<f32>>,
1354    rope_ctr_t: Vec<CudaSlice<u32>>,
1355    rope_pos_t: Vec<CudaSlice<i32>>,
1356    /// Per-rank combined 6-word row tables, keyed by the caller's (layer, session-set,
1357    /// base-arming) signature.
1358    rows_tabs: Vec<std::collections::HashMap<u64, CudaSlice<u64>>>,
1359    tcol_gated: Vec<CudaSlice<f32>>,
1360    tcol_opart: Vec<CudaSlice<f32>>,
1361    tcol_opeer: Option<CudaSlice<f32>>,
1362    tcol_omix: Option<CudaSlice<f32>>,
1363    tcol_ocap: usize,
1364    // rank-context buffers, indexed by rank (pub(crate): the v2 driver in hybrid_forward
1365    // feeds them to the KV transaction and attention kernels between the two v2 phases)
1366    pub(crate) q_raw: Vec<CudaSlice<f32>>,
1367    pub(crate) k_raw: Vec<CudaSlice<f32>>,
1368    pub(crate) v_raw: Vec<CudaSlice<f32>>,
1369    pub(crate) q: Vec<CudaSlice<f32>>,
1370    pub(crate) k: Vec<CudaSlice<f32>>,
1371    pub(crate) pos: Vec<CudaSlice<i32>>,
1372    /// FUSION #1 last-block counters (one per rank; atomicInc auto-resets per launch).
1373    pub(crate) fuse_ctr: Vec<CudaSlice<u32>>,
1374    pub(crate) gate: Vec<CudaSlice<f32>>,
1375    pub(crate) attn_out: Vec<CudaSlice<f32>>,
1376    pub(crate) gated: Vec<CudaSlice<f32>>,
1377    /// [rank][block] O partials, each `o_out` wide, in the owning rank's context.
1378    o_partials: Vec<Vec<CudaSlice<f32>>>,
1379    /// Recorded on each rank's stream after its per-call work; root waits before peer reads.
1380    ev_rank: Vec<CudaEvent>,
1381    // root-context buffers
1382    peer_partial: CudaSlice<f32>,
1383    reduce_a: CudaSlice<f32>,
1384    reduce_b: CudaSlice<f32>,
1385    /// Never written; the canonical zero start of the v1 add chain.
1386    zeros: CudaSlice<f32>,
1387    pub(crate) k_shadow: CudaSlice<f32>,
1388    pub(crate) v_shadow: CudaSlice<f32>,
1389    ev_refresh: CudaEvent,
1390    ev_oproj: CudaEvent,
1391    // model-engine (e) context
1392    gate_e: CudaSlice<f32>,
1393    /// Per-token stages (e-ctx, fixed addresses): one eager e-stream copy each per layer; the
1394    /// rank flows raw-copy FROM them, which is exactly the shape graph capture needs.
1395    pub(crate) h_stage: Option<CudaSlice<f32>>,
1396    pub(crate) pos_stage: Option<CudaSlice<i32>>,
1397    /// Workspace-owned per-rank attention input rows (the stage flow copies into THESE, not
1398    /// the per-layer decode_input buffers — the workspace is shared across layers, so every
1399    /// captured/raw address it uses must be layer-invariant).
1400    attn_in: Vec<CudaSlice<f32>>,
1401    /// Cached raw pointers of the stage-flow operands (set when the stages arm).
1402    raw_h_stage: u64,
1403    raw_pos_stage: u64,
1404    raw_attn_in: Vec<u64>,
1405    raw_pos: Vec<u64>,
1406    raw_o_partial1: u64,
1407    raw_peer_partial: u64,
1408    raw_k1: u64,
1409    raw_v1: u64,
1410    raw_k_shadow: u64,
1411    raw_v_shadow: u64,
1412    /// Token-graph e-context mirrors (armed by the orchestrator): the root section
1413    /// raw-copies the reduced attention output and the shadow rows here so the e-glue
1414    /// children read same-context memory (cross-context kernel args are capture-illegal).
1415    raw_mixed_stage_e: u64,
1416    raw_reduce_a: u64,
1417    raw_shadow_stage_e: (u64, u64),
1418    ev_entry: CudaEvent,
1419    e_device: usize,
1420    // geometry pins
1421    local_q_dim: usize,
1422    local_kv_dim: usize,
1423    heads: usize,
1424    pub(crate) o_out: usize,
1425    o_block_cols: usize,
1426    blocks_per_rank: usize,
1427}
1428
1429impl TpE4m3HostBounce {
1430    pub fn new(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1431        Self::new_inner(devices, false, false, false, false)
1432    }
1433
1434    pub fn new_native_p2p(devices: &[usize]) -> Result<Self, Box<dyn std::error::Error>> {
1435        Self::new_inner(devices, false, true, false, false)
1436    }
1437
1438    pub fn new_native_p2p_device_arithmetic(
1439        devices: &[usize],
1440    ) -> Result<Self, Box<dyn std::error::Error>> {
1441        Self::new_inner(devices, false, true, true, false)
1442    }
1443
1444    pub(crate) fn new_configured(
1445        devices: &[usize],
1446        native_p2p: bool,
1447        ep_device_arithmetic: bool,
1448        bulk_p2p: bool,
1449    ) -> Result<Self, Box<dyn std::error::Error>> {
1450        Self::new_inner(devices, false, native_p2p, ep_device_arithmetic, bulk_p2p)
1451    }
1452
1453    /// Single-rank execution of the canonical checkpoint-block TP program.
1454    ///
1455    /// This is an oracle for distributed exactness, not a serving topology. It lets gates compare
1456    /// TP=1 and TP>1 with the same packing, kernel launches, and deterministic reduction order.
1457    pub fn new_single_rank_oracle(device: usize) -> Result<Self, Box<dyn std::error::Error>> {
1458        Self::new_inner(&[device], true, false, false, false)
1459    }
1460
1461    fn new_inner(
1462        devices: &[usize],
1463        allow_single_rank: bool,
1464        native_p2p: bool,
1465        ep_device_arithmetic: bool,
1466        bulk_p2p: bool,
1467    ) -> Result<Self, Box<dyn std::error::Error>> {
1468        if ep_device_arithmetic && !native_p2p {
1469            return Err("device-resident EP arithmetic requires native P2P".into());
1470        }
1471        if bulk_p2p && !native_p2p {
1472            return Err("bulk TP transport requires native P2P".into());
1473        }
1474        let minimum = if allow_single_rank { 1 } else { 2 };
1475        if !(minimum..=8).contains(&devices.len()) {
1476            return Err(format!(
1477                "TP reference requires {minimum}..=8 devices, got {}",
1478                devices.len()
1479            )
1480            .into());
1481        }
1482        let mut unique = devices.to_vec();
1483        unique.sort_unstable();
1484        unique.dedup();
1485        if unique.len() != devices.len() {
1486            return Err(format!("TP devices must be distinct, got {devices:?}").into());
1487        }
1488        let ranks = devices
1489            .iter()
1490            .map(|&device| Engine::new(device))
1491            .collect::<Result<Vec<_>, _>>()?;
1492        if native_p2p {
1493            configure_native_p2p(&ranks, devices)?;
1494        }
1495        if allow_single_rank {
1496            eprintln!(
1497                "[tp] canonical oracle transport=local device={} performance_claim=false",
1498                devices[0]
1499            );
1500        } else if native_p2p {
1501            if ep_device_arithmetic {
1502                eprintln!(
1503                    "[tp] correctness transport=native-p2p devices={devices:?} \
1504                     native_p2p=true activation=device-host-exact \
1505                     accumulation=device-host-exact output=root-readback \
1506                     bulk_p2p={bulk_p2p} performance_claim=false"
1507                );
1508            } else {
1509                eprintln!(
1510                    "[tp] correctness transport=native-p2p devices={devices:?} \
1511                     native_p2p=true activation=host-canonical bulk_p2p={bulk_p2p} \
1512                     performance_claim=false"
1513                );
1514            }
1515        } else {
1516            eprintln!(
1517                "[tp] correctness transport=host-bounce devices={devices:?} \
1518                 native_p2p=false performance_claim=false"
1519            );
1520        }
1521        Ok(Self {
1522            devices: devices.to_vec(),
1523            ranks,
1524            native_p2p,
1525            ep_device_arithmetic,
1526            bulk_p2p,
1527            decode_v2: std::sync::Mutex::new(Vec::new()),
1528        })
1529    }
1530
1531    pub fn devices(&self) -> &[usize] {
1532        &self.devices
1533    }
1534
1535    pub fn native_p2p(&self) -> bool {
1536        self.native_p2p
1537    }
1538
1539    pub fn bulk_p2p(&self) -> bool {
1540        self.bulk_p2p
1541    }
1542
1543    pub fn expert_activation_label(&self) -> &'static str {
1544        if self.ep_device_arithmetic {
1545            "device-host-exact"
1546        } else {
1547            "host-canonical"
1548        }
1549    }
1550
1551    pub fn expert_accumulation_label(&self) -> &'static str {
1552        self.expert_activation_label()
1553    }
1554
1555    pub fn expert_output_label(&self) -> &'static str {
1556        if self.ep_device_arithmetic {
1557            "root-readback"
1558        } else {
1559            "host-accumulated"
1560        }
1561    }
1562
1563    pub fn transport_label(&self) -> &'static str {
1564        if self.devices.len() == 1 {
1565            "local"
1566        } else if self.native_p2p {
1567            "native-p2p"
1568        } else {
1569            "host-bounce"
1570        }
1571    }
1572
1573    pub fn device_names(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
1574        self.ranks
1575            .iter()
1576            .map(|rank| rank.ctx().name().map_err(Into::into))
1577            .collect()
1578    }
1579
1580    /// Correctness-gate access to the engine that owns one TP rank.
1581    ///
1582    /// Model execution should prefer collective methods on this runtime. This accessor exists so
1583    /// focused gates can prove that the rank-local projection outputs remain device-resident
1584    /// through the next ownership boundary before that boundary is wired into serving.
1585    pub fn rank_engine(&self, rank: usize) -> Option<&Engine> {
1586        self.ranks.get(rank)
1587    }
1588
1589    pub fn allocate_tp_kv_cache(
1590        &self,
1591        kv_dim_k: usize,
1592        kv_dim_v: usize,
1593        capacity: usize,
1594    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1595        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, None)
1596    }
1597
1598    pub fn allocate_tp_swa_kv_cache(
1599        &self,
1600        kv_dim_k: usize,
1601        kv_dim_v: usize,
1602        capacity: usize,
1603        window: usize,
1604    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1605        if window == 0 {
1606            return Err("TP SWA KV window must be nonzero".into());
1607        }
1608        self.allocate_tp_kv_cache_inner(kv_dim_k, kv_dim_v, capacity, Some(window))
1609    }
1610
1611    fn allocate_tp_kv_cache_inner(
1612        &self,
1613        kv_dim_k: usize,
1614        kv_dim_v: usize,
1615        capacity: usize,
1616        window: Option<usize>,
1617    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1618        if capacity == 0 || capacity > i32::MAX as usize {
1619            return Err(
1620                format!("TP KV capacity must be in 1..={}, got {capacity}", i32::MAX).into(),
1621            );
1622        }
1623        let tp = self.ranks.len();
1624        let shape = crate::cache::tp_kv_rank_allocation_shape(kv_dim_k, kv_dim_v, tp)?;
1625        let physical_rows = window
1626            .map(|window| crate::cache::swa_ring_rows(window, capacity))
1627            .unwrap_or(capacity);
1628        let k_plane_bytes = physical_rows
1629            .checked_mul(shape.k_token_bytes)
1630            .and_then(|bytes| bytes.checked_add(8))
1631            .ok_or("TP KV K plane-byte overflow")?;
1632        let v_plane_bytes = physical_rows
1633            .checked_mul(shape.v_token_bytes)
1634            .and_then(|bytes| bytes.checked_add(8))
1635            .ok_or("TP KV V plane-byte overflow")?;
1636        let mut ranks = Vec::with_capacity(tp);
1637        for engine in &self.ranks {
1638            let _main = engine.gpu.enter_main()?;
1639            ranks.push(ResidentTpKvCacheRank::new(
1640                engine.alloc_u8(k_plane_bytes)?,
1641                engine.alloc_u8(v_plane_bytes)?,
1642                engine.htod_i32(&[0])?,
1643            ));
1644        }
1645        Ok(match window {
1646            Some(window) => ResidentTpKvCache::new_swa(
1647                ranks,
1648                shape.kv_dim_k,
1649                shape.kv_dim_v,
1650                shape.k_token_bytes,
1651                shape.v_token_bytes,
1652                capacity,
1653                window,
1654            ),
1655            None => ResidentTpKvCache::new(
1656                ranks,
1657                shape.kv_dim_k,
1658                shape.kv_dim_v,
1659                shape.k_token_bytes,
1660                shape.v_token_bytes,
1661                capacity,
1662            ),
1663        })
1664    }
1665
1666    pub fn grow_tp_kv_cache(
1667        &self,
1668        source: &ResidentTpKvCache,
1669        target_capacity: usize,
1670        rows: usize,
1671    ) -> Result<ResidentTpKvCache, Box<dyn std::error::Error>> {
1672        self.validate_tp_kv_cache(source)?;
1673        let plan = source.prepare_grow(target_capacity, rows)?;
1674        let ranks = self.ranks.len();
1675        let global_k = source
1676            .kv_dim_k()
1677            .checked_mul(ranks)
1678            .ok_or("TP KV grow global K dimension overflow")?;
1679        let global_v = source
1680            .kv_dim_v()
1681            .checked_mul(ranks)
1682            .ok_or("TP KV grow global V dimension overflow")?;
1683        let mut target = match source.ring_window() {
1684            Some(window) => {
1685                self.allocate_tp_swa_kv_cache(global_k, global_v, target_capacity, window)?
1686            }
1687            None => self.allocate_tp_kv_cache(global_k, global_v, target_capacity)?,
1688        };
1689        self.validate_tp_kv_cache(&target)?;
1690
1691        for (rank, engine) in self.ranks.iter().enumerate() {
1692            let _main = engine.gpu.enter_main()?;
1693            let src = source
1694                .rank(rank)
1695                .ok_or_else(|| format!("TP KV grow source has no rank {rank}"))?;
1696            let dst = target
1697                .rank_mut(rank)
1698                .ok_or_else(|| format!("TP KV grow target has no rank {rank}"))?;
1699            if plan.k_bytes() > 0 {
1700                engine.copy_u8_range_into(
1701                    dst.k_mut(),
1702                    0,
1703                    src.k(),
1704                    plan.source_row() * source.k_tok_bytes(),
1705                    plan.k_bytes(),
1706                )?;
1707            }
1708            if plan.v_bytes() > 0 {
1709                engine.copy_u8_range_into(
1710                    dst.v_mut(),
1711                    0,
1712                    src.v(),
1713                    plan.source_row() * source.v_tok_bytes(),
1714                    plan.v_bytes(),
1715                )?;
1716            }
1717        }
1718        self.set_tp_kv_len_mirrors(&mut target, plan.rows())?;
1719
1720        // The caller publishes `target` and immediately drops `source`. Drain every rank's
1721        // stream so an async-pool free cannot recycle a source plane under an in-flight D2D copy.
1722        for engine in &self.ranks {
1723            let _main = engine.gpu.enter_main()?;
1724            engine.stream().synchronize()?;
1725        }
1726        let physical_copy_rows = plan.copy_rows();
1727        target.publish_grow(plan)?;
1728        eprintln!(
1729            "[step-tp-kv-grow] rows={} source_capacity={} target_capacity={} ranks={} \
1730             physical_copy_rows={} ring_window={:?} copy=rank-local-dtod \
1731             rank_streams_synchronized=true generation_preserved=true",
1732            rows,
1733            source.capacity(),
1734            target_capacity,
1735            ranks,
1736            physical_copy_rows,
1737            source.ring_window(),
1738        );
1739        Ok(target)
1740    }
1741
1742    pub fn hydrate_tp_kv_cache(
1743        &self,
1744        cache: &mut ResidentTpKvCache,
1745        rows: usize,
1746        k_rows: &[u8],
1747        v_rows: &[u8],
1748    ) -> Result<(), Box<dyn std::error::Error>> {
1749        self.hydrate_tp_kv_cache_from(cache, rows, 0, k_rows, v_rows)
1750    }
1751
1752    pub fn hydrate_tp_kv_cache_from(
1753        &self,
1754        cache: &mut ResidentTpKvCache,
1755        logical_len: usize,
1756        resident_start: usize,
1757        k_rows: &[u8],
1758        v_rows: &[u8],
1759    ) -> Result<(), Box<dyn std::error::Error>> {
1760        self.validate_tp_kv_cache(cache)?;
1761        if cache.committed_len() != 0 || cache.staged_len() != 0 {
1762            return Err(format!(
1763                "TP KV hydration requires an empty cache, got committed/staged={}/{}",
1764                cache.committed_len(),
1765                cache.staged_len()
1766            )
1767            .into());
1768        }
1769        if resident_start > logical_len || logical_len > cache.capacity() {
1770            return Err(format!(
1771                "TP KV hydration range [{resident_start},{logical_len}) exceeds capacity {}",
1772                cache.capacity(),
1773            )
1774            .into());
1775        }
1776        let rows = logical_len - resident_start;
1777        if rows > cache.physical_capacity() {
1778            return Err(format!(
1779                "TP KV hydration rows {rows} exceed physical capacity {}",
1780                cache.physical_capacity()
1781            )
1782            .into());
1783        }
1784        for rank in 0..self.ranks.len() {
1785            let k_rank =
1786                cache_rank_rows(k_rows, rows, cache.k_tok_bytes(), self.ranks.len(), rank)?;
1787            let v_rank =
1788                cache_rank_rows(v_rows, rows, cache.v_tok_bytes(), self.ranks.len(), rank)?;
1789            let engine = &self.ranks[rank];
1790            let _main = engine.gpu.enter_main()?;
1791            let rank_cache = cache
1792                .rank_mut(rank)
1793                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1794            engine.htod_u8_into(rank_cache.k_mut(), 0, &k_rank)?;
1795            engine.htod_u8_into(rank_cache.v_mut(), 0, &v_rank)?;
1796        }
1797        cache.publish_hydration(logical_len, resident_start)?;
1798        Ok(())
1799    }
1800
1801    pub fn append_tp_kv_transaction(
1802        &self,
1803        cache: &mut ResidentTpKvCache,
1804        transaction: TpKvTransaction,
1805        k_shards: &[CudaSlice<f32>],
1806        v_shards: &[CudaSlice<f32>],
1807        rows: usize,
1808    ) -> Result<(), Box<dyn std::error::Error>> {
1809        self.append_tp_kv_transaction_inner(cache, transaction, k_shards, v_shards, rows, false)
1810    }
1811
1812    /// `external_rank_appends`: the dcw path already wrote the rank rows (device-counter
1813    /// append) — run everything EXCEPT the per-rank quantize/append loop (plan validation,
1814    /// rebase arm — unreachable when the caller peeked — and the absolute len-mirror sets,
1815    /// which land the same value the in-stream inc produced).
1816    #[allow(clippy::too_many_arguments)]
1817    pub fn append_tp_kv_transaction_inner(
1818        &self,
1819        cache: &mut ResidentTpKvCache,
1820        transaction: TpKvTransaction,
1821        k_shards: &[CudaSlice<f32>],
1822        v_shards: &[CudaSlice<f32>],
1823        rows: usize,
1824        external_rank_appends: bool,
1825    ) -> Result<(), Box<dyn std::error::Error>> {
1826        self.validate_tp_kv_cache(cache)?;
1827        let plan = cache.prepare_append(transaction, rows)?;
1828        let target = plan.target();
1829        let expected_k = rows
1830            .checked_mul(cache.kv_dim_k())
1831            .ok_or("TP KV K append size overflow")?;
1832        let expected_v = rows
1833            .checked_mul(cache.kv_dim_v())
1834            .ok_or("TP KV V append size overflow")?;
1835        // external_rank_appends passes no shards — the graph's dcw appends already wrote
1836        // the rank rows, so this call is bookkeeping-only and the shard slices are unused.
1837        if !external_rank_appends
1838            && (k_shards.len() != self.ranks.len() || v_shards.len() != self.ranks.len())
1839        {
1840            return Err(format!(
1841                "TP KV append shard counts k={} v={} != ranks {}",
1842                k_shards.len(),
1843                v_shards.len(),
1844                self.ranks.len()
1845            )
1846            .into());
1847        }
1848        let kv_dim_k = cache.kv_dim_k();
1849        let kv_dim_v = cache.kv_dim_v();
1850        let k_tok_bytes = cache.k_tok_bytes();
1851        let v_tok_bytes = cache.v_tok_bytes();
1852        if let Some(KvRingAppend::Rebase {
1853            src_row,
1854            keep_rows,
1855            new_base,
1856            ..
1857        }) = plan.ring_append()
1858        {
1859            for rank in 0..self.ranks.len() {
1860                let engine = &self.ranks[rank];
1861                let _main = engine.gpu.enter_main()?;
1862                let rank_cache = cache
1863                    .rank_mut(rank)
1864                    .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1865                if keep_rows > 0 {
1866                    let k_len = keep_rows
1867                        .checked_mul(k_tok_bytes)
1868                        .ok_or("TP KV K rebase-byte overflow")?;
1869                    let v_len = keep_rows
1870                        .checked_mul(v_tok_bytes)
1871                        .ok_or("TP KV V rebase-byte overflow")?;
1872                    let mut k_tmp = engine.alloc_u8_uninit(k_len)?;
1873                    let mut v_tmp = engine.alloc_u8_uninit(v_len)?;
1874                    engine.copy_u8_range_into(
1875                        &mut k_tmp,
1876                        0,
1877                        rank_cache.k(),
1878                        src_row * k_tok_bytes,
1879                        k_len,
1880                    )?;
1881                    engine.copy_u8_range_into(
1882                        &mut v_tmp,
1883                        0,
1884                        rank_cache.v(),
1885                        src_row * v_tok_bytes,
1886                        v_len,
1887                    )?;
1888                    engine.copy_u8_into(rank_cache.k_mut(), 0, &k_tmp, k_len)?;
1889                    engine.copy_u8_into(rank_cache.v_mut(), 0, &v_tmp, v_len)?;
1890                }
1891                // dcw base mirror (graph increment A): physical row 0 now holds logical
1892                // row `new_base`; armed device mirrors track it (rebases are rare host
1893                // events, so a host set here is the whole maintenance cost).
1894                if rank_cache.base_d().is_some() {
1895                    let value = new_base as i32;
1896                    let rank_cache = cache
1897                        .rank_mut(rank)
1898                        .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1899                    if let Some(base_d) = rank_cache.base_d_mut() {
1900                        engine.set_i32_one(base_d, value)?;
1901                    }
1902                }
1903            }
1904        }
1905        cache.publish_append_rebase(plan)?;
1906        let write_row = plan.write_row();
1907        for rank in 0..self.ranks.len() {
1908            if external_rank_appends {
1909                break;
1910            }
1911            let engine = &self.ranks[rank];
1912            let _main = engine.gpu.enter_main()?;
1913            if k_shards[rank].len() != expected_k
1914                || v_shards[rank].len() != expected_v
1915                || k_shards[rank].ordinal() != engine.ctx().ordinal()
1916                || v_shards[rank].ordinal() != engine.ctx().ordinal()
1917            {
1918                return Err(format!(
1919                    "TP KV rank {rank} shard geometry/device k={}/{} v={}/{} \
1920                     != expected {expected_k}/{expected_v} on device {}",
1921                    k_shards[rank].len(),
1922                    k_shards[rank].ordinal(),
1923                    v_shards[rank].len(),
1924                    v_shards[rank].ordinal(),
1925                    engine.ctx().ordinal(),
1926                )
1927                .into());
1928            }
1929            let rank_cache = cache
1930                .rank_mut(rank)
1931                .ok_or_else(|| format!("TP KV cache has no rank {rank}"))?;
1932            let (rank_k, rank_v) = rank_cache.planes_mut();
1933            engine.append_kv_quantized_rows(
1934                &k_shards[rank],
1935                &v_shards[rank],
1936                rank_k,
1937                rank_v,
1938                write_row,
1939                rows,
1940                kv_dim_k,
1941                kv_dim_v,
1942                k_tok_bytes,
1943                v_tok_bytes,
1944                Engine::kv_fp8_on(),
1945            )?;
1946        }
1947        if !external_rank_appends {
1948            // dcw appends advance the device counters with in-stream inc_i32; an absolute set
1949            // here would race the merged per-rank append (it reads len_d for its write row).
1950            self.set_tp_kv_len_mirrors(cache, target)?;
1951        }
1952        cache.publish_append_plan(plan)?;
1953        Ok(())
1954    }
1955
1956    pub fn commit_tp_kv_transaction(
1957        &self,
1958        cache: &mut ResidentTpKvCache,
1959        transaction: TpKvTransaction,
1960        accepted_rows: usize,
1961    ) -> Result<(), Box<dyn std::error::Error>> {
1962        self.validate_tp_kv_cache(cache)?;
1963        let target = cache.commit_target(transaction, accepted_rows)?;
1964        self.set_tp_kv_len_mirrors(cache, target)?;
1965        cache.publish_finalize(transaction, target)?;
1966        Ok(())
1967    }
1968
1969    /// Commit for the external-appends (token graph) path: host bookkeeping only, NO absolute
1970    /// len-mirror sets. The graph's in-stream inc_i32 owns the device counters; a rank-stream
1971    /// set here has no ordering edge against the NEXT token's graph launch (graph children do
1972    /// not wait on the rank streams), so it can land AFTER that graph's inc and drag the
1973    /// counter backward mid-token.
1974    pub fn commit_tp_kv_transaction_external(
1975        &self,
1976        cache: &mut ResidentTpKvCache,
1977        transaction: TpKvTransaction,
1978        accepted_rows: usize,
1979    ) -> Result<(), Box<dyn std::error::Error>> {
1980        self.validate_tp_kv_cache(cache)?;
1981        let target = cache.commit_target(transaction, accepted_rows)?;
1982        cache.publish_finalize(transaction, target)?;
1983        Ok(())
1984    }
1985
1986    pub fn rollback_tp_kv_transaction(
1987        &self,
1988        cache: &mut ResidentTpKvCache,
1989        transaction: TpKvTransaction,
1990    ) -> Result<(), Box<dyn std::error::Error>> {
1991        self.validate_tp_kv_cache(cache)?;
1992        cache.validate_transaction(transaction)?;
1993        let target = transaction.base_len();
1994        self.set_tp_kv_len_mirrors(cache, target)?;
1995        cache.publish_finalize(transaction, target)?;
1996        Ok(())
1997    }
1998
1999    pub fn tp_kv_device_lengths(
2000        &self,
2001        cache: &ResidentTpKvCache,
2002    ) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
2003        self.validate_tp_kv_cache(cache)?;
2004        let mut lengths = Vec::with_capacity(self.ranks.len());
2005        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks()) {
2006            let _main = engine.gpu.enter_main()?;
2007            lengths.push(engine.dtoh_i32_one(rank_cache.len_d())?);
2008        }
2009        Ok(lengths)
2010    }
2011
2012    fn set_tp_kv_len_mirrors(
2013        &self,
2014        cache: &mut ResidentTpKvCache,
2015        len: usize,
2016    ) -> Result<(), Box<dyn std::error::Error>> {
2017        let len = i32::try_from(len).map_err(|_| "TP KV length exceeds i32 device mirror")?;
2018        for (engine, rank_cache) in self.ranks.iter().zip(cache.ranks_mut()) {
2019            let _main = engine.gpu.enter_main()?;
2020            engine.set_i32_one(rank_cache.len_d_mut(), len)?;
2021        }
2022        Ok(())
2023    }
2024
2025    fn validate_tp_kv_cache(
2026        &self,
2027        cache: &ResidentTpKvCache,
2028    ) -> Result<(), Box<dyn std::error::Error>> {
2029        if cache.ranks_len() != self.ranks.len() {
2030            return Err(format!(
2031                "TP KV cache ranks {} != runtime ranks {}",
2032                cache.ranks_len(),
2033                self.ranks.len()
2034            )
2035            .into());
2036        }
2037        let expected_k = cache
2038            .physical_capacity()
2039            .checked_mul(cache.k_tok_bytes())
2040            .and_then(|bytes| bytes.checked_add(8))
2041            .ok_or("TP KV K plane validation overflow")?;
2042        let expected_v = cache
2043            .physical_capacity()
2044            .checked_mul(cache.v_tok_bytes())
2045            .and_then(|bytes| bytes.checked_add(8))
2046            .ok_or("TP KV V plane validation overflow")?;
2047        for (rank, (engine, rank_cache)) in self.ranks.iter().zip(cache.ranks()).enumerate() {
2048            let device = engine.ctx().ordinal();
2049            if rank_cache.k().len() != expected_k
2050                || rank_cache.v().len() != expected_v
2051                || rank_cache.len_d().len() != 1
2052                || rank_cache.k().ordinal() != device
2053                || rank_cache.v().ordinal() != device
2054                || rank_cache.len_d().ordinal() != device
2055            {
2056                return Err(format!(
2057                    "TP KV rank {rank} residency does not match device {device} or plane geometry"
2058                )
2059                .into());
2060            }
2061        }
2062        Ok(())
2063    }
2064
2065    pub fn full(
2066        &self,
2067        matrix: E4m3BlockMatrix<'_>,
2068        activations: &[f32],
2069        tokens: usize,
2070    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2071        matrix.validate()?;
2072        validate_activations(activations, tokens, matrix.in_features)?;
2073        run_rank(&self.ranks[0], matrix, activations, tokens)
2074    }
2075
2076    /// Column-parallel projection. Weight output rows and their scale rows are partitioned across
2077    /// ranks. The input is host-broadcast, rank-local projections execute independently, and the
2078    /// output is host-gathered in rank order.
2079    pub fn column_parallel(
2080        &self,
2081        matrix: E4m3BlockMatrix<'_>,
2082        activations: &[f32],
2083        tokens: usize,
2084    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2085        matrix.validate()?;
2086        validate_activations(activations, tokens, matrix.in_features)?;
2087        let tp = self.ranks.len();
2088        if matrix.out_features % tp != 0 {
2089            return Err(format!(
2090                "column-parallel out_features {} is not divisible by TP={tp}",
2091                matrix.out_features
2092            )
2093            .into());
2094        }
2095        let local_out = matrix.out_features / tp;
2096        if local_out % FP8_BLOCK != 0 {
2097            return Err(format!(
2098                "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
2099                 E4M3 scale block"
2100            )
2101            .into());
2102        }
2103
2104        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2105        let mut rank_outputs = Vec::with_capacity(tp);
2106        for (rank_index, rank) in self.ranks.iter().enumerate() {
2107            let shard = column_shard(matrix, tp, rank_index)?;
2108            let output = run_rank(rank, shard, activations, tokens)?;
2109            let row_start = rank_index * local_out;
2110            for token in 0..tokens {
2111                gathered[token * matrix.out_features + row_start
2112                    ..token * matrix.out_features + row_start + local_out]
2113                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2114            }
2115            rank_outputs.push(output);
2116        }
2117        Ok(ColumnParallelResult {
2118            gathered,
2119            rank_outputs,
2120        })
2121    }
2122
2123    pub fn upload_column_parallel(
2124        &self,
2125        matrix: E4m3BlockMatrix<'_>,
2126    ) -> Result<ResidentColumnParallel, Box<dyn std::error::Error>> {
2127        matrix.validate()?;
2128        let tp = self.ranks.len();
2129        validate_column_shape(matrix, tp)?;
2130        let mut ranks = Vec::with_capacity(tp);
2131        for (rank_index, engine) in self.ranks.iter().enumerate() {
2132            ranks.push(upload_rank(engine, column_shard(matrix, tp, rank_index)?)?);
2133        }
2134        Ok(ResidentColumnParallel {
2135            ranks,
2136            out_features: matrix.out_features,
2137            in_features: matrix.in_features,
2138        })
2139    }
2140
2141    pub fn column_parallel_resident(
2142        &self,
2143        matrix: &ResidentColumnParallel,
2144        activations: &[f32],
2145        tokens: usize,
2146    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2147        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2148        validate_activations(activations, tokens, matrix.in_features)?;
2149        let local_out = matrix.out_features / self.ranks.len();
2150        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2151        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2152        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2153            let output = run_resident_rank(engine, shard, activations, tokens)?;
2154            let row_start = rank_index * local_out;
2155            for token in 0..tokens {
2156                gathered[token * matrix.out_features + row_start
2157                    ..token * matrix.out_features + row_start + local_out]
2158                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
2159            }
2160            rank_outputs.push(output);
2161        }
2162        Ok(ColumnParallelResult {
2163            gathered,
2164            rank_outputs,
2165        })
2166    }
2167
2168    /// Row-parallel projection. Weight/input columns and their scale columns are partitioned
2169    /// across ranks. Rank-local partials return through host memory and are reduced in stable
2170    /// rank order.
2171    pub fn row_parallel(
2172        &self,
2173        matrix: E4m3BlockMatrix<'_>,
2174        activations: &[f32],
2175        tokens: usize,
2176    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2177        matrix.validate()?;
2178        validate_activations(activations, tokens, matrix.in_features)?;
2179        let tp = self.ranks.len();
2180        if matrix.in_features % tp != 0 {
2181            return Err(format!(
2182                "row-parallel in_features {} is not divisible by TP={tp}",
2183                matrix.in_features
2184            )
2185            .into());
2186        }
2187        let local_in = matrix.in_features / tp;
2188        if local_in % FP8_BLOCK != 0 {
2189            return Err(format!(
2190                "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
2191                 E4M3 scale block"
2192            )
2193            .into());
2194        }
2195
2196        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2197        let mut rank_partials = Vec::with_capacity(tp);
2198        for (rank_index, rank) in self.ranks.iter().enumerate() {
2199            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2200            let local_activations =
2201                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2202            let shard = E4m3BlockMatrix {
2203                codes: &codes,
2204                scales: &scales,
2205                out_features: matrix.out_features,
2206                in_features: local_in,
2207            };
2208            let partial = run_rank(rank, shard, &local_activations, tokens)?;
2209            for (sum, value) in reduced.iter_mut().zip(&partial) {
2210                *sum += *value;
2211            }
2212            rank_partials.push(partial);
2213        }
2214        Ok(RowParallelResult {
2215            reduced,
2216            rank_partials,
2217        })
2218    }
2219
2220    pub fn upload_row_parallel(
2221        &self,
2222        matrix: E4m3BlockMatrix<'_>,
2223    ) -> Result<ResidentRowParallel, Box<dyn std::error::Error>> {
2224        matrix.validate()?;
2225        let tp = self.ranks.len();
2226        validate_row_shape(matrix, tp)?;
2227        let local_in = matrix.in_features / tp;
2228        let mut ranks = Vec::with_capacity(tp);
2229        for (rank_index, engine) in self.ranks.iter().enumerate() {
2230            let (codes, scales) = row_shard(matrix, tp, rank_index)?;
2231            ranks.push(upload_rank(
2232                engine,
2233                E4m3BlockMatrix {
2234                    codes: &codes,
2235                    scales: &scales,
2236                    out_features: matrix.out_features,
2237                    in_features: local_in,
2238                },
2239            )?);
2240        }
2241        Ok(ResidentRowParallel {
2242            ranks,
2243            out_features: matrix.out_features,
2244            in_features: matrix.in_features,
2245        })
2246    }
2247
2248    pub fn row_parallel_resident(
2249        &self,
2250        matrix: &ResidentRowParallel,
2251        activations: &[f32],
2252        tokens: usize,
2253    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
2254        validate_resident_ranks(&self.ranks, &matrix.ranks)?;
2255        validate_activations(activations, tokens, matrix.in_features)?;
2256        let tp = self.ranks.len();
2257        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
2258        let mut rank_partials = Vec::with_capacity(tp);
2259        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2260            let local_activations =
2261                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
2262            let partial = run_resident_rank(engine, shard, &local_activations, tokens)?;
2263            for (sum, value) in reduced.iter_mut().zip(&partial) {
2264                *sum += *value;
2265            }
2266            rank_partials.push(partial);
2267        }
2268        Ok(RowParallelResult {
2269            reduced,
2270            rank_partials,
2271        })
2272    }
2273
2274    pub fn upload_bf16_column_parallel(
2275        &self,
2276        matrix: Bf16Matrix<'_>,
2277    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2278        self.upload_bf16_column_parallel_inner(matrix, None, false)
2279    }
2280
2281    /// Step-3.7 column projection with one numerical program across TP1/TP2/TP4/TP8.
2282    pub fn upload_step_bf16_column_parallel(
2283        &self,
2284        matrix: Bf16Matrix<'_>,
2285    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2286        self.upload_step_bf16_column_parallel_inner(matrix, false)
2287    }
2288
2289    /// Load-time exact F32 expansion of a Step BF16 shard.
2290    ///
2291    /// The original BF16 allocation is released after the stream-ordered conversion. Decode then
2292    /// reuses the resident F32 values with the same topology-invariant output-row chunks.
2293    pub fn upload_step_bf16_column_parallel_f32_mirror(
2294        &self,
2295        matrix: Bf16Matrix<'_>,
2296    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2297        self.upload_step_bf16_column_parallel_inner(matrix, true)
2298    }
2299
2300    fn upload_step_bf16_column_parallel_inner(
2301        &self,
2302        matrix: Bf16Matrix<'_>,
2303        f32_mirror: bool,
2304    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2305        let canonical_chunk_rows =
2306            step_bf16_canonical_chunk_rows(matrix.out_features, self.ranks.len())?;
2307        self.upload_bf16_column_parallel_inner(matrix, Some(canonical_chunk_rows), f32_mirror)
2308    }
2309
2310    fn upload_bf16_column_parallel_inner(
2311        &self,
2312        matrix: Bf16Matrix<'_>,
2313        canonical_chunk_rows: Option<usize>,
2314        f32_mirror: bool,
2315    ) -> Result<ResidentBf16ColumnParallel, Box<dyn std::error::Error>> {
2316        matrix.validate()?;
2317        let tp = self.ranks.len();
2318        if matrix.out_features % tp != 0 {
2319            return Err(format!(
2320                "BF16 column-parallel out_features {} is not divisible by TP={tp}",
2321                matrix.out_features
2322            )
2323            .into());
2324        }
2325        let mut ranks = Vec::with_capacity(tp);
2326        for (rank, engine) in self.ranks.iter().enumerate() {
2327            ranks.push(upload_bf16_rank(
2328                engine,
2329                bf16_column_shard(matrix, tp, rank)?,
2330                f32_mirror,
2331            )?);
2332        }
2333        Ok(ResidentBf16ColumnParallel {
2334            ranks,
2335            out_features: matrix.out_features,
2336            in_features: matrix.in_features,
2337            canonical_chunk_rows,
2338        })
2339    }
2340
2341    pub fn bf16_column_parallel_resident(
2342        &self,
2343        matrix: &ResidentBf16ColumnParallel,
2344        activations: &[f32],
2345        tokens: usize,
2346    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
2347        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2348        validate_activations(activations, tokens, matrix.in_features)?;
2349        let local_out = matrix.out_features / self.ranks.len();
2350        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
2351        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2352        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
2353            let output = run_resident_bf16_rank(
2354                engine,
2355                shard,
2356                activations,
2357                tokens,
2358                matrix.canonical_chunk_rows,
2359            )?;
2360            for token in 0..tokens {
2361                let src = &output[token * local_out..(token + 1) * local_out];
2362                let dst_start = token * matrix.out_features + rank * local_out;
2363                gathered[dst_start..dst_start + local_out].copy_from_slice(src);
2364            }
2365            rank_outputs.push(output);
2366        }
2367        Ok(ColumnParallelResult {
2368            gathered,
2369            rank_outputs,
2370        })
2371    }
2372
2373    /// Native-P2P twin of [`Self::bf16_column_parallel_resident`].
2374    ///
2375    /// The host-canonical activation is uploaded once on rank zero and peer-broadcast to the
2376    /// remaining ranks. Rank-local outputs are peer-gathered in token-major order before one root
2377    /// readback. This removes per-rank host staging but deliberately still returns a host oracle;
2378    /// attention and KV ownership are separate milestones.
2379    pub fn bf16_column_parallel_resident_native(
2380        &self,
2381        matrix: &ResidentBf16ColumnParallel,
2382        activations: &[f32],
2383        tokens: usize,
2384    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
2385        let rank_outputs =
2386            self.bf16_column_parallel_resident_device_shards(matrix, activations, tokens)?;
2387        let local_out = matrix.out_features / self.ranks.len();
2388        self.gather_native_column_shards(&rank_outputs, tokens, local_out)
2389    }
2390
2391    /// Does the serving engine live in the SAME CUDA context as this runtime's root rank?
2392    /// The device-resident input/output seams below hand raw device buffers across the
2393    /// Engine boundary, which is only addressable when both sides share the root device's
2394    /// primary context — the seam `step35_tp_qkv` keys its residency dispatch on.
2395    pub fn root_shares_ctx(&self, e: &Engine) -> bool {
2396        self.ranks
2397            .first()
2398            .is_some_and(|root| root.ctx().cu_ctx() == e.ctx().cu_ctx())
2399    }
2400
2401    /// Device-input twin of [`Self::bf16_column_parallel_resident_native`] (lane/
2402    /// hermes-perf-fixes, 2026-08-23 — the step QKV TP host-bounce finding). The activation
2403    /// arrives as a ROOT-DEVICE buffer (first `tokens * in_features` values) instead of a
2404    /// host slice, and the gathered output stays root-resident: no DtoH of the hidden state,
2405    /// no host q/k/v staging, no re-upload. BYTE-IDENTICAL to the host-canonical native arm
2406    /// by construction — the root input bytes are dtod-copied where the host arm htod'd the
2407    /// same bytes, and every kernel, peer copy, and gather order is shared.
2408    ///
2409    /// FENCES: caller must have synchronized the producer stream that wrote
2410    /// `root_activation` (the serving engine's — a DIFFERENT stream in the same context);
2411    /// this method synchronizes the root stream before returning so the caller's stream can
2412    /// consume the gathered output immediately.
2413    pub fn bf16_column_parallel_resident_native_device(
2414        &self,
2415        matrix: &ResidentBf16ColumnParallel,
2416        root_activation: &CudaSlice<f32>,
2417        tokens: usize,
2418    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2419        let rank_outputs = self.bf16_column_parallel_resident_device_shards_from_root(
2420            matrix,
2421            root_activation,
2422            tokens,
2423        )?;
2424        let local_out = matrix.out_features / self.ranks.len();
2425        let gathered = self.gather_native_column_shards_device(&rank_outputs, tokens, local_out)?;
2426        let root = &self.ranks[0];
2427        let _main = root.gpu.enter_main()?;
2428        root.stream().synchronize()?;
2429        Ok(gathered)
2430    }
2431
2432    /// Root-device-input twin of [`Self::bf16_column_parallel_resident_device_shards`]:
2433    /// the canonical activation is already resident on the root device (len >=
2434    /// `tokens * in_features`; extra tail values beyond the active prefix are ignored,
2435    /// the reused-prime-slab contract of `active_matrix_values`).
2436    pub fn bf16_column_parallel_resident_device_shards_from_root(
2437        &self,
2438        matrix: &ResidentBf16ColumnParallel,
2439        root_activation: &CudaSlice<f32>,
2440        tokens: usize,
2441    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2442        if self.ranks.len() > 1 && !self.native_p2p {
2443            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2444        }
2445        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2446        let values = tokens
2447            .checked_mul(matrix.in_features)
2448            .ok_or("device BF16 column activation size overflow")?;
2449        let root = &self.ranks[0];
2450        if tokens == 0
2451            || root_activation.len() < values
2452            || root_activation.ordinal() != root.ctx().ordinal()
2453        {
2454            return Err("device BF16 column root activation geometry mismatch".into());
2455        }
2456
2457        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2458        let root_input = {
2459            let _main = root.gpu.enter_main()?;
2460            let mut root_input = root.uninit(values)?;
2461            root.stream()
2462                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
2463            root_input
2464        };
2465        // PRODUCER FENCE (same discipline as the host-input twin): the peer broadcast
2466        // below reads this buffer from the OTHER ranks' streams while the root dtod may
2467        // still be in flight.
2468        {
2469            let _main = root.gpu.enter_main()?;
2470            root.stream().synchronize()?;
2471        }
2472        rank_inputs.push(root_input);
2473        for engine in &self.ranks[1..] {
2474            let peer_input = {
2475                let _main = engine.gpu.enter_main()?;
2476                let mut peer_input = engine.uninit(values)?;
2477                engine
2478                    .stream()
2479                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2480                peer_input
2481            };
2482            rank_inputs.push(peer_input);
2483        }
2484
2485        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2486        for rank in 0..self.ranks.len() {
2487            rank_outputs.push(run_resident_bf16_rank_device(
2488                &self.ranks[rank],
2489                &matrix.ranks[rank],
2490                &rank_inputs[rank],
2491                tokens,
2492                matrix.canonical_chunk_rows,
2493                self.bulk_p2p,
2494            )?);
2495        }
2496        Ok(rank_outputs)
2497    }
2498
2499    /// Keep Step BF16 column outputs resident on their owning TP ranks.
2500    ///
2501    /// Rank zero receives the host-canonical activation once and peer-broadcasts it when TP>1.
2502    /// Unlike [`Self::bf16_column_parallel_resident_native`], this method performs no output
2503    /// gather or readback. It is the correctness substrate for rank-local norm, RoPE, attention,
2504    /// and cache ownership; callers must not treat its existence as serving qualification.
2505    pub fn bf16_column_parallel_resident_device_shards(
2506        &self,
2507        matrix: &ResidentBf16ColumnParallel,
2508        activations: &[f32],
2509        tokens: usize,
2510    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2511        if self.ranks.len() > 1 && !self.native_p2p {
2512            return Err("device-resident BF16 column parallelism requires native P2P ranks".into());
2513        }
2514        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2515        validate_activations(activations, tokens, matrix.in_features)?;
2516
2517        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
2518        let root_input = {
2519            let root = &self.ranks[0];
2520            let _main = root.gpu.enter_main()?;
2521            root.htod(activations)?
2522        };
2523        // PRODUCER FENCE (2026-08-20 flake fix): the peer broadcast below reads this buffer from
2524        // the OTHER ranks' streams, and clone_htod is asynchronous on the root stream. Without
2525        // this fence a peer copy can overtake the in-flight H2D and replicate stale bytes — the
2526        // measured ~30%-of-boots prefill/decode argmax flake. Same discipline as
2527        // `upload_replicated_device_rows`.
2528        {
2529            let root = &self.ranks[0];
2530            let _main = root.gpu.enter_main()?;
2531            root.stream().synchronize()?;
2532        }
2533        rank_inputs.push(root_input);
2534        for engine in &self.ranks[1..] {
2535            let peer_input = {
2536                let _main = engine.gpu.enter_main()?;
2537                let mut peer_input = engine.uninit(activations.len())?;
2538                engine
2539                    .stream()
2540                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
2541                peer_input
2542            };
2543            rank_inputs.push(peer_input);
2544        }
2545
2546        let mut rank_outputs = Vec::with_capacity(self.ranks.len());
2547        for rank in 0..self.ranks.len() {
2548            rank_outputs.push(run_resident_bf16_rank_device(
2549                &self.ranks[rank],
2550                &matrix.ranks[rank],
2551                &rank_inputs[rank],
2552                tokens,
2553                matrix.canonical_chunk_rows,
2554                self.bulk_p2p,
2555            )?);
2556        }
2557        Ok(rank_outputs)
2558    }
2559
2560    /// Allocate one fixed-shape replicated batch without initializing its contents.
2561    ///
2562    /// Callers must refresh every rank before passing the batch to an operator.
2563    pub fn allocate_replicated_device_rows(
2564        &self,
2565        tokens: usize,
2566        width: usize,
2567    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2568        if self.ranks.len() > 1 && !self.native_p2p {
2569            return Err("replicated device rows require native P2P ranks".into());
2570        }
2571        let values = tokens
2572            .checked_mul(width)
2573            .ok_or("replicated device row size overflow")?;
2574        let rank_lengths = vec![values; self.ranks.len()];
2575        replicated_device_row_values(tokens, width, self.ranks.len(), &rank_lengths)?;
2576        let mut ranks = Vec::with_capacity(self.ranks.len());
2577        for engine in &self.ranks {
2578            let _main = engine.gpu.enter_main()?;
2579            ranks.push(engine.uninit(values)?);
2580        }
2581        Ok(ResidentReplicatedDeviceRows {
2582            ranks,
2583            tokens,
2584            width,
2585        })
2586    }
2587
2588    /// Replace a fixed-shape replicated batch from a root-device source.
2589    pub fn refresh_replicated_device_rows_from_root(
2590        &self,
2591        rows: &mut ResidentReplicatedDeviceRows,
2592        source: &CudaSlice<f32>,
2593    ) -> Result<(), Box<dyn std::error::Error>> {
2594        if self.ranks.len() > 1 && !self.native_p2p {
2595            return Err("replicated device rows require native P2P ranks".into());
2596        }
2597        validate_replicated_device_rows(&self.ranks, rows)?;
2598        let root = self
2599            .ranks
2600            .first()
2601            .ok_or("replicated rows have no root rank")?;
2602        let values = replicated_device_row_source_values(
2603            rows.tokens,
2604            rows.width,
2605            source.len(),
2606            source.ordinal(),
2607            root.ctx().ordinal(),
2608        )?;
2609        let (root_rows, peer_rows) = rows
2610            .ranks
2611            .split_first_mut()
2612            .ok_or("replicated rows have no root allocation")?;
2613        {
2614            let _main = root.gpu.enter_main()?;
2615            let mut destination = root_rows.slice_mut(0..values);
2616            root.stream()
2617                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
2618            root.stream().synchronize()?;
2619        }
2620        for (engine, peer_rows) in self.ranks.iter().skip(1).zip(peer_rows) {
2621            let _main = engine.gpu.enter_main()?;
2622            let mut destination = peer_rows.slice_mut(0..values);
2623            engine
2624                .stream()
2625                .memcpy_dtod(&root_rows.slice(0..values), &mut destination)?;
2626        }
2627        Ok(())
2628    }
2629
2630    /// Upload one canonical batch on rank zero and replicate it over native P2P.
2631    pub fn upload_replicated_device_rows(
2632        &self,
2633        rows: &[f32],
2634        tokens: usize,
2635        width: usize,
2636    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2637        if self.ranks.len() > 1 && !self.native_p2p {
2638            return Err("replicated device rows require native P2P ranks".into());
2639        }
2640        validate_activations(rows, tokens, width)?;
2641        let root = self
2642            .ranks
2643            .first()
2644            .ok_or("replicated rows have no root rank")?;
2645        let root_rows = {
2646            let _main = root.gpu.enter_main()?;
2647            root.htod(rows)?
2648        };
2649        {
2650            let _main = root.gpu.enter_main()?;
2651            root.stream().synchronize()?;
2652        }
2653        let mut ranks = Vec::with_capacity(self.ranks.len());
2654        ranks.push(root_rows);
2655        for engine in self.ranks.iter().skip(1) {
2656            let _main = engine.gpu.enter_main()?;
2657            let mut peer_rows = engine.uninit(rows.len())?;
2658            engine.stream().memcpy_dtod(&ranks[0], &mut peer_rows)?;
2659            ranks.push(peer_rows);
2660        }
2661        Ok(ResidentReplicatedDeviceRows {
2662            ranks,
2663            tokens,
2664            width,
2665        })
2666    }
2667
2668    /// Execute a column-parallel BF16 matrix directly from rank-local replicated inputs.
2669    pub fn bf16_column_parallel_resident_replicated_device_shards(
2670        &self,
2671        matrix: &ResidentBf16ColumnParallel,
2672        activations: &ResidentReplicatedDeviceRows,
2673    ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
2674        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
2675        validate_replicated_device_rows(&self.ranks, activations)?;
2676        if activations.width != matrix.in_features {
2677            return Err(format!(
2678                "replicated BF16 column input width {} != matrix width {}",
2679                activations.width, matrix.in_features
2680            )
2681            .into());
2682        }
2683        let mut outputs = Vec::with_capacity(self.ranks.len());
2684        for rank in 0..self.ranks.len() {
2685            outputs.push(run_resident_bf16_rank_device(
2686                &self.ranks[rank],
2687                &matrix.ranks[rank],
2688                &activations.ranks[rank],
2689                activations.tokens,
2690                matrix.canonical_chunk_rows,
2691                self.bulk_p2p,
2692            )?);
2693        }
2694        Ok(outputs)
2695    }
2696
2697    /// Upload a BF16 router once on rank zero and retain its exact F32 expansion.
2698    #[allow(clippy::too_many_arguments)]
2699    pub fn upload_sigmoid_topk_router(
2700        &self,
2701        weight: Bf16Matrix<'_>,
2702        correction_bias: &[f32],
2703        active: Option<&[bool]>,
2704        experts_per_token: usize,
2705        scaling_factor: f32,
2706        route_norm: bool,
2707    ) -> Result<ResidentSigmoidTopKRouter, Box<dyn std::error::Error>> {
2708        weight.validate()?;
2709        if correction_bias.len() != weight.out_features
2710            || experts_per_token == 0
2711            || experts_per_token > weight.out_features
2712            || !correction_bias.iter().all(|value| value.is_finite())
2713            || !scaling_factor.is_finite()
2714            || scaling_factor <= 0.0
2715        {
2716            return Err(format!(
2717                "sigmoid router geometry weight={}x{} bias={} top_k={} scale={scaling_factor}",
2718                weight.out_features,
2719                weight.in_features,
2720                correction_bias.len(),
2721                experts_per_token,
2722            )
2723            .into());
2724        }
2725        let active_row = active
2726            .map(|mask| {
2727                if mask.len() != weight.out_features {
2728                    return Err(format!(
2729                        "sigmoid router active mask {} != experts {}",
2730                        mask.len(),
2731                        weight.out_features
2732                    ));
2733                }
2734                Ok(mask
2735                    .iter()
2736                    .map(|&enabled| u8::from(enabled))
2737                    .collect::<Vec<_>>())
2738            })
2739            .transpose()?
2740            .unwrap_or_else(|| vec![1; weight.out_features]);
2741        let active_count = active_row.iter().filter(|&&enabled| enabled != 0).count();
2742        crate::sigrouter_contract::validate_active_count(experts_per_token, active_count)?;
2743
2744        let root = self
2745            .ranks
2746            .first()
2747            .ok_or("sigmoid router runtime has no root rank")?;
2748        let _main = root.gpu.enter_main()?;
2749        let bf16 = root.htod_bytes(weight.bytes)?;
2750        let weight_f32 = root.bf16_to_f32(
2751            &bf16.slice(0..bf16.len()),
2752            weight.out_features * weight.in_features,
2753        )?;
2754        Ok(ResidentSigmoidTopKRouter {
2755            weight: weight_f32,
2756            correction_bias: root.htod(correction_bias)?,
2757            active: root.htod_bytes(&active_row)?,
2758            root_device: root.ctx().ordinal(),
2759            input_width: weight.in_features,
2760            expert_count: weight.out_features,
2761            experts_per_token,
2762            active_count,
2763            scaling_factor,
2764            route_norm,
2765        })
2766    }
2767
2768    /// Route rank-zero replicated rows and return the narrow host control result plus logits.
2769    ///
2770    /// The logits readback exists for independent oracle comparison. This method is a correctness
2771    /// surface; a serving scheduler may retain logits and selected routes on device.
2772    pub fn sigmoid_topk_replicated_device_rows_host(
2773        &self,
2774        router: &ResidentSigmoidTopKRouter,
2775        input: &ResidentReplicatedDeviceRows,
2776    ) -> Result<SigmoidTopKHostOutput, Box<dyn std::error::Error>> {
2777        validate_replicated_device_rows(&self.ranks, input)?;
2778        if input.width != router.input_width {
2779            return Err(format!(
2780                "sigmoid router input width {} != resident width {}",
2781                input.width, router.input_width
2782            )
2783            .into());
2784        }
2785        let root = self
2786            .ranks
2787            .first()
2788            .ok_or("sigmoid router runtime has no root rank")?;
2789        let _main = root.gpu.enter_main()?;
2790        if root.ctx().ordinal() != router.root_device
2791            || router.weight.ordinal() != router.root_device
2792            || router.correction_bias.ordinal() != router.root_device
2793            || router.active.ordinal() != router.root_device
2794        {
2795            return Err("sigmoid router root residency changed".into());
2796        }
2797        let logits = root.router_gemv(
2798            &router.weight,
2799            &input.ranks[0],
2800            router.input_width,
2801            router.expert_count,
2802            input.tokens,
2803        )?;
2804        let (selected, weights) = root.moe_router_sigmoid_topk_host(
2805            &logits,
2806            input.tokens,
2807            router.expert_count,
2808            router.experts_per_token,
2809            router.active_count,
2810            &router.correction_bias,
2811            &router.active,
2812            router.scaling_factor,
2813            router.route_norm,
2814        )?;
2815        Ok(SigmoidTopKHostOutput {
2816            logits: root.dtoh(&logits)?,
2817            selected,
2818            weights,
2819        })
2820    }
2821
2822    /// Replicate a full BF16 SwiGLU bank on every rank.
2823    pub fn upload_replicated_bf16_swiglu(
2824        &self,
2825        gate: Bf16Matrix<'_>,
2826        up: Bf16Matrix<'_>,
2827        down: Bf16Matrix<'_>,
2828    ) -> Result<ResidentReplicatedBf16SwiGlu, Box<dyn std::error::Error>> {
2829        gate.validate()?;
2830        up.validate()?;
2831        down.validate()?;
2832        if gate.in_features != up.in_features
2833            || gate.out_features != up.out_features
2834            || down.in_features != gate.out_features
2835            || down.out_features != gate.in_features
2836        {
2837            return Err(format!(
2838                "replicated BF16 SwiGLU geometry gate={}x{} up={}x{} down={}x{}",
2839                gate.out_features,
2840                gate.in_features,
2841                up.out_features,
2842                up.in_features,
2843                down.out_features,
2844                down.in_features,
2845            )
2846            .into());
2847        }
2848        let mut gate_ranks = Vec::with_capacity(self.ranks.len());
2849        let mut up_ranks = Vec::with_capacity(self.ranks.len());
2850        let mut down_ranks = Vec::with_capacity(self.ranks.len());
2851        for engine in &self.ranks {
2852            gate_ranks.push(upload_bf16_rank(engine, gate, false)?);
2853            up_ranks.push(upload_bf16_rank(engine, up, false)?);
2854            down_ranks.push(upload_bf16_rank(engine, down, false)?);
2855        }
2856        Ok(ResidentReplicatedBf16SwiGlu {
2857            gate: gate_ranks,
2858            up: up_ranks,
2859            down: down_ranks,
2860            input_width: gate.in_features,
2861            intermediate_width: gate.out_features,
2862        })
2863    }
2864
2865    /// Execute a fully replicated BF16 SwiGLU directly from replicated device rows.
2866    pub fn replicated_bf16_swiglu_resident_device(
2867        &self,
2868        mlp: &ResidentReplicatedBf16SwiGlu,
2869        input: &ResidentReplicatedDeviceRows,
2870        activation_limit: Option<f32>,
2871    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2872        validate_step_expert_activation_limit(activation_limit)?;
2873        validate_replicated_device_rows(&self.ranks, input)?;
2874        validate_resident_bf16_ranks(&self.ranks, &mlp.gate)?;
2875        validate_resident_bf16_ranks(&self.ranks, &mlp.up)?;
2876        validate_resident_bf16_ranks(&self.ranks, &mlp.down)?;
2877        if input.width != mlp.input_width
2878            || mlp.gate.len() != self.ranks.len()
2879            || mlp.up.len() != self.ranks.len()
2880            || mlp.down.len() != self.ranks.len()
2881        {
2882            return Err("replicated BF16 SwiGLU residency or input width changed".into());
2883        }
2884
2885        let mut outputs = Vec::with_capacity(self.ranks.len());
2886        for rank in 0..self.ranks.len() {
2887            let engine = &self.ranks[rank];
2888            let gate = run_resident_bf16_rank_device(
2889                engine,
2890                &mlp.gate[rank],
2891                &input.ranks[rank],
2892                input.tokens,
2893                None,
2894                self.bulk_p2p,
2895            )?;
2896            let up = run_resident_bf16_rank_device(
2897                engine,
2898                &mlp.up[rank],
2899                &input.ranks[rank],
2900                input.tokens,
2901                None,
2902                self.bulk_p2p,
2903            )?;
2904            let _main = engine.gpu.enter_main()?;
2905            let values = input
2906                .tokens
2907                .checked_mul(mlp.intermediate_width)
2908                .ok_or("replicated BF16 SwiGLU activation size overflow")?;
2909            let mut activation = engine.uninit(values)?;
2910            if let Some(limit) = activation_limit {
2911                engine.silu_clamped_mul_host_expf(&gate, &up, limit, &mut activation, values)?;
2912            } else {
2913                engine.silu_mul_host_expf(&gate, &up, &mut activation, values)?;
2914            }
2915            outputs.push(run_resident_bf16_rank_device(
2916                engine,
2917                &mlp.down[rank],
2918                &activation,
2919                input.tokens,
2920                None,
2921                self.bulk_p2p,
2922            )?);
2923        }
2924        Ok(ResidentReplicatedDeviceRows {
2925            ranks: outputs,
2926            tokens: input.tokens,
2927            width: mlp.input_width,
2928        })
2929    }
2930
2931    /// Apply the same RMS-norm row program independently on every replicated rank.
2932    pub fn rms_norm_replicated_device_rows(
2933        &self,
2934        input: &ResidentReplicatedDeviceRows,
2935        weight: &[f32],
2936        eps: f32,
2937    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
2938        validate_replicated_device_rows(&self.ranks, input)?;
2939        if weight.len() != input.width || !eps.is_finite() || eps <= 0.0 {
2940            return Err(format!(
2941                "replicated RMS norm weight/eps {}/{} != width {}",
2942                weight.len(),
2943                eps,
2944                input.width
2945            )
2946            .into());
2947        }
2948        let mut ranks = Vec::with_capacity(self.ranks.len());
2949        for (rank, engine) in self.ranks.iter().enumerate() {
2950            let _main = engine.gpu.enter_main()?;
2951            let weight = engine.htod(weight)?;
2952            let mut output = engine.uninit(input.tokens * input.width)?;
2953            engine.rms_norm(
2954                &input.ranks[rank],
2955                &weight,
2956                &mut output,
2957                input.width,
2958                input.tokens,
2959                eps,
2960            )?;
2961            ranks.push(output);
2962        }
2963        Ok(ResidentReplicatedDeviceRows {
2964            ranks,
2965            tokens: input.tokens,
2966            width: input.width,
2967        })
2968    }
2969
2970    /// Add two replicated batches and RMS-normalize the exact residual on every rank.
2971    pub fn add_rms_norm_replicated_device_rows(
2972        &self,
2973        input: &ResidentReplicatedDeviceRows,
2974        update: &ResidentReplicatedDeviceRows,
2975        weight: &[f32],
2976        eps: f32,
2977    ) -> Result<
2978        (ResidentReplicatedDeviceRows, ResidentReplicatedDeviceRows),
2979        Box<dyn std::error::Error>,
2980    > {
2981        validate_replicated_device_rows(&self.ranks, input)?;
2982        validate_replicated_device_rows(&self.ranks, update)?;
2983        if input.tokens != update.tokens
2984            || input.width != update.width
2985            || weight.len() != input.width
2986            || !eps.is_finite()
2987            || eps <= 0.0
2988        {
2989            return Err(format!(
2990                "replicated add/RMS geometry input={}x{} update={}x{} weight={} eps={eps}",
2991                input.tokens,
2992                input.width,
2993                update.tokens,
2994                update.width,
2995                weight.len(),
2996            )
2997            .into());
2998        }
2999        let values = input.tokens * input.width;
3000        let mut residual_ranks = Vec::with_capacity(self.ranks.len());
3001        let mut normalized_ranks = Vec::with_capacity(self.ranks.len());
3002        for (rank, engine) in self.ranks.iter().enumerate() {
3003            let _main = engine.gpu.enter_main()?;
3004            let weight = engine.htod(weight)?;
3005            let mut residual = engine.uninit(values)?;
3006            let mut normalized = engine.uninit(values)?;
3007            engine.add_rms_norm(
3008                &input.ranks[rank],
3009                &update.ranks[rank],
3010                &weight,
3011                &mut residual,
3012                &mut normalized,
3013                input.width,
3014                input.tokens,
3015                eps,
3016            )?;
3017            residual_ranks.push(residual);
3018            normalized_ranks.push(normalized);
3019        }
3020        Ok((
3021            ResidentReplicatedDeviceRows {
3022                ranks: residual_ranks,
3023                tokens: input.tokens,
3024                width: input.width,
3025            },
3026            ResidentReplicatedDeviceRows {
3027                ranks: normalized_ranks,
3028                tokens: input.tokens,
3029                width: input.width,
3030            },
3031        ))
3032    }
3033
3034    pub fn collect_replicated_device_rows(
3035        &self,
3036        rows: &ResidentReplicatedDeviceRows,
3037    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
3038        validate_replicated_device_rows(&self.ranks, rows)?;
3039        let mut outputs = Vec::with_capacity(self.ranks.len());
3040        for (rank, engine) in self.ranks.iter().enumerate() {
3041            let _main = engine.gpu.enter_main()?;
3042            outputs.push(engine.dtoh(&rows.ranks[rank])?);
3043        }
3044        Ok(outputs)
3045    }
3046
3047    pub fn upload_bf16_row_parallel(
3048        &self,
3049        matrix: Bf16Matrix<'_>,
3050    ) -> Result<ResidentBf16RowParallel, Box<dyn std::error::Error>> {
3051        matrix.validate()?;
3052        let tp = self.ranks.len();
3053        if matrix.in_features % tp != 0 {
3054            return Err(format!(
3055                "BF16 row-parallel in_features {} is not divisible by TP={tp}",
3056                matrix.in_features
3057            )
3058            .into());
3059        }
3060        let mut ranks = Vec::with_capacity(tp);
3061        for (rank, engine) in self.ranks.iter().enumerate() {
3062            let shard = bf16_row_shard(matrix, tp, rank)?;
3063            ranks.push(upload_bf16_rank(
3064                engine,
3065                Bf16Matrix {
3066                    bytes: &shard,
3067                    out_features: matrix.out_features,
3068                    in_features: matrix.in_features / tp,
3069                },
3070                false,
3071            )?);
3072        }
3073        Ok(ResidentBf16RowParallel {
3074            ranks,
3075            out_features: matrix.out_features,
3076            in_features: matrix.in_features,
3077        })
3078    }
3079
3080    pub fn bf16_row_parallel_resident(
3081        &self,
3082        matrix: &ResidentBf16RowParallel,
3083        activations: &[f32],
3084        tokens: usize,
3085    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
3086        validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
3087        validate_activations(activations, tokens, matrix.in_features)?;
3088        let tp = self.ranks.len();
3089        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
3090        let mut rank_partials = Vec::with_capacity(tp);
3091        for (rank, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
3092            let local_activations =
3093                activation_shard(activations, tokens, matrix.in_features, tp, rank);
3094            let partial = run_resident_bf16_rank(engine, shard, &local_activations, tokens, None)?;
3095            for (sum, value) in reduced.iter_mut().zip(&partial) {
3096                *sum += value;
3097            }
3098            rank_partials.push(partial);
3099        }
3100        Ok(RowParallelResult {
3101            reduced,
3102            rank_partials,
3103        })
3104    }
3105
3106    /// Step-3.7 row projection split into the same eight global K blocks for TP1/TP2/TP4/TP8.
3107    pub fn upload_step_bf16_row_parallel(
3108        &self,
3109        matrix: Bf16Matrix<'_>,
3110    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3111        self.upload_step_bf16_row_parallel_inner(matrix, false)
3112    }
3113
3114    pub fn upload_step_bf16_row_parallel_f32_mirror(
3115        &self,
3116        matrix: Bf16Matrix<'_>,
3117    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3118        self.upload_step_bf16_row_parallel_inner(matrix, true)
3119    }
3120
3121    fn upload_step_bf16_row_parallel_inner(
3122        &self,
3123        matrix: Bf16Matrix<'_>,
3124        f32_mirror: bool,
3125    ) -> Result<ResidentStepBf16RowParallel, Box<dyn std::error::Error>> {
3126        matrix.validate()?;
3127        let tp = self.ranks.len();
3128        let canonical_chunk_cols = step_bf16_canonical_chunk_cols(matrix.in_features, tp)?;
3129        let local_in = matrix.in_features / tp;
3130        let blocks_per_rank = local_in / canonical_chunk_cols;
3131        let mut ranks = Vec::with_capacity(tp);
3132        for (rank, engine) in self.ranks.iter().enumerate() {
3133            let mut blocks = Vec::with_capacity(blocks_per_rank);
3134            for block in 0..blocks_per_rank {
3135                let global_block = rank * blocks_per_rank + block;
3136                let col_start = global_block * canonical_chunk_cols;
3137                let bytes = bf16_row_block(matrix, col_start, canonical_chunk_cols)?;
3138                blocks.push(upload_bf16_rank(
3139                    engine,
3140                    Bf16Matrix {
3141                        bytes: &bytes,
3142                        out_features: matrix.out_features,
3143                        in_features: canonical_chunk_cols,
3144                    },
3145                    f32_mirror,
3146                )?);
3147            }
3148            ranks.push(blocks);
3149        }
3150        Ok(ResidentStepBf16RowParallel {
3151            ranks,
3152            out_features: matrix.out_features,
3153            in_features: matrix.in_features,
3154            canonical_chunk_cols,
3155        })
3156    }
3157
3158    /// Host-staged exactness twin of [`Self::step_bf16_row_parallel_resident_native`].
3159    ///
3160    /// Block inputs and partials cross host memory, but every partial is added on the root device
3161    /// in global checkpoint-column order. Native transport must reproduce this result bitwise.
3162    pub fn step_bf16_row_parallel_resident(
3163        &self,
3164        matrix: &ResidentStepBf16RowParallel,
3165        activations: &[f32],
3166        tokens: usize,
3167    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3168        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3169        validate_activations(activations, tokens, matrix.in_features)?;
3170        let root = &self.ranks[0];
3171        let output_len = tokens
3172            .checked_mul(matrix.out_features)
3173            .ok_or("Step BF16 row output size overflow")?;
3174        let mut reduced = {
3175            let _main = root.gpu.enter_main()?;
3176            root.htod(&vec![0.0f32; output_len])?
3177        };
3178        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3179        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3180            for (block, resident) in blocks.iter().enumerate() {
3181                let global_block = rank * blocks_per_rank + block;
3182                let input = activation_shard(
3183                    activations,
3184                    tokens,
3185                    matrix.in_features,
3186                    PRODUCT_MAX_CARDS,
3187                    global_block,
3188                );
3189                let partial =
3190                    run_resident_bf16_rank(&self.ranks[rank], resident, &input, tokens, None)?;
3191                let next = {
3192                    let _main = root.gpu.enter_main()?;
3193                    let partial = root.htod(&partial)?;
3194                    let mut next = root.uninit(output_len)?;
3195                    root.add(&reduced, &partial, &mut next, output_len)?;
3196                    next
3197                };
3198                reduced = next;
3199            }
3200        }
3201        let _main = root.gpu.enter_main()?;
3202        root.dtoh(&reduced)
3203    }
3204
3205    /// Native-P2P Step row projection with canonical global K-block reduction.
3206    ///
3207    /// The full activation is uploaded once on the root. Each TP8-sized block is peer-scattered
3208    /// to its owning rank, its BF16 partial is peer-returned to the root, and root-device adds
3209    /// replay the same eight-block order as TP1 and the host-staged oracle.
3210    pub fn step_bf16_row_parallel_resident_native(
3211        &self,
3212        matrix: &ResidentStepBf16RowParallel,
3213        activations: &[f32],
3214        tokens: usize,
3215    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3216        if self.ranks.len() > 1 && !self.native_p2p {
3217            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3218        }
3219        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3220        validate_activations(activations, tokens, matrix.in_features)?;
3221        let root = &self.ranks[0];
3222        let root_input = {
3223            let _main = root.gpu.enter_main()?;
3224            root.htod(activations)?
3225        };
3226        // PRODUCER FENCE (2026-08-20 flake fix): the non-bulk arm below peer-reads root_input
3227        // from the other ranks' streams while root's clone_htod may still be in flight.
3228        {
3229            let _main = root.gpu.enter_main()?;
3230            root.stream().synchronize()?;
3231        }
3232        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3233        let _main = root.gpu.enter_main()?;
3234        root.dtoh(&reduced)
3235    }
3236
3237    /// Device-input twin of [`Self::step_bf16_row_parallel_resident_native`] (lane/
3238    /// hermes-perf-fixes, 2026-08-23): the full activation arrives as a ROOT-DEVICE buffer
3239    /// and the reduced output stays root-resident — no DtoH of the attention output, no
3240    /// host O staging, no re-upload. Byte-identical to the host-canonical arm by
3241    /// construction (same block scatter, kernels, and global TP8 reduction order; the root
3242    /// bytes are dtod-copied where the host arm htod'd the same bytes). Caller must have
3243    /// synchronized the producer stream; the root stream is synchronized before returning.
3244    pub fn step_bf16_row_parallel_resident_native_device(
3245        &self,
3246        matrix: &ResidentStepBf16RowParallel,
3247        root_activation: &CudaSlice<f32>,
3248        tokens: usize,
3249    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3250        if self.ranks.len() > 1 && !self.native_p2p {
3251            return Err("native Step BF16 row parallelism requires P2P ranks".into());
3252        }
3253        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3254        let values = tokens
3255            .checked_mul(matrix.in_features)
3256            .ok_or("device Step BF16 row activation size overflow")?;
3257        let root = &self.ranks[0];
3258        if tokens == 0
3259            || root_activation.len() < values
3260            || root_activation.ordinal() != root.ctx().ordinal()
3261        {
3262            return Err("device Step BF16 row root activation geometry mismatch".into());
3263        }
3264        let root_input = {
3265            let _main = root.gpu.enter_main()?;
3266            let mut root_input = root.uninit(values)?;
3267            root.stream()
3268                .memcpy_dtod(&root_activation.slice(0..values), &mut root_input)?;
3269            root.stream().synchronize()?; // producer fence, as the host-input twin
3270            root_input
3271        };
3272        let reduced = self.step_bf16_row_native_reduce_from_root(matrix, &root_input, tokens)?;
3273        let _main = root.gpu.enter_main()?;
3274        root.stream().synchronize()?;
3275        Ok(reduced)
3276    }
3277
3278    /// Shared core of the two native Step row arms above: block scatter + rank GEMMs +
3279    /// canonical global TP8-order root reduction, from a root-resident input, returning the
3280    /// root-resident reduced output. Extracted verbatim so the host and device twins cannot
3281    /// drift numerically.
3282    fn step_bf16_row_native_reduce_from_root(
3283        &self,
3284        matrix: &ResidentStepBf16RowParallel,
3285        root_input: &CudaSlice<f32>,
3286        tokens: usize,
3287    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3288        let root = &self.ranks[0];
3289        let output_len = tokens
3290            .checked_mul(matrix.out_features)
3291            .ok_or("native Step BF16 row output size overflow")?;
3292        let mut reduced = {
3293            let _main = root.gpu.enter_main()?;
3294            root.htod(&vec![0.0f32; output_len])?
3295        };
3296        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3297        let mut block_input_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3298        let mut root_packed_keepalive = Vec::with_capacity(PRODUCT_MAX_CARDS);
3299        let mut remote_partial_keepalive = Vec::new();
3300        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3301            for (block, resident) in blocks.iter().enumerate() {
3302                let global_block = rank * blocks_per_rank + block;
3303                let col_start = global_block * matrix.canonical_chunk_cols;
3304                let block_len = tokens
3305                    .checked_mul(matrix.canonical_chunk_cols)
3306                    .ok_or("native Step BF16 row block size overflow")?;
3307                let block_input = if self.bulk_p2p {
3308                    let root_packed = {
3309                        let _main = root.gpu.enter_main()?;
3310                        let mut root_packed = root.uninit(block_len)?;
3311                        root.copy_rows_strided(
3312                            &root_input,
3313                            &mut root_packed,
3314                            matrix.canonical_chunk_cols,
3315                            tokens,
3316                            matrix.in_features,
3317                            col_start,
3318                        )?;
3319                        root_packed
3320                    };
3321                    if rank == 0 {
3322                        root_packed
3323                    } else {
3324                        // PRODUCER FENCE (2026-08-20 flake fix): the pack kernel runs on the
3325                        // root stream; this rank's peer read must not overtake it.
3326                        {
3327                            let _main = root.gpu.enter_main()?;
3328                            root.stream().synchronize()?;
3329                        }
3330                        let engine = &self.ranks[rank];
3331                        let _main = engine.gpu.enter_main()?;
3332                        let mut block_input = engine.uninit(block_len)?;
3333                        engine
3334                            .stream()
3335                            .memcpy_dtod(&root_packed, &mut block_input)?;
3336                        root_packed_keepalive.push(root_packed);
3337                        block_input
3338                    }
3339                } else {
3340                    let engine = &self.ranks[rank];
3341                    let _main = engine.gpu.enter_main()?;
3342                    let mut block_input = engine.uninit(block_len)?;
3343                    for token in 0..tokens {
3344                        let source_start = token * matrix.in_features + col_start;
3345                        let source = root_input
3346                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3347                        let destination_start = token * matrix.canonical_chunk_cols;
3348                        let mut destination = block_input.slice_mut(
3349                            destination_start..destination_start + matrix.canonical_chunk_cols,
3350                        );
3351                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3352                    }
3353                    block_input
3354                };
3355                let partial = run_resident_bf16_rank_device(
3356                    &self.ranks[rank],
3357                    resident,
3358                    &block_input,
3359                    tokens,
3360                    None,
3361                    self.bulk_p2p,
3362                )?;
3363                block_input_keepalive.push(block_input);
3364                let root_partial = if rank == 0 {
3365                    partial
3366                } else {
3367                    // PRODUCER FENCE (2026-08-20 flake fix): the partial was produced by this
3368                    // rank's kernel on its own stream; root's peer read must not overtake it.
3369                    {
3370                        let engine = &self.ranks[rank];
3371                        let _main = engine.gpu.enter_main()?;
3372                        engine.stream().synchronize()?;
3373                    }
3374                    let _main = root.gpu.enter_main()?;
3375                    let mut peer_partial = root.uninit(output_len)?;
3376                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3377                    remote_partial_keepalive.push(partial);
3378                    peer_partial
3379                };
3380                let next = {
3381                    let _main = root.gpu.enter_main()?;
3382                    let mut next = root.uninit(output_len)?;
3383                    root.add(&reduced, &root_partial, &mut next, output_len)?;
3384                    next
3385                };
3386                reduced = next;
3387            }
3388        }
3389        {
3390            let _main = root.gpu.enter_main()?;
3391            root.stream().synchronize()?;
3392        }
3393        drop(remote_partial_keepalive);
3394        drop(root_packed_keepalive);
3395        drop(block_input_keepalive);
3396        Ok(reduced)
3397    }
3398
3399    /// Reduce rank-local Step attention shards in canonical TP8 K-block order and keep the result
3400    /// on the root device.
3401    pub fn step_bf16_row_parallel_resident_root_device(
3402        &self,
3403        matrix: &ResidentStepBf16RowParallel,
3404        rank_activations: &[CudaSlice<f32>],
3405        tokens: usize,
3406    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3407        if self.ranks.len() > 1 && !self.native_p2p {
3408            return Err(
3409                "device-resident Step BF16 row parallelism requires native P2P ranks".into(),
3410            );
3411        }
3412        validate_step_bf16_row_residency(&self.ranks, matrix)?;
3413        let local_width = matrix.in_features / self.ranks.len();
3414        let shard_len = tokens
3415            .checked_mul(local_width)
3416            .ok_or("device Step BF16 row shard size overflow")?;
3417        if tokens == 0
3418            || rank_activations.len() != self.ranks.len()
3419            || rank_activations
3420                .iter()
3421                .zip(&self.ranks)
3422                .any(|(rows, engine)| {
3423                    rows.len() != shard_len || rows.ordinal() != engine.ctx().ordinal()
3424                })
3425        {
3426            return Err("device Step BF16 row activation shard geometry changed".into());
3427        }
3428
3429        let blocks_per_rank = PRODUCT_MAX_CARDS / self.ranks.len();
3430        let mut block_inputs = Vec::with_capacity(self.ranks.len());
3431        let mut partials = Vec::with_capacity(self.ranks.len());
3432        for (rank, blocks) in matrix.ranks.iter().enumerate() {
3433            if blocks.len() != blocks_per_rank {
3434                return Err(format!(
3435                    "device Step BF16 row rank {rank} blocks {} != {blocks_per_rank}",
3436                    blocks.len()
3437                )
3438                .into());
3439            }
3440            let engine = &self.ranks[rank];
3441            let _main = engine.gpu.enter_main()?;
3442            let mut rank_inputs = Vec::with_capacity(blocks_per_rank);
3443            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
3444            for (block, resident) in blocks.iter().enumerate() {
3445                let block_len = tokens
3446                    .checked_mul(matrix.canonical_chunk_cols)
3447                    .ok_or("device Step BF16 row block size overflow")?;
3448                let mut block_input = engine.uninit(block_len)?;
3449                let local_col_start = block * matrix.canonical_chunk_cols;
3450                if self.bulk_p2p {
3451                    engine.copy_rows_strided(
3452                        &rank_activations[rank],
3453                        &mut block_input,
3454                        matrix.canonical_chunk_cols,
3455                        tokens,
3456                        local_width,
3457                        local_col_start,
3458                    )?;
3459                } else {
3460                    for token in 0..tokens {
3461                        let source_start = token * local_width + local_col_start;
3462                        let source = rank_activations[rank]
3463                            .slice(source_start..source_start + matrix.canonical_chunk_cols);
3464                        let destination_start = token * matrix.canonical_chunk_cols;
3465                        let mut destination = block_input.slice_mut(
3466                            destination_start..destination_start + matrix.canonical_chunk_cols,
3467                        );
3468                        engine.stream().memcpy_dtod(&source, &mut destination)?;
3469                    }
3470                }
3471                let partial = run_resident_bf16_rank_device(
3472                    engine,
3473                    resident,
3474                    &block_input,
3475                    tokens,
3476                    None,
3477                    self.bulk_p2p,
3478                )?;
3479                rank_inputs.push(block_input);
3480                rank_partials.push(partial);
3481            }
3482            block_inputs.push(rank_inputs);
3483            partials.push(rank_partials);
3484        }
3485        for engine in self.ranks.iter().skip(1) {
3486            let _main = engine.gpu.enter_main()?;
3487            engine.stream().synchronize()?;
3488        }
3489
3490        let output_len = tokens
3491            .checked_mul(matrix.out_features)
3492            .ok_or("device Step BF16 row output size overflow")?;
3493        let root = &self.ranks[0];
3494        let _main = root.gpu.enter_main()?;
3495        let mut reduced = root.htod(&vec![0.0f32; output_len])?;
3496        let mut remote_partials = Vec::new();
3497        for (rank, rank_partials) in partials.into_iter().enumerate() {
3498            for partial in rank_partials {
3499                let root_partial = if rank == 0 {
3500                    partial
3501                } else {
3502                    let mut peer_partial = root.uninit(output_len)?;
3503                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
3504                    remote_partials.push(partial);
3505                    peer_partial
3506                };
3507                let mut next = root.uninit(output_len)?;
3508                root.add(&reduced, &root_partial, &mut next, output_len)?;
3509                reduced = next;
3510            }
3511        }
3512        root.stream().synchronize()?;
3513        drop(remote_partials);
3514        drop(block_inputs);
3515        Ok(reduced)
3516    }
3517
3518    /// Reduce rank-local Step attention shards, then replicate the canonical root result.
3519    pub fn step_bf16_row_parallel_resident_replicated_device(
3520        &self,
3521        matrix: &ResidentStepBf16RowParallel,
3522        rank_activations: &[CudaSlice<f32>],
3523        tokens: usize,
3524    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
3525        let reduced =
3526            self.step_bf16_row_parallel_resident_root_device(matrix, rank_activations, tokens)?;
3527        let output_len = tokens
3528            .checked_mul(matrix.out_features)
3529            .ok_or("device Step BF16 row output size overflow")?;
3530        let mut ranks = Vec::with_capacity(self.ranks.len());
3531        ranks.push(reduced);
3532        for engine in self.ranks.iter().skip(1) {
3533            let _main = engine.gpu.enter_main()?;
3534            let mut peer_output = engine.uninit(output_len)?;
3535            engine.stream().memcpy_dtod(&ranks[0], &mut peer_output)?;
3536            ranks.push(peer_output);
3537        }
3538        Ok(ResidentReplicatedDeviceRows {
3539            ranks,
3540            tokens,
3541            width: matrix.out_features,
3542        })
3543    }
3544
3545    pub fn upload_expert(
3546        &self,
3547        gate: E4m3BlockMatrix<'_>,
3548        up: E4m3BlockMatrix<'_>,
3549        down: E4m3BlockMatrix<'_>,
3550    ) -> Result<ResidentTpExpert, Box<dyn std::error::Error>> {
3551        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3552            return Err("TP expert gate/up dimensions differ".into());
3553        }
3554        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3555            return Err(format!(
3556                "TP expert down {}x{} does not invert gate/up {}x{}",
3557                down.out_features, down.in_features, gate.out_features, gate.in_features
3558            )
3559            .into());
3560        }
3561        Ok(ResidentTpExpert {
3562            gate: self.upload_column_parallel(gate)?,
3563            up: self.upload_column_parallel(up)?,
3564            down: self.upload_row_parallel(down)?,
3565            input_width: gate.in_features,
3566            expert_width: gate.out_features,
3567        })
3568    }
3569
3570    pub fn run_expert(
3571        &self,
3572        expert: &ResidentTpExpert,
3573        input: &[f32],
3574        tokens: usize,
3575    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
3576        validate_activations(input, tokens, expert.input_width)?;
3577        let gate = self.column_parallel_resident(&expert.gate, input, tokens)?;
3578        let up = self.column_parallel_resident(&expert.up, input, tokens)?;
3579        let activated: Vec<f32> = gate
3580            .gathered
3581            .iter()
3582            .zip(&up.gathered)
3583            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
3584            .collect();
3585        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
3586        Ok(self
3587            .row_parallel_resident(&expert.down, &activated, tokens)?
3588            .reduced)
3589    }
3590
3591    pub fn upload_expert_parallel(
3592        &self,
3593        gate: E4m3ExpertBank<'_>,
3594        up: E4m3ExpertBank<'_>,
3595        down: E4m3ExpertBank<'_>,
3596    ) -> Result<ResidentExpertParallel, Box<dyn std::error::Error>> {
3597        gate.validate()?;
3598        up.validate()?;
3599        down.validate()?;
3600        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
3601            return Err("EP gate/up/down expert counts differ".into());
3602        }
3603        if gate.in_features != up.in_features || gate.out_features != up.out_features {
3604            return Err("EP gate/up dimensions differ".into());
3605        }
3606        if down.in_features != gate.out_features || down.out_features != gate.in_features {
3607            return Err(format!(
3608                "EP down {}x{} does not invert gate/up {}x{}",
3609                down.out_features, down.in_features, gate.out_features, gate.in_features
3610            )
3611            .into());
3612        }
3613        if gate.expert_count % self.ranks.len() != 0 {
3614            return Err(format!(
3615                "EP expert count {} is not divisible by {} ranks",
3616                gate.expert_count,
3617                self.ranks.len()
3618            )
3619            .into());
3620        }
3621
3622        let per_rank = gate.expert_count / self.ranks.len();
3623        let mut ranks = Vec::with_capacity(self.ranks.len());
3624        for (rank, engine) in self.ranks.iter().enumerate() {
3625            let expert_range = rank * per_rank..(rank + 1) * per_rank;
3626            ranks.push(ResidentEpRank {
3627                gate: upload_expert_bank_rank(engine, gate, expert_range.clone())?,
3628                up: upload_expert_bank_rank(engine, up, expert_range.clone())?,
3629                down: upload_expert_bank_rank(engine, down, expert_range)?,
3630            });
3631        }
3632        Ok(ResidentExpertParallel {
3633            ranks,
3634            expert_count: gate.expert_count,
3635            input_width: gate.in_features,
3636            expert_width: gate.out_features,
3637        })
3638    }
3639
3640    /// Prepare the official Step gate-only grouped-FP8 projection oracle on rank zero.
3641    ///
3642    /// This intentionally does not alter the resident EP path. It owns a full rank-local tensor
3643    /// bank solely so the grouped projection can be compared with the existing per-route oracle
3644    /// without routing, transport, or combine changing underneath it.
3645    #[allow(clippy::too_many_arguments)]
3646    pub fn prepare_step_grouped_fp8_gate(
3647        &self,
3648        gate: E4m3ExpertBank<'_>,
3649        up: E4m3ExpertBank<'_>,
3650        down: E4m3ExpertBank<'_>,
3651        input: &[f32],
3652        tokens: usize,
3653        selected: &[usize],
3654        activation_limit: Option<f32>,
3655    ) -> Result<PreparedStepGroupedFp8Gate, Box<dyn std::error::Error>> {
3656        gate.validate()?;
3657        up.validate()?;
3658        down.validate()?;
3659        validate_step_expert_activation_limit(activation_limit)?;
3660        if gate.expert_count != STEP_GROUPED_FP8_EXPERTS
3661            || up.expert_count != STEP_GROUPED_FP8_EXPERTS
3662            || down.expert_count != STEP_GROUPED_FP8_EXPERTS
3663        {
3664            return Err(format!(
3665                "official Step grouped FP8 gate requires {STEP_GROUPED_FP8_EXPERTS} experts, \
3666                 got gate/up/down={}/{}/{}",
3667                gate.expert_count, up.expert_count, down.expert_count,
3668            )
3669            .into());
3670        }
3671        if gate.in_features != up.in_features
3672            || gate.out_features != STEP_GROUPED_FP8_WIDTH
3673            || up.out_features != STEP_GROUPED_FP8_WIDTH
3674            || down.in_features != STEP_GROUPED_FP8_WIDTH
3675            || down.out_features != gate.in_features
3676        {
3677            return Err(format!(
3678                "official Step grouped FP8 geometry gate={}x{} up={}x{} down={}x{}",
3679                gate.out_features,
3680                gate.in_features,
3681                up.out_features,
3682                up.in_features,
3683                down.out_features,
3684                down.in_features,
3685            )
3686            .into());
3687        }
3688        validate_activations(input, tokens, gate.in_features)?;
3689        let pairs = tokens
3690            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3691            .ok_or("official Step grouped FP8 route count overflow")?;
3692        if selected.len() != pairs {
3693            return Err(format!(
3694                "official Step grouped FP8 routes {} != {tokens}x{STEP_GROUPED_FP8_TOP_K} \
3695                 ({pairs})",
3696                selected.len()
3697            )
3698            .into());
3699        }
3700        for (token, routes) in selected.chunks_exact(STEP_GROUPED_FP8_TOP_K).enumerate() {
3701            let mut unique = routes.to_vec();
3702            unique.sort_unstable();
3703            unique.dedup();
3704            if unique.len() != STEP_GROUPED_FP8_TOP_K {
3705                return Err(format!(
3706                    "official Step grouped FP8 token {token} routes are not top-8 unique: \
3707                     {routes:?}"
3708                )
3709                .into());
3710            }
3711        }
3712
3713        let engine = self
3714            .ranks
3715            .first()
3716            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3717        let _main = engine.gpu.enter_main()?;
3718        let expert_range = 0..STEP_GROUPED_FP8_EXPERTS;
3719        let gate = upload_expert_bank_rank(engine, gate, expert_range.clone())?;
3720        let up = upload_expert_bank_rank(engine, up, expert_range.clone())?;
3721        let down = upload_expert_bank_rank(engine, down, expert_range)?;
3722        let input = engine.htod(input)?;
3723        let route_csr = ExpertCsr::from_token_routes(
3724            STEP_GROUPED_FP8_EXPERTS,
3725            tokens,
3726            STEP_GROUPED_FP8_TOP_K,
3727            selected,
3728        )?
3729        .upload(engine)?;
3730        let pair_rows = (0..pairs).collect::<Vec<_>>();
3731        let down_csr =
3732            ExpertCsr::from_pair_rows(STEP_GROUPED_FP8_EXPERTS, pairs, selected, &pair_rows)?
3733                .upload(engine)?;
3734        let gate_workspace =
3735            Fp8GroupedWorkspace::new(engine, gate.in_features, gate.out_features, tokens, pairs)?;
3736        let up_workspace =
3737            Fp8GroupedWorkspace::new(engine, up.in_features, up.out_features, tokens, pairs)?;
3738        let down_workspace =
3739            Fp8GroupedWorkspace::new(engine, down.in_features, down.out_features, pairs, pairs)?;
3740        let activation = engine.uninit(pairs * STEP_GROUPED_FP8_WIDTH)?;
3741        Ok(PreparedStepGroupedFp8Gate {
3742            device: engine.ctx().ordinal(),
3743            gate,
3744            up,
3745            down,
3746            input,
3747            route_csr,
3748            down_csr,
3749            gate_workspace,
3750            up_workspace,
3751            down_workspace,
3752            activation,
3753            activation_limit,
3754            tokens,
3755            pairs,
3756        })
3757    }
3758
3759    /// Execute one prepared gate/up/activation/down projection sequence on rank zero.
3760    pub fn run_step_grouped_fp8_gate(
3761        &self,
3762        plan: &mut PreparedStepGroupedFp8Gate,
3763    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
3764        let engine = self
3765            .ranks
3766            .first()
3767            .ok_or("official Step grouped FP8 gate has no rank-zero engine")?;
3768        if engine.ctx().ordinal() != plan.device {
3769            return Err(format!(
3770                "official Step grouped FP8 plan device {} != rank-zero device {}",
3771                plan.device,
3772                engine.ctx().ordinal()
3773            )
3774            .into());
3775        }
3776        let _main = engine.gpu.enter_main()?;
3777
3778        plan.gate_workspace.quantize(engine, &plan.input)?;
3779        plan.gate_workspace.project(
3780            engine,
3781            &plan.gate.codes,
3782            &plan.gate.scales,
3783            &plan.route_csr,
3784            plan.gate.code_stride,
3785            plan.gate.scale_stride,
3786            1.0,
3787        )?;
3788        plan.up_workspace.quantize(engine, &plan.input)?;
3789        plan.up_workspace.project(
3790            engine,
3791            &plan.up.codes,
3792            &plan.up.scales,
3793            &plan.route_csr,
3794            plan.up.code_stride,
3795            plan.up.scale_stride,
3796            1.0,
3797        )?;
3798        if let Some(limit) = plan.activation_limit {
3799            engine.silu_clamped_mul_host_expf(
3800                plan.gate_workspace.output(),
3801                plan.up_workspace.output(),
3802                limit,
3803                &mut plan.activation,
3804                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3805            )?;
3806        } else {
3807            engine.silu_mul_host_expf(
3808                plan.gate_workspace.output(),
3809                plan.up_workspace.output(),
3810                &mut plan.activation,
3811                plan.pairs * STEP_GROUPED_FP8_WIDTH,
3812            )?;
3813        }
3814        plan.down_workspace.quantize(engine, &plan.activation)?;
3815        plan.down_workspace.project(
3816            engine,
3817            &plan.down.codes,
3818            &plan.down.scales,
3819            &plan.down_csr,
3820            plan.down.code_stride,
3821            plan.down.scale_stride,
3822            1.0,
3823        )?;
3824
3825        Ok(StepGroupedFp8ProjectionOutput {
3826            gate: engine.dtoh(plan.gate_workspace.output())?,
3827            up: engine.dtoh(plan.up_workspace.output())?,
3828            down: engine.dtoh(plan.down_workspace.output())?,
3829        })
3830    }
3831
3832    pub fn prepare_step_grouped_expert_parallel_gate(
3833        &self,
3834        experts: &ResidentExpertParallel,
3835        input: &[f32],
3836        tokens: usize,
3837        selected: &[usize],
3838        activation_limit: Option<f32>,
3839    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3840        self.prepare_step_grouped_expert_parallel_gate_with_capacity(
3841            experts,
3842            input,
3843            tokens,
3844            selected,
3845            activation_limit,
3846            tokens,
3847        )
3848    }
3849
3850    #[allow(clippy::too_many_arguments)]
3851    pub fn prepare_step_grouped_expert_parallel_gate_with_capacity(
3852        &self,
3853        experts: &ResidentExpertParallel,
3854        input: &[f32],
3855        tokens: usize,
3856        selected: &[usize],
3857        activation_limit: Option<f32>,
3858        max_tokens: usize,
3859    ) -> Result<PreparedStepGroupedExpertParallelGate, Box<dyn std::error::Error>> {
3860        if !self.native_p2p || !self.ep_device_arithmetic {
3861            return Err(
3862                "Step owner-grouped FP8 requires native P2P and device-resident arithmetic".into(),
3863            );
3864        }
3865        validate_step_expert_activation_limit(activation_limit)?;
3866        validate_ep_residency(&self.ranks, experts)?;
3867        validate_activations(input, tokens, experts.input_width)?;
3868        if max_tokens < tokens || max_tokens > i32::MAX as usize {
3869            return Err(format!(
3870                "official Step owner-grouped FP8 tokens {tokens} exceed capacity {max_tokens}"
3871            )
3872            .into());
3873        }
3874        if experts.expert_count != STEP_GROUPED_FP8_EXPERTS
3875            || experts.expert_width != STEP_GROUPED_FP8_WIDTH
3876        {
3877            return Err(format!(
3878                "official Step owner-grouped FP8 requires {} experts at width {}, got {} at {}",
3879                STEP_GROUPED_FP8_EXPERTS,
3880                STEP_GROUPED_FP8_WIDTH,
3881                experts.expert_count,
3882                experts.expert_width,
3883            )
3884            .into());
3885        }
3886        validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
3887        let max_pairs = max_tokens
3888            .checked_mul(STEP_GROUPED_FP8_TOP_K)
3889            .ok_or("official Step owner-grouped FP8 capacity route count overflow")?;
3890        let input_capacity = max_tokens
3891            .checked_mul(experts.input_width)
3892            .ok_or("official Step owner-grouped FP8 input capacity overflow")?;
3893
3894        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
3895        for engine in &self.ranks {
3896            let _main = engine.gpu.enter_main()?;
3897            rank_inputs.push(engine.uninit(input_capacity)?);
3898        }
3899
3900        let mut owners = Vec::with_capacity(self.ranks.len());
3901        for (owner_rank, rank) in experts.ranks.iter().enumerate() {
3902            if rank.gate.expert_range != rank.up.expert_range
3903                || rank.gate.expert_range != rank.down.expert_range
3904            {
3905                return Err(format!(
3906                    "owner-grouped FP8 rank {} gate/up/down expert ranges differ",
3907                    owner_rank
3908                )
3909                .into());
3910            }
3911            let local_experts = rank.gate.expert_range.len();
3912            let engine = &self.ranks[owner_rank];
3913            let _main = engine.gpu.enter_main()?;
3914            let route_csr =
3915                DeviceExpertCsr::with_capacity(engine, local_experts, max_tokens, max_pairs)?;
3916            let down_csr =
3917                DeviceExpertCsr::with_capacity(engine, local_experts, max_pairs, max_pairs)?;
3918            let gate_workspace = Fp8GroupedWorkspace::new(
3919                engine,
3920                experts.input_width,
3921                experts.expert_width,
3922                max_tokens,
3923                max_pairs,
3924            )?;
3925            let up_workspace = Fp8GroupedWorkspace::new(
3926                engine,
3927                experts.input_width,
3928                experts.expert_width,
3929                max_tokens,
3930                max_pairs,
3931            )?;
3932            let down_workspace = Fp8GroupedWorkspace::new(
3933                engine,
3934                experts.expert_width,
3935                experts.input_width,
3936                max_pairs,
3937                max_pairs,
3938            )?;
3939            let activation = engine.uninit(
3940                max_pairs
3941                    .checked_mul(experts.expert_width)
3942                    .ok_or("official Step owner-grouped FP8 activation capacity overflow")?,
3943            )?;
3944            owners.push(PreparedStepGroupedExpertOwner {
3945                rank: owner_rank,
3946                global_pairs: Vec::new(),
3947                route_csr,
3948                down_csr,
3949                gate_workspace,
3950                up_workspace,
3951                down_workspace,
3952                activation,
3953            });
3954        }
3955
3956        let mut plan = PreparedStepGroupedExpertParallelGate {
3957            rank_inputs,
3958            owners,
3959            activation_limit,
3960            tokens: 0,
3961            pairs: 0,
3962            max_tokens,
3963            max_pairs,
3964            input_width: experts.input_width,
3965            expert_width: experts.expert_width,
3966            generation: 0,
3967            executed_generation: None,
3968            ready: false,
3969        };
3970        self.refresh_step_grouped_expert_parallel_gate(
3971            experts, &mut plan, input, tokens, selected,
3972        )?;
3973        Ok(plan)
3974    }
3975
3976    fn prepare_step_grouped_expert_parallel_refresh(
3977        &self,
3978        experts: &ResidentExpertParallel,
3979        plan: &PreparedStepGroupedExpertParallelGate,
3980        tokens: usize,
3981        selected: &[usize],
3982    ) -> Result<(usize, u64, Vec<Option<StepGroupedExpertOwnerSchedule>>), Box<dyn std::error::Error>>
3983    {
3984        validate_ep_residency(&self.ranks, experts)?;
3985        if plan.rank_inputs.len() != self.ranks.len()
3986            || plan.owners.len() != self.ranks.len()
3987            || plan.input_width != experts.input_width
3988            || plan.expert_width != experts.expert_width
3989            || tokens > plan.max_tokens
3990        {
3991            return Err(format!(
3992                "Step owner-grouped FP8 refresh geometry changed ranks={}/{} owners={}/{} \
3993                 input={}/{} expert={}/{} tokens={}/{}",
3994                plan.rank_inputs.len(),
3995                self.ranks.len(),
3996                plan.owners.len(),
3997                self.ranks.len(),
3998                plan.input_width,
3999                experts.input_width,
4000                plan.expert_width,
4001                experts.expert_width,
4002                tokens,
4003                plan.max_tokens,
4004            )
4005            .into());
4006        }
4007        let pairs = validate_step_grouped_owner_routes(experts.expert_count, tokens, selected)?;
4008        if pairs > plan.max_pairs {
4009            return Err(format!(
4010                "Step owner-grouped FP8 route count {pairs} exceeds capacity {}",
4011                plan.max_pairs
4012            )
4013            .into());
4014        }
4015        let next_generation = plan
4016            .generation
4017            .checked_add(1)
4018            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4019        let owner_routes = partition_expert_owner_routes(
4020            experts.expert_count,
4021            self.ranks.len(),
4022            tokens,
4023            STEP_GROUPED_FP8_TOP_K,
4024            selected,
4025        )?;
4026        let mut schedules = Vec::with_capacity(self.ranks.len());
4027        for routes in owner_routes {
4028            if routes.selected.is_empty() {
4029                schedules.push(None);
4030                continue;
4031            }
4032            let local_experts = experts.ranks[routes.rank].gate.expert_range.len();
4033            let local_pairs = routes.selected.len();
4034            let route_csr = ExpertCsr::from_pair_rows(
4035                local_experts,
4036                tokens,
4037                &routes.selected,
4038                &routes.token_rows,
4039            )?;
4040            let down_rows = (0..local_pairs).collect::<Vec<_>>();
4041            let down_csr = ExpertCsr::from_pair_rows(
4042                local_experts,
4043                local_pairs,
4044                &routes.selected,
4045                &down_rows,
4046            )?;
4047            schedules.push(Some(StepGroupedExpertOwnerSchedule {
4048                global_pairs: routes.global_pairs,
4049                route_csr,
4050                down_csr,
4051            }));
4052        }
4053        Ok((pairs, next_generation, schedules))
4054    }
4055
4056    fn commit_step_grouped_expert_parallel_refresh(
4057        &self,
4058        plan: &mut PreparedStepGroupedExpertParallelGate,
4059        tokens: usize,
4060        pairs: usize,
4061        next_generation: u64,
4062        schedules: Vec<Option<StepGroupedExpertOwnerSchedule>>,
4063    ) -> Result<(), Box<dyn std::error::Error>> {
4064        for (owner, schedule) in plan.owners.iter_mut().zip(schedules) {
4065            let engine = &self.ranks[owner.rank];
4066            let _main = engine.gpu.enter_main()?;
4067            if let Some(schedule) = schedule {
4068                owner.route_csr.refresh(engine, &schedule.route_csr)?;
4069                owner.down_csr.refresh(engine, &schedule.down_csr)?;
4070                owner.global_pairs = schedule.global_pairs;
4071            } else {
4072                owner.route_csr.clear();
4073                owner.down_csr.clear();
4074                owner.global_pairs.clear();
4075            }
4076        }
4077        plan.tokens = tokens;
4078        plan.pairs = pairs;
4079        plan.generation = next_generation;
4080        plan.ready = true;
4081        Ok(())
4082    }
4083
4084    pub fn refresh_step_grouped_expert_parallel_gate(
4085        &self,
4086        experts: &ResidentExpertParallel,
4087        plan: &mut PreparedStepGroupedExpertParallelGate,
4088        input: &[f32],
4089        tokens: usize,
4090        selected: &[usize],
4091    ) -> Result<(), Box<dyn std::error::Error>> {
4092        validate_activations(input, tokens, experts.input_width)?;
4093        let (pairs, next_generation, schedules) =
4094            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4095
4096        plan.ready = false;
4097        plan.executed_generation = None;
4098        {
4099            let root = &self.ranks[0];
4100            let _main = root.gpu.enter_main()?;
4101            let mut destination = plan.rank_inputs[0].slice_mut(0..input.len());
4102            root.stream().memcpy_htod(input, &mut destination)?;
4103            root.stream().synchronize()?;
4104        }
4105        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4106        let root_input = &root_inputs[0];
4107        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4108            let engine = &self.ranks[rank + 1];
4109            let _main = engine.gpu.enter_main()?;
4110            let mut destination = peer_input.slice_mut(0..input.len());
4111            engine
4112                .stream()
4113                .memcpy_dtod(&root_input.slice(0..input.len()), &mut destination)?;
4114        }
4115        self.commit_step_grouped_expert_parallel_refresh(
4116            plan,
4117            tokens,
4118            pairs,
4119            next_generation,
4120            schedules,
4121        )
4122    }
4123
4124    /// Refresh routes and inputs from an already-resident rank-zero activation.
4125    ///
4126    /// The caller must order the source producer before this call. The root copy is completed
4127    /// before peer dispatch, while CSR and workspace allocations retain their stable addresses.
4128    pub fn refresh_step_grouped_expert_parallel_gate_from_root_device(
4129        &self,
4130        experts: &ResidentExpertParallel,
4131        plan: &mut PreparedStepGroupedExpertParallelGate,
4132        input: &CudaSlice<f32>,
4133        tokens: usize,
4134        selected: &[usize],
4135    ) -> Result<(), Box<dyn std::error::Error>> {
4136        let input_values = tokens
4137            .checked_mul(experts.input_width)
4138            .ok_or("Step owner-grouped FP8 input size overflow")?;
4139        let root = self
4140            .ranks
4141            .first()
4142            .ok_or("Step owner-grouped FP8 runtime has no root rank")?;
4143        if input.len() < input_values || input.ordinal() != root.ctx().ordinal() {
4144            return Err(format!(
4145                "Step owner-grouped FP8 root input len/device {}/{} does not cover {} values on \
4146                 device {}",
4147                input.len(),
4148                input.ordinal(),
4149                input_values,
4150                root.ctx().ordinal(),
4151            )
4152            .into());
4153        }
4154        let (pairs, next_generation, schedules) =
4155            self.prepare_step_grouped_expert_parallel_refresh(experts, plan, tokens, selected)?;
4156
4157        plan.ready = false;
4158        plan.executed_generation = None;
4159        {
4160            let _main = root.gpu.enter_main()?;
4161            let mut destination = plan.rank_inputs[0].slice_mut(0..input_values);
4162            root.stream()
4163                .memcpy_dtod(&input.slice(0..input_values), &mut destination)?;
4164            root.stream().synchronize()?;
4165        }
4166        let (root_inputs, peer_inputs) = plan.rank_inputs.split_at_mut(1);
4167        let root_input = &root_inputs[0];
4168        for (rank, peer_input) in peer_inputs.iter_mut().enumerate() {
4169            let engine = &self.ranks[rank + 1];
4170            let _main = engine.gpu.enter_main()?;
4171            let mut destination = peer_input.slice_mut(0..input_values);
4172            engine
4173                .stream()
4174                .memcpy_dtod(&root_input.slice(0..input_values), &mut destination)?;
4175        }
4176        self.commit_step_grouped_expert_parallel_refresh(
4177            plan,
4178            tokens,
4179            pairs,
4180            next_generation,
4181            schedules,
4182        )
4183    }
4184
4185    /// Replace a fixed route plan's rank inputs from an already replicated device batch.
4186    ///
4187    /// Route CSR remains unchanged. Advancing the generation invalidates every prior projection
4188    /// and combine result, so callers must refresh combine metadata before executing again.
4189    pub fn refresh_step_grouped_expert_parallel_inputs_from_replicated(
4190        &self,
4191        experts: &ResidentExpertParallel,
4192        plan: &mut PreparedStepGroupedExpertParallelGate,
4193        input: &ResidentReplicatedDeviceRows,
4194    ) -> Result<(), Box<dyn std::error::Error>> {
4195        validate_ep_residency(&self.ranks, experts)?;
4196        validate_replicated_device_rows(&self.ranks, input)?;
4197        if !plan.ready
4198            || input.tokens != plan.tokens
4199            || input.width != plan.input_width
4200            || input.tokens > plan.max_tokens
4201            || plan.rank_inputs.len() != self.ranks.len()
4202            || plan.owners.len() != self.ranks.len()
4203            || plan.input_width != experts.input_width
4204            || plan.expert_width != experts.expert_width
4205        {
4206            return Err("Step owner-grouped replicated input geometry changed".into());
4207        }
4208        let values = input
4209            .tokens
4210            .checked_mul(input.width)
4211            .ok_or("Step owner-grouped replicated input size overflow")?;
4212        let next_generation = plan
4213            .generation
4214            .checked_add(1)
4215            .ok_or("Step owner-grouped FP8 plan generation overflow")?;
4216        plan.ready = false;
4217        plan.executed_generation = None;
4218        for (rank, engine) in self.ranks.iter().enumerate() {
4219            let _main = engine.gpu.enter_main()?;
4220            let mut destination = plan.rank_inputs[rank].slice_mut(0..values);
4221            engine
4222                .stream()
4223                .memcpy_dtod(&input.ranks[rank], &mut destination)?;
4224        }
4225        plan.generation = next_generation;
4226        plan.ready = true;
4227        Ok(())
4228    }
4229
4230    pub fn execute_step_grouped_expert_parallel_gate(
4231        &self,
4232        experts: &ResidentExpertParallel,
4233        plan: &mut PreparedStepGroupedExpertParallelGate,
4234    ) -> Result<(), Box<dyn std::error::Error>> {
4235        validate_ep_residency(&self.ranks, experts)?;
4236        if !plan.ready
4237            || plan.rank_inputs.len() != self.ranks.len()
4238            || plan.owners.len() != self.ranks.len()
4239            || plan.input_width != experts.input_width
4240            || plan.expert_width != experts.expert_width
4241        {
4242            return Err("Step owner-grouped FP8 plan is not ready or its geometry changed".into());
4243        }
4244        plan.executed_generation = None;
4245
4246        for owner in &mut plan.owners {
4247            if owner.global_pairs.is_empty() {
4248                continue;
4249            }
4250            let engine = &self.ranks[owner.rank];
4251            let bank = &experts.ranks[owner.rank];
4252            let _main = engine.gpu.enter_main()?;
4253            let local_pairs = owner.global_pairs.len();
4254            owner.gate_workspace.quantize_for_shape(
4255                engine,
4256                &plan.rank_inputs[owner.rank],
4257                plan.tokens,
4258                local_pairs,
4259            )?;
4260            owner.gate_workspace.project(
4261                engine,
4262                &bank.gate.codes,
4263                &bank.gate.scales,
4264                &owner.route_csr,
4265                bank.gate.code_stride,
4266                bank.gate.scale_stride,
4267                1.0,
4268            )?;
4269            owner.up_workspace.quantize_for_shape(
4270                engine,
4271                &plan.rank_inputs[owner.rank],
4272                plan.tokens,
4273                local_pairs,
4274            )?;
4275            owner.up_workspace.project(
4276                engine,
4277                &bank.up.codes,
4278                &bank.up.scales,
4279                &owner.route_csr,
4280                bank.up.code_stride,
4281                bank.up.scale_stride,
4282                1.0,
4283            )?;
4284        }
4285        for owner in &mut plan.owners {
4286            if owner.global_pairs.is_empty() {
4287                continue;
4288            }
4289            let engine = &self.ranks[owner.rank];
4290            let _main = engine.gpu.enter_main()?;
4291            let values = owner.global_pairs.len() * plan.expert_width;
4292            if let Some(limit) = plan.activation_limit {
4293                engine.silu_clamped_mul_host_expf(
4294                    owner.gate_workspace.output(),
4295                    owner.up_workspace.output(),
4296                    limit,
4297                    &mut owner.activation,
4298                    values,
4299                )?;
4300            } else {
4301                engine.silu_mul_host_expf(
4302                    owner.gate_workspace.output(),
4303                    owner.up_workspace.output(),
4304                    &mut owner.activation,
4305                    values,
4306                )?;
4307            }
4308        }
4309        for owner in &mut plan.owners {
4310            if owner.global_pairs.is_empty() {
4311                continue;
4312            }
4313            let engine = &self.ranks[owner.rank];
4314            let bank = &experts.ranks[owner.rank];
4315            let _main = engine.gpu.enter_main()?;
4316            let local_pairs = owner.global_pairs.len();
4317            owner.down_workspace.quantize_for_shape(
4318                engine,
4319                &owner.activation,
4320                local_pairs,
4321                local_pairs,
4322            )?;
4323            owner.down_workspace.project(
4324                engine,
4325                &bank.down.codes,
4326                &bank.down.scales,
4327                &owner.down_csr,
4328                bank.down.code_stride,
4329                bank.down.scale_stride,
4330                1.0,
4331            )?;
4332        }
4333        plan.executed_generation = Some(plan.generation);
4334        Ok(())
4335    }
4336
4337    pub fn collect_step_grouped_expert_parallel_gate(
4338        &self,
4339        plan: &PreparedStepGroupedExpertParallelGate,
4340    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4341        if !plan.ready || plan.executed_generation != Some(plan.generation) {
4342            return Err("Step owner-grouped FP8 projection is stale or has not executed".into());
4343        }
4344        let mut gate = vec![0.0f32; plan.pairs * plan.expert_width];
4345        let mut up = vec![0.0f32; plan.pairs * plan.expert_width];
4346        let mut down = vec![0.0f32; plan.pairs * plan.input_width];
4347        for owner in &plan.owners {
4348            if owner.global_pairs.is_empty() {
4349                continue;
4350            }
4351            let engine = &self.ranks[owner.rank];
4352            let _main = engine.gpu.enter_main()?;
4353            let owner_gate = engine.dtoh_view(
4354                &owner
4355                    .gate_workspace
4356                    .output()
4357                    .slice(0..owner.gate_workspace.output_len()),
4358            )?;
4359            let owner_up = engine.dtoh_view(
4360                &owner
4361                    .up_workspace
4362                    .output()
4363                    .slice(0..owner.up_workspace.output_len()),
4364            )?;
4365            let owner_down = engine.dtoh_view(
4366                &owner
4367                    .down_workspace
4368                    .output()
4369                    .slice(0..owner.down_workspace.output_len()),
4370            )?;
4371            for (local_pair, &global_pair) in owner.global_pairs.iter().enumerate() {
4372                let local_expert = local_pair * plan.expert_width;
4373                let global_expert = global_pair * plan.expert_width;
4374                gate[global_expert..global_expert + plan.expert_width]
4375                    .copy_from_slice(&owner_gate[local_expert..local_expert + plan.expert_width]);
4376                up[global_expert..global_expert + plan.expert_width]
4377                    .copy_from_slice(&owner_up[local_expert..local_expert + plan.expert_width]);
4378
4379                let local_hidden = local_pair * plan.input_width;
4380                let global_hidden = global_pair * plan.input_width;
4381                down[global_hidden..global_hidden + plan.input_width]
4382                    .copy_from_slice(&owner_down[local_hidden..local_hidden + plan.input_width]);
4383            }
4384        }
4385        Ok(StepGroupedFp8ProjectionOutput { gate, up, down })
4386    }
4387
4388    pub fn run_step_grouped_expert_parallel_gate(
4389        &self,
4390        experts: &ResidentExpertParallel,
4391        plan: &mut PreparedStepGroupedExpertParallelGate,
4392    ) -> Result<StepGroupedFp8ProjectionOutput, Box<dyn std::error::Error>> {
4393        self.execute_step_grouped_expert_parallel_gate(experts, plan)?;
4394        self.collect_step_grouped_expert_parallel_gate(plan)
4395    }
4396
4397    pub fn prepare_step_grouped_expert_parallel_combine(
4398        &self,
4399        plan: &PreparedStepGroupedExpertParallelGate,
4400        route_weights: &[f32],
4401    ) -> Result<PreparedPeerWeightedRouteCombine, Box<dyn std::error::Error>> {
4402        if !self.native_p2p || !self.ep_device_arithmetic || !plan.ready {
4403            return Err(
4404                "Step owner-grouped combine requires a ready native-P2P device plan".into(),
4405            );
4406        }
4407        let owner_pairs = plan
4408            .owners
4409            .iter()
4410            .map(|owner| owner.global_pairs.as_slice())
4411            .collect::<Vec<_>>();
4412        let shape = validate_weighted_route_combine(
4413            plan.input_width,
4414            STEP_GROUPED_FP8_TOP_K,
4415            plan.max_tokens,
4416            plan.tokens,
4417            &owner_pairs,
4418            route_weights,
4419        )?;
4420        if shape.max_pairs != plan.max_pairs {
4421            return Err(format!(
4422                "Step owner-grouped combine capacity {} != projection capacity {}",
4423                shape.max_pairs, plan.max_pairs
4424            )
4425            .into());
4426        }
4427        let root = self
4428            .ranks
4429            .first()
4430            .ok_or("Step owner-grouped combine has no root rank")?;
4431        let slot_values = shape
4432            .max_pairs
4433            .checked_mul(plan.input_width)
4434            .ok_or("Step owner-grouped combine slot capacity overflow")?;
4435        let output_values = plan
4436            .max_tokens
4437            .checked_mul(plan.input_width)
4438            .ok_or("Step owner-grouped combine output capacity overflow")?;
4439        let (root_device, owners, peer_staging, slots, weights, output) = {
4440            let _main = root.gpu.enter_main()?;
4441            let mut owners = Vec::with_capacity(plan.owners.len());
4442            for _ in &plan.owners {
4443                owners.push(PreparedPeerWeightedRouteOwner {
4444                    token_rows: root.htod_i32(&vec![0; shape.max_pairs])?,
4445                    slots: root.htod_i32(&vec![0; shape.max_pairs])?,
4446                    weights: root.htod(&vec![0.0; shape.max_pairs])?,
4447                    active_pairs: 0,
4448                });
4449            }
4450            (
4451                root.ctx().ordinal(),
4452                owners,
4453                root.uninit(slot_values)?,
4454                root.uninit(slot_values)?,
4455                root.uninit(shape.max_pairs)?,
4456                root.uninit(output_values)?,
4457            )
4458        };
4459        let mut peer_devices = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4460        let mut peer_outputs = Vec::with_capacity(self.ranks.len().saturating_sub(1));
4461        for engine in self.ranks.iter().skip(1) {
4462            let _main = engine.gpu.enter_main()?;
4463            peer_devices.push(engine.ctx().ordinal());
4464            peer_outputs.push(engine.uninit(output_values)?);
4465        }
4466        let mut combine = PreparedPeerWeightedRouteCombine {
4467            root_device,
4468            owners,
4469            peer_staging,
4470            slots,
4471            weights,
4472            output,
4473            peer_devices,
4474            peer_outputs,
4475            width: plan.input_width,
4476            experts_per_token: STEP_GROUPED_FP8_TOP_K,
4477            max_tokens: plan.max_tokens,
4478            max_pairs: shape.max_pairs,
4479            tokens: 0,
4480            pairs: 0,
4481            projection_generation: 0,
4482            output_generation: None,
4483            broadcast_generation: None,
4484            ready: false,
4485        };
4486        self.refresh_step_grouped_expert_parallel_combine(plan, &mut combine, route_weights)?;
4487        Ok(combine)
4488    }
4489
4490    pub fn refresh_step_grouped_expert_parallel_combine(
4491        &self,
4492        plan: &PreparedStepGroupedExpertParallelGate,
4493        combine: &mut PreparedPeerWeightedRouteCombine,
4494        route_weights: &[f32],
4495    ) -> Result<(), Box<dyn std::error::Error>> {
4496        let output_capacity = combine
4497            .max_tokens
4498            .checked_mul(combine.width)
4499            .ok_or("Step owner-grouped combine output capacity overflow")?;
4500        if !plan.ready
4501            || combine.owners.len() != plan.owners.len()
4502            || combine.peer_devices.len() + 1 != self.ranks.len()
4503            || combine.peer_outputs.len() + 1 != self.ranks.len()
4504            || combine.width != plan.input_width
4505            || combine.experts_per_token != STEP_GROUPED_FP8_TOP_K
4506            || combine.max_tokens != plan.max_tokens
4507            || combine.max_pairs != plan.max_pairs
4508            || combine.output.len() < output_capacity
4509            || combine
4510                .peer_outputs
4511                .iter()
4512                .any(|output| output.len() < output_capacity)
4513        {
4514            return Err("Step owner-grouped combine/projection geometry changed".into());
4515        }
4516        if self
4517            .ranks
4518            .iter()
4519            .skip(1)
4520            .zip(&combine.peer_devices)
4521            .any(|(engine, &device)| engine.ctx().ordinal() != device)
4522        {
4523            return Err("Step owner-grouped combine peer devices changed".into());
4524        }
4525        let owner_pairs = plan
4526            .owners
4527            .iter()
4528            .map(|owner| owner.global_pairs.as_slice())
4529            .collect::<Vec<_>>();
4530        let shape = validate_weighted_route_combine(
4531            combine.width,
4532            combine.experts_per_token,
4533            combine.max_tokens,
4534            plan.tokens,
4535            &owner_pairs,
4536            route_weights,
4537        )?;
4538        if shape.max_pairs != combine.max_pairs {
4539            return Err("Step owner-grouped combine capacity changed during refresh".into());
4540        }
4541        let metadata = owner_pairs
4542            .iter()
4543            .map(|pairs| {
4544                let token_rows = pairs
4545                    .iter()
4546                    .map(|&pair| (pair / combine.experts_per_token) as i32)
4547                    .collect::<Vec<_>>();
4548                let slots = pairs
4549                    .iter()
4550                    .map(|&pair| (pair % combine.experts_per_token) as i32)
4551                    .collect::<Vec<_>>();
4552                let weights = pairs
4553                    .iter()
4554                    .map(|&pair| route_weights[pair])
4555                    .collect::<Vec<_>>();
4556                (token_rows, slots, weights)
4557            })
4558            .collect::<Vec<_>>();
4559
4560        combine.ready = false;
4561        combine.output_generation = None;
4562        combine.broadcast_generation = None;
4563        let root = self
4564            .ranks
4565            .first()
4566            .ok_or("Step owner-grouped combine has no root rank")?;
4567        let _main = root.gpu.enter_main()?;
4568        if root.ctx().ordinal() != combine.root_device {
4569            return Err(format!(
4570                "Step owner-grouped combine root device changed {} != {}",
4571                root.ctx().ordinal(),
4572                combine.root_device
4573            )
4574            .into());
4575        }
4576        for (owner, (token_rows, slots, weights)) in combine.owners.iter_mut().zip(metadata) {
4577            if token_rows.is_empty() {
4578                owner.active_pairs = 0;
4579                continue;
4580            }
4581            root.htod_i32_into(&mut owner.token_rows, &token_rows)?;
4582            root.htod_i32_into(&mut owner.slots, &slots)?;
4583            let mut weight_prefix = owner.weights.slice_mut(0..weights.len());
4584            root.stream().memcpy_htod(&weights, &mut weight_prefix)?;
4585            owner.active_pairs = token_rows.len();
4586        }
4587        combine.tokens = plan.tokens;
4588        combine.pairs = shape.pairs;
4589        combine.projection_generation = plan.generation;
4590        combine.ready = true;
4591        Ok(())
4592    }
4593
4594    pub fn execute_step_grouped_expert_parallel_combine(
4595        &self,
4596        plan: &PreparedStepGroupedExpertParallelGate,
4597        combine: &mut PreparedPeerWeightedRouteCombine,
4598    ) -> Result<(), Box<dyn std::error::Error>> {
4599        if !plan.ready
4600            || plan.executed_generation != Some(plan.generation)
4601            || !combine.ready
4602            || combine.tokens != plan.tokens
4603            || combine.pairs != plan.pairs
4604            || combine.width != plan.input_width
4605            || combine.owners.len() != plan.owners.len()
4606            || combine.projection_generation != plan.generation
4607        {
4608            return Err("Step owner-grouped combine is stale or its geometry changed".into());
4609        }
4610        combine.output_generation = None;
4611        combine.broadcast_generation = None;
4612        for owner in &plan.owners {
4613            if owner.rank == 0 || owner.global_pairs.is_empty() {
4614                continue;
4615            }
4616            let engine = &self.ranks[owner.rank];
4617            let _main = engine.gpu.enter_main()?;
4618            engine.stream().synchronize()?;
4619        }
4620        let root = self
4621            .ranks
4622            .first()
4623            .ok_or("Step owner-grouped combine has no root rank")?;
4624        let _main = root.gpu.enter_main()?;
4625        if root.ctx().ordinal() != combine.root_device {
4626            return Err("Step owner-grouped combine is not resident on the root device".into());
4627        }
4628        for (index, owner) in plan.owners.iter().enumerate() {
4629            let metadata = &combine.owners[index];
4630            if owner.global_pairs.len() != metadata.active_pairs {
4631                return Err(format!(
4632                    "Step owner-grouped combine owner {index} rows {} != metadata {}",
4633                    owner.global_pairs.len(),
4634                    metadata.active_pairs
4635                )
4636                .into());
4637            }
4638            if metadata.active_pairs == 0 {
4639                continue;
4640            }
4641            let values = metadata
4642                .active_pairs
4643                .checked_mul(combine.width)
4644                .ok_or("Step owner-grouped combine peer value count overflow")?;
4645            if owner.rank == 0 {
4646                root.scatter_slot(
4647                    owner.down_workspace.output(),
4648                    &metadata.token_rows,
4649                    &metadata.slots,
4650                    &metadata.weights,
4651                    &mut combine.slots,
4652                    &mut combine.weights,
4653                    combine.width,
4654                    combine.experts_per_token,
4655                    metadata.active_pairs,
4656                )?;
4657            } else {
4658                let source = owner.down_workspace.output().slice(0..values);
4659                let mut destination = combine.peer_staging.slice_mut(0..values);
4660                root.stream().memcpy_dtod(&source, &mut destination)?;
4661                root.scatter_slot(
4662                    &combine.peer_staging,
4663                    &metadata.token_rows,
4664                    &metadata.slots,
4665                    &metadata.weights,
4666                    &mut combine.slots,
4667                    &mut combine.weights,
4668                    combine.width,
4669                    combine.experts_per_token,
4670                    metadata.active_pairs,
4671                )?;
4672            }
4673        }
4674        root.reduce_slots_host(
4675            &combine.slots,
4676            &combine.weights,
4677            &mut combine.output,
4678            combine.width,
4679            combine.experts_per_token,
4680            combine.tokens,
4681        )?;
4682        combine.output_generation = Some(plan.generation);
4683        Ok(())
4684    }
4685
4686    pub fn collect_step_grouped_expert_parallel_combine(
4687        &self,
4688        plan: &PreparedStepGroupedExpertParallelGate,
4689        combine: &PreparedPeerWeightedRouteCombine,
4690    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4691        if !plan.ready
4692            || combine.output_generation != Some(plan.generation)
4693            || combine.projection_generation != plan.generation
4694        {
4695            return Err("Step owner-grouped combine output is stale or has not executed".into());
4696        }
4697        let root = self
4698            .ranks
4699            .first()
4700            .ok_or("Step owner-grouped combine has no root rank")?;
4701        let _main = root.gpu.enter_main()?;
4702        if root.ctx().ordinal() != combine.root_device {
4703            return Err("Step owner-grouped combine is not resident on the root device".into());
4704        }
4705        root.dtoh_view(&combine.output.slice(0..combine.tokens * combine.width))
4706    }
4707
4708    /// Copy the active root combine result into a caller-owned engine on the same CUDA device.
4709    ///
4710    /// The persistent combine buffer remains reusable by the next route generation; the returned
4711    /// allocation follows the serving runtime's ordinary transient-output ownership.
4712    pub fn copy_step_grouped_expert_parallel_combine_root(
4713        &self,
4714        plan: &PreparedStepGroupedExpertParallelGate,
4715        combine: &PreparedPeerWeightedRouteCombine,
4716        destination: &Engine,
4717    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4718        if !plan.ready
4719            || combine.output_generation != Some(plan.generation)
4720            || combine.projection_generation != plan.generation
4721        {
4722            return Err("Step owner-grouped combine output is stale or has not executed".into());
4723        }
4724        let root = self
4725            .ranks
4726            .first()
4727            .ok_or("Step owner-grouped combine has no root rank")?;
4728        if root.ctx().ordinal() != combine.root_device
4729            || destination.ctx().ordinal() != combine.root_device
4730        {
4731            return Err(format!(
4732                "Step owner-grouped combine root/destination devices {}/{} != {}",
4733                root.ctx().ordinal(),
4734                destination.ctx().ordinal(),
4735                combine.root_device,
4736            )
4737            .into());
4738        }
4739        let values = combine
4740            .tokens
4741            .checked_mul(combine.width)
4742            .ok_or("Step owner-grouped combine copy size overflow")?;
4743        {
4744            let _main = root.gpu.enter_main()?;
4745            root.stream().synchronize()?;
4746        }
4747        let _main = destination.gpu.enter_main()?;
4748        let mut output = destination.uninit(values)?;
4749        destination
4750            .stream()
4751            .memcpy_dtod(&combine.output.slice(0..values), &mut output)?;
4752        Ok(output)
4753    }
4754
4755    pub fn broadcast_step_grouped_expert_parallel_combine(
4756        &self,
4757        plan: &PreparedStepGroupedExpertParallelGate,
4758        combine: &mut PreparedPeerWeightedRouteCombine,
4759    ) -> Result<(), Box<dyn std::error::Error>> {
4760        if !plan.ready
4761            || combine.output_generation != Some(plan.generation)
4762            || combine.projection_generation != plan.generation
4763            || combine.peer_devices.len() + 1 != self.ranks.len()
4764            || combine.peer_outputs.len() + 1 != self.ranks.len()
4765        {
4766            return Err("Step owner-grouped combine output cannot be broadcast".into());
4767        }
4768        combine.broadcast_generation = None;
4769        let values = combine
4770            .tokens
4771            .checked_mul(combine.width)
4772            .ok_or("Step owner-grouped combine broadcast size overflow")?;
4773        {
4774            let root = self
4775                .ranks
4776                .first()
4777                .ok_or("Step owner-grouped combine has no root rank")?;
4778            let _main = root.gpu.enter_main()?;
4779            if root.ctx().ordinal() != combine.root_device {
4780                return Err("Step owner-grouped combine root device changed".into());
4781            }
4782            root.stream().synchronize()?;
4783        }
4784        let source = &combine.output;
4785        for (index, destination_buffer) in combine.peer_outputs.iter_mut().enumerate() {
4786            let engine = &self.ranks[index + 1];
4787            let _main = engine.gpu.enter_main()?;
4788            if engine.ctx().ordinal() != combine.peer_devices[index] {
4789                return Err(format!(
4790                    "Step owner-grouped combine peer {} device changed",
4791                    index + 1
4792                )
4793                .into());
4794            }
4795            let mut destination = destination_buffer.slice_mut(0..values);
4796            engine
4797                .stream()
4798                .memcpy_dtod(&source.slice(0..values), &mut destination)?;
4799        }
4800        combine.broadcast_generation = Some(plan.generation);
4801        Ok(())
4802    }
4803
4804    pub fn collect_step_grouped_expert_parallel_broadcast(
4805        &self,
4806        plan: &PreparedStepGroupedExpertParallelGate,
4807        combine: &PreparedPeerWeightedRouteCombine,
4808    ) -> Result<Vec<Vec<f32>>, Box<dyn std::error::Error>> {
4809        if !plan.ready
4810            || combine.output_generation != Some(plan.generation)
4811            || combine.broadcast_generation != Some(plan.generation)
4812            || combine.peer_outputs.len() + 1 != self.ranks.len()
4813        {
4814            return Err("Step owner-grouped combine broadcast is stale or incomplete".into());
4815        }
4816        let values = combine
4817            .tokens
4818            .checked_mul(combine.width)
4819            .ok_or("Step owner-grouped combine collection size overflow")?;
4820        let mut outputs = Vec::with_capacity(self.ranks.len());
4821        {
4822            let root = &self.ranks[0];
4823            let _main = root.gpu.enter_main()?;
4824            outputs.push(root.dtoh_view(&combine.output.slice(0..values))?);
4825        }
4826        for (index, output) in combine.peer_outputs.iter().enumerate() {
4827            let engine = &self.ranks[index + 1];
4828            let _main = engine.gpu.enter_main()?;
4829            outputs.push(engine.dtoh_view(&output.slice(0..values))?);
4830        }
4831        Ok(outputs)
4832    }
4833
4834    /// Add routed and replicated shared-expert outputs, then add the attention residual.
4835    pub fn finish_step_grouped_expert_parallel_layer(
4836        &self,
4837        plan: &PreparedStepGroupedExpertParallelGate,
4838        combine: &PreparedPeerWeightedRouteCombine,
4839        shared: &ResidentReplicatedDeviceRows,
4840        residual: &ResidentReplicatedDeviceRows,
4841    ) -> Result<ResidentReplicatedDeviceRows, Box<dyn std::error::Error>> {
4842        validate_replicated_device_rows(&self.ranks, shared)?;
4843        validate_replicated_device_rows(&self.ranks, residual)?;
4844        if !plan.ready
4845            || plan.executed_generation != Some(plan.generation)
4846            || combine.output_generation != Some(plan.generation)
4847            || combine.broadcast_generation != Some(plan.generation)
4848            || combine.projection_generation != plan.generation
4849            || combine.peer_outputs.len() + 1 != self.ranks.len()
4850            || shared.tokens != combine.tokens
4851            || residual.tokens != combine.tokens
4852            || shared.width != combine.width
4853            || residual.width != combine.width
4854        {
4855            return Err("Step full-layer finish inputs are stale or their geometry changed".into());
4856        }
4857        let values = combine
4858            .tokens
4859            .checked_mul(combine.width)
4860            .ok_or("Step full-layer output size overflow")?;
4861        let mut ranks = Vec::with_capacity(self.ranks.len());
4862        for rank in 0..self.ranks.len() {
4863            let engine = &self.ranks[rank];
4864            let _main = engine.gpu.enter_main()?;
4865            let routed = if rank == 0 {
4866                &combine.output
4867            } else {
4868                &combine.peer_outputs[rank - 1]
4869            };
4870            let mut ffn = engine.uninit(values)?;
4871            engine.add(routed, &shared.ranks[rank], &mut ffn, values)?;
4872            let mut output = engine.uninit(values)?;
4873            engine.add(&residual.ranks[rank], &ffn, &mut output, values)?;
4874            ranks.push(output);
4875        }
4876        Ok(ResidentReplicatedDeviceRows {
4877            ranks,
4878            tokens: combine.tokens,
4879            width: combine.width,
4880        })
4881    }
4882
4883    pub fn run_step_grouped_expert_parallel_combine(
4884        &self,
4885        plan: &PreparedStepGroupedExpertParallelGate,
4886        combine: &mut PreparedPeerWeightedRouteCombine,
4887    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4888        self.execute_step_grouped_expert_parallel_combine(plan, combine)?;
4889        self.collect_step_grouped_expert_parallel_combine(plan, combine)
4890    }
4891
4892    pub fn upload_tensor_parallel(
4893        &self,
4894        gate: E4m3ExpertBank<'_>,
4895        up: E4m3ExpertBank<'_>,
4896        down: E4m3ExpertBank<'_>,
4897    ) -> Result<ResidentTensorParallel, Box<dyn std::error::Error>> {
4898        gate.validate()?;
4899        up.validate()?;
4900        down.validate()?;
4901        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
4902            return Err("TP gate/up/down expert counts differ".into());
4903        }
4904        if gate.in_features != up.in_features || gate.out_features != up.out_features {
4905            return Err("TP gate/up dimensions differ".into());
4906        }
4907        if down.in_features != gate.out_features || down.out_features != gate.in_features {
4908            return Err(format!(
4909                "TP down {}x{} does not invert gate/up {}x{}",
4910                down.out_features, down.in_features, gate.out_features, gate.in_features
4911            )
4912            .into());
4913        }
4914        let tp = self.ranks.len();
4915        validate_column_bank_shape(gate, tp)?;
4916        validate_column_bank_shape(up, tp)?;
4917        validate_row_bank_shape(down, tp)?;
4918
4919        let mut gate_ranks = Vec::with_capacity(tp);
4920        let mut up_ranks = Vec::with_capacity(tp);
4921        let mut down_ranks = Vec::with_capacity(tp);
4922        for (rank, engine) in self.ranks.iter().enumerate() {
4923            gate_ranks.push(upload_column_bank_rank(engine, gate, tp, rank)?);
4924            up_ranks.push(upload_column_bank_rank(engine, up, tp, rank)?);
4925            down_ranks.push(upload_row_bank_rank(engine, down, tp, rank)?);
4926        }
4927        Ok(ResidentTensorParallel {
4928            bank: ResidentTpExpertBank {
4929                gate: gate_ranks,
4930                up: up_ranks,
4931                down: down_ranks,
4932                expert_count: gate.expert_count,
4933                input_width: gate.in_features,
4934                expert_width: gate.out_features,
4935            },
4936        })
4937    }
4938
4939    pub fn run_tensor_parallel_routes(
4940        &self,
4941        experts: &ResidentTensorParallel,
4942        input: &[f32],
4943        tokens: usize,
4944        selected: &[usize],
4945        route_weights: &[f32],
4946        experts_per_token: usize,
4947    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
4948        validate_tp_bank_residency(&self.ranks, &experts.bank)?;
4949        validate_activations(input, tokens, experts.bank.input_width)?;
4950        let pairs = tokens
4951            .checked_mul(experts_per_token)
4952            .ok_or("TP route count overflow")?;
4953        if selected.len() != pairs || route_weights.len() != pairs {
4954            return Err(format!(
4955                "TP routes selected={} weights={} != tokens {tokens} x experts/token \
4956                 {experts_per_token} ({pairs})",
4957                selected.len(),
4958                route_weights.len(),
4959            )
4960            .into());
4961        }
4962        if !route_weights.iter().all(|weight| weight.is_finite()) {
4963            return Err("TP route weights contain a non-finite value".into());
4964        }
4965
4966        let mut output = vec![0.0f32; tokens * experts.bank.input_width];
4967        for token in 0..tokens {
4968            let input_row =
4969                &input[token * experts.bank.input_width..(token + 1) * experts.bank.input_width];
4970            for slot in 0..experts_per_token {
4971                let pair = token * experts_per_token + slot;
4972                let expert = selected[pair];
4973                if expert >= experts.bank.expert_count {
4974                    return Err(format!(
4975                        "TP selected expert {expert} outside 0..{}",
4976                        experts.bank.expert_count
4977                    )
4978                    .into());
4979                }
4980                let down = if self.native_p2p {
4981                    self.run_tensor_parallel_expert_native(&experts.bank, expert, input_row)?
4982                } else {
4983                    let gate =
4984                        self.run_column_bank_expert(&experts.bank.gate, expert, input_row)?;
4985                    let up = self.run_column_bank_expert(&experts.bank.up, expert, input_row)?;
4986                    let activated: Vec<f32> = gate
4987                        .iter()
4988                        .zip(&up)
4989                        .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
4990                        .collect();
4991                    debug_assert_eq!(activated.len(), experts.bank.expert_width);
4992                    self.run_row_bank_expert(&experts.bank.down, expert, &activated)?
4993                };
4994                let weight = route_weights[pair];
4995                for (sum, value) in output
4996                    [token * experts.bank.input_width..(token + 1) * experts.bank.input_width]
4997                    .iter_mut()
4998                    .zip(down)
4999                {
5000                    *sum += weight * value;
5001                }
5002            }
5003        }
5004        Ok(output)
5005    }
5006
5007    fn run_column_bank_expert(
5008        &self,
5009        ranks: &[ResidentE4m3ExpertBankRank],
5010        expert: usize,
5011        input: &[f32],
5012    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5013        let local_out = ranks
5014            .first()
5015            .ok_or("TP column bank has no ranks")?
5016            .out_features;
5017        let mut gathered = vec![0.0f32; local_out * ranks.len()];
5018        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5019            let shard = run_resident_bank_expert(engine, bank, expert, input, 1)?;
5020            gathered[rank * local_out..(rank + 1) * local_out].copy_from_slice(&shard);
5021        }
5022        Ok(gathered)
5023    }
5024
5025    fn run_row_bank_expert(
5026        &self,
5027        ranks: &[ResidentE4m3ExpertBankRank],
5028        expert: usize,
5029        input: &[f32],
5030    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5031        let local_in = ranks.first().ok_or("TP row bank has no ranks")?.in_features;
5032        if input.len() != local_in * ranks.len() {
5033            return Err(format!(
5034                "TP row input {} != {} ranks x {local_in}",
5035                input.len(),
5036                ranks.len()
5037            )
5038            .into());
5039        }
5040        let out_features = ranks[0].out_features;
5041        let mut reduced = vec![0.0f32; out_features];
5042        for (rank, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
5043            let blocks = bank
5044                .k_blocks
5045                .ok_or("TP row bank is not packed in native K-block order")?;
5046            if blocks * FP8_BLOCK != local_in {
5047                return Err(format!(
5048                    "TP row bank has {blocks} blocks but local input width is {local_in}"
5049                )
5050                .into());
5051            }
5052            for block in 0..blocks {
5053                let global_start = rank * local_in + block * FP8_BLOCK;
5054                let partial = run_resident_bank_expert_block(
5055                    engine,
5056                    bank,
5057                    expert,
5058                    block,
5059                    &input[global_start..global_start + FP8_BLOCK],
5060                )?;
5061                for (sum, value) in reduced.iter_mut().zip(partial) {
5062                    *sum += value;
5063                }
5064            }
5065        }
5066        Ok(reduced)
5067    }
5068
5069    fn run_tensor_parallel_expert_native(
5070        &self,
5071        bank: &ResidentTpExpertBank,
5072        expert: usize,
5073        input: &[f32],
5074    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5075        if !self.native_p2p || self.ranks.len() < 2 {
5076            return Err("native TP expert execution requires at least two P2P ranks".into());
5077        }
5078        let local_out = bank
5079            .gate
5080            .first()
5081            .ok_or("native TP gate bank has no ranks")?
5082            .out_features;
5083        if local_out * self.ranks.len() != bank.expert_width {
5084            return Err(format!(
5085                "native TP gate shards {}x{local_out} != expert width {}",
5086                self.ranks.len(),
5087                bank.expert_width
5088            )
5089            .into());
5090        }
5091
5092        // The caller's routed input is already host-canonical. Upload once on rank zero, then
5093        // broadcast over peer copies so no other rank receives a host-staged duplicate.
5094        let mut rank_inputs = Vec::with_capacity(self.ranks.len());
5095        let root_input = {
5096            let root = &self.ranks[0];
5097            let _main = root.gpu.enter_main()?;
5098            root.htod(input)?
5099        };
5100        rank_inputs.push(root_input);
5101        for engine in &self.ranks[1..] {
5102            let peer_input = {
5103                let _main = engine.gpu.enter_main()?;
5104                let mut peer_input = engine.uninit(input.len())?;
5105                engine
5106                    .stream()
5107                    .memcpy_dtod(&rank_inputs[0], &mut peer_input)?;
5108                peer_input
5109            };
5110            rank_inputs.push(peer_input);
5111        }
5112
5113        let mut gate_shards = Vec::with_capacity(self.ranks.len());
5114        let mut up_shards = Vec::with_capacity(self.ranks.len());
5115        for rank in 0..self.ranks.len() {
5116            gate_shards.push(run_resident_bank_expert_device(
5117                &self.ranks[rank],
5118                &bank.gate[rank],
5119                expert,
5120                &rank_inputs[rank],
5121                1,
5122            )?);
5123            up_shards.push(run_resident_bank_expert_device(
5124                &self.ranks[rank],
5125                &bank.up[rank],
5126                expert,
5127                &rank_inputs[rank],
5128                1,
5129            )?);
5130        }
5131
5132        // Preserve the established canonical activation program for the first native transport
5133        // milestone. The shards move to rank zero over P2P; only the scalar activation expression
5134        // executes on host. A later device-activation increment must earn its own exactness gate.
5135        let gate = self.gather_native_column_shards(&gate_shards, 1, local_out)?;
5136        let up = self.gather_native_column_shards(&up_shards, 1, local_out)?;
5137        let activated = gate
5138            .iter()
5139            .zip(&up)
5140            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
5141            .collect::<Vec<_>>();
5142        debug_assert_eq!(activated.len(), bank.expert_width);
5143
5144        let root_activated = {
5145            let root = &self.ranks[0];
5146            let _main = root.gpu.enter_main()?;
5147            root.htod(&activated)?
5148        };
5149        let mut rank_activated = Vec::with_capacity(self.ranks.len());
5150        for (rank, engine) in self.ranks.iter().enumerate() {
5151            let start = rank * local_out;
5152            let source = root_activated.slice(start..start + local_out);
5153            let local = {
5154                let _main = engine.gpu.enter_main()?;
5155                let mut local = engine.uninit(local_out)?;
5156                engine.stream().memcpy_dtod(&source, &mut local)?;
5157                local
5158            };
5159            rank_activated.push(local);
5160        }
5161
5162        let out_features = bank
5163            .down
5164            .first()
5165            .ok_or("native TP down bank has no ranks")?
5166            .out_features;
5167        let mut reduced = {
5168            let root = &self.ranks[0];
5169            let _main = root.gpu.enter_main()?;
5170            root.htod(&vec![0.0f32; out_features])?
5171        };
5172        let mut remote_partial_keepalive = Vec::new();
5173        for rank in 0..self.ranks.len() {
5174            let down = &bank.down[rank];
5175            let blocks = down
5176                .k_blocks
5177                .ok_or("native TP row bank is not packed in checkpoint-block order")?;
5178            if blocks * FP8_BLOCK != local_out {
5179                return Err(format!(
5180                    "native TP rank {rank} has {blocks} blocks but local activation width is \
5181                     {local_out}"
5182                )
5183                .into());
5184            }
5185            for block in 0..blocks {
5186                let start = block * FP8_BLOCK;
5187                let input_block = rank_activated[rank].slice(start..start + FP8_BLOCK);
5188                let partial = run_resident_bank_expert_block_device(
5189                    &self.ranks[rank],
5190                    down,
5191                    expert,
5192                    block,
5193                    &input_block,
5194                )?;
5195                let root_partial = if rank == 0 {
5196                    partial
5197                } else {
5198                    let root = &self.ranks[0];
5199                    let _main = root.gpu.enter_main()?;
5200                    let mut peer_partial = root.uninit(out_features)?;
5201                    root.stream().memcpy_dtod(&partial, &mut peer_partial)?;
5202                    remote_partial_keepalive.push(partial);
5203                    peer_partial
5204                };
5205                let next = {
5206                    let root = &self.ranks[0];
5207                    let _main = root.gpu.enter_main()?;
5208                    let mut next = root.uninit(out_features)?;
5209                    root.add(&reduced, &root_partial, &mut next, out_features)?;
5210                    next
5211                };
5212                reduced = next;
5213            }
5214        }
5215        let output = {
5216            let root = &self.ranks[0];
5217            let _main = root.gpu.enter_main()?;
5218            root.dtoh(&reduced)?
5219        };
5220        drop(remote_partial_keepalive);
5221        Ok(output)
5222    }
5223
5224    /// Gather token-major rank-local columns into one canonical root-device matrix.
5225    pub fn gather_native_column_shards_device(
5226        &self,
5227        shards: &[CudaSlice<f32>],
5228        tokens: usize,
5229        local_out: usize,
5230    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5231        let shard_len = tokens
5232            .checked_mul(local_out)
5233            .ok_or("native TP gather shard size overflow")?;
5234        if shards.len() != self.ranks.len() || shards.iter().any(|shard| shard.len() != shard_len) {
5235            return Err("native TP gather shard geometry mismatch".into());
5236        }
5237        // PRODUCER FENCE (2026-08-20 flake fix): the root stream peer-reads shards produced on
5238        // the other ranks' streams; without fencing those producers the copy can read a partial
5239        // kernel output.
5240        for engine in &self.ranks[1..] {
5241            let _main = engine.gpu.enter_main()?;
5242            engine.stream().synchronize()?;
5243        }
5244        let root = &self.ranks[0];
5245        let _main = root.gpu.enter_main()?;
5246        let global_out = shards
5247            .len()
5248            .checked_mul(local_out)
5249            .ok_or("native TP gather output width overflow")?;
5250        let gathered_len = tokens
5251            .checked_mul(global_out)
5252            .ok_or("native TP gather output size overflow")?;
5253        let mut gathered = root.uninit(gathered_len)?;
5254        if self.bulk_p2p {
5255            root.place_rows_strided(&shards[0], &mut gathered, local_out, tokens, global_out, 0)?;
5256            if shards.len() > 1 {
5257                let mut staging = root.uninit(shard_len)?;
5258                for (rank, shard) in shards.iter().enumerate().skip(1) {
5259                    root.stream().memcpy_dtod(shard, &mut staging)?;
5260                    root.place_rows_strided(
5261                        &staging,
5262                        &mut gathered,
5263                        local_out,
5264                        tokens,
5265                        global_out,
5266                        rank * local_out,
5267                    )?;
5268                }
5269            }
5270        } else {
5271            for token in 0..tokens {
5272                for (rank, shard) in shards.iter().enumerate() {
5273                    let source = shard.slice(token * local_out..(token + 1) * local_out);
5274                    let start = token * global_out + rank * local_out;
5275                    let mut destination = gathered.slice_mut(start..start + local_out);
5276                    root.stream().memcpy_dtod(&source, &mut destination)?;
5277                }
5278            }
5279        }
5280        Ok(gathered)
5281    }
5282
5283    pub fn gather_native_column_shards(
5284        &self,
5285        shards: &[CudaSlice<f32>],
5286        tokens: usize,
5287        local_out: usize,
5288    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
5289        let gathered = self.gather_native_column_shards_device(shards, tokens, local_out)?;
5290        let root = &self.ranks[0];
5291        let _main = root.gpu.enter_main()?;
5292        root.dtoh(&gathered)
5293    }
5294
5295    pub(crate) fn decode_v2_workspace(&self) -> &std::sync::Mutex<Vec<StepTpDecodeV2Ws>> {
5296        &self.decode_v2
5297    }
5298
5299    /// Build the v2 decode-attention workspace for this layer's geometry on first use, or
5300    /// return the index of the matching one. Attention geometry varies across the trunk
5301    /// (per-layer query-head counts), so workspaces are keyed by their geometry pins — a
5302    /// handful exist per model, never one per layer.
5303    ///
5304    /// Refuses non-F32-resident projections: the v2 driver's bit-exactness claim against v1
5305    /// holds per residency class, and only the mirror class has no per-call weight expansion
5306    /// to hide allocation churn behind.
5307    pub(crate) fn decode_v2_ensure(
5308        &self,
5309        e: &Engine,
5310        q_m: &ResidentBf16ColumnParallel,
5311        k_m: &ResidentBf16ColumnParallel,
5312        v_m: &ResidentBf16ColumnParallel,
5313        o_m: &ResidentStepBf16RowParallel,
5314        heads: usize,
5315    ) -> Result<usize, Box<dyn std::error::Error>> {
5316        if self.ranks.len() > 1 && !self.native_p2p {
5317            return Err("step TP decode v2 requires native P2P ranks".into());
5318        }
5319        let ranks = self.ranks.len();
5320        // Residency contract: the canonical-chunk (non-fused) program needs the F32 mirror;
5321        // the fused-kernel door also reads raw checkpoint bf16 directly (halving the weight
5322        // traffic), so bf16 residency is accepted when that door is on.
5323        let fused_door = step_tp_qkv_fused_enabled()?;
5324        let arm_ok = |weight: &ResidentBf16Weight| match weight {
5325            ResidentBf16Weight::F32(_) => true,
5326            ResidentBf16Weight::Bf16(_) => fused_door,
5327        };
5328        for matrix in [q_m, k_m, v_m] {
5329            validate_resident_bf16_ranks(&self.ranks, &matrix.ranks)?;
5330            if matrix.out_features % ranks != 0 || matrix.in_features != q_m.in_features {
5331                return Err("step TP decode v2 QKV geometry mismatch".into());
5332            }
5333            for rank in &matrix.ranks {
5334                if !arm_ok(&rank.weight) {
5335                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5336                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5337                        .into());
5338                }
5339            }
5340        }
5341        validate_step_bf16_row_residency(&self.ranks, o_m)?;
5342        for blocks in &o_m.ranks {
5343            for block in blocks {
5344                if !arm_ok(&block.weight) {
5345                    return Err("step TP decode v2 requires MEMRA_STEP_TP_F32_MIRROR=1 or \
5346                                MEMRA_STEP_TP_QKV_FUSED=1 (bf16-resident fused kernels)"
5347                        .into());
5348                }
5349            }
5350        }
5351        if v_m.out_features != k_m.out_features
5352            || o_m.in_features != q_m.out_features
5353            || heads == 0
5354            || heads % ranks != 0
5355        {
5356            return Err("step TP decode v2 K/V/O geometry mismatch".into());
5357        }
5358        let local_q_dim = q_m.out_features / ranks;
5359        let local_kv_dim = k_m.out_features / ranks;
5360        let o_out = o_m.out_features;
5361        let o_block_cols = o_m.canonical_chunk_cols;
5362        let blocks_per_rank = o_m.ranks.first().map(Vec::len).unwrap_or(0);
5363        if blocks_per_rank == 0
5364            || o_m
5365                .ranks
5366                .iter()
5367                .any(|blocks| blocks.len() != blocks_per_rank)
5368            || blocks_per_rank * o_block_cols * ranks != o_m.in_features
5369        {
5370            return Err("step TP decode v2 O canonical block grid mismatch".into());
5371        }
5372
5373        let mut guard = self
5374            .decode_v2
5375            .lock()
5376            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5377        if let Some(index) = guard.iter().position(|ws| {
5378            ws.local_q_dim == local_q_dim
5379                && ws.local_kv_dim == local_kv_dim
5380                && ws.heads == heads
5381                && ws.o_out == o_out
5382                && ws.o_block_cols == o_block_cols
5383                && ws.blocks_per_rank == blocks_per_rank
5384                && ws.e_device == e.ctx().ordinal()
5385                && ws.q.len() == ranks
5386        }) {
5387            return Ok(index);
5388        }
5389
5390        let mut q_raw = Vec::with_capacity(ranks);
5391        let mut k_raw = Vec::with_capacity(ranks);
5392        let mut v_raw = Vec::with_capacity(ranks);
5393        let mut q = Vec::with_capacity(ranks);
5394        let mut k = Vec::with_capacity(ranks);
5395        let mut pos = Vec::with_capacity(ranks);
5396        let mut gate = Vec::with_capacity(ranks);
5397        let mut attn_out = Vec::with_capacity(ranks);
5398        let mut gated = Vec::with_capacity(ranks);
5399        let mut fuse_ctr = Vec::with_capacity(ranks);
5400        let mut o_partials = Vec::with_capacity(ranks);
5401        let mut ev_rank = Vec::with_capacity(ranks);
5402        let direct_join = oproj_direct_on();
5403        for (rank, engine) in self.ranks.iter().enumerate() {
5404            let _main = engine.gpu.enter_main()?;
5405            q_raw.push(engine.uninit(local_q_dim)?);
5406            k_raw.push(engine.uninit(local_kv_dim)?);
5407            v_raw.push(engine.uninit(local_kv_dim)?);
5408            q.push(engine.uninit(local_q_dim)?);
5409            k.push(engine.uninit(local_kv_dim)?);
5410            pos.push(engine.htod_i32(&[0])?);
5411            fuse_ctr.push(engine.stream().clone_htod(&[0u32])?);
5412            gate.push(engine.uninit(heads / ranks)?);
5413            attn_out.push(engine.uninit(local_q_dim)?);
5414            gated.push(engine.uninit(local_q_dim)?);
5415            let mut rank_partials = Vec::with_capacity(blocks_per_rank);
5416            for _ in 0..blocks_per_rank {
5417                // Direct join: peer ranks' partials live on ROOT so the b4 kernel's
5418                // stores land there over P2P (UVA) and no pull copy is needed.
5419                if direct_join && rank != 0 {
5420                    let root = &self.ranks[0];
5421                    let _root_main = root.gpu.enter_main()?;
5422                    rank_partials.push(root.uninit(o_out)?);
5423                } else {
5424                    rank_partials.push(engine.uninit(o_out)?);
5425                }
5426            }
5427            o_partials.push(rank_partials);
5428            ev_rank.push(engine.ctx().new_event(None)?);
5429        }
5430        let root = &self.ranks[0];
5431        let (peer_partial, reduce_a, reduce_b, zeros, k_shadow, v_shadow, ev_refresh, ev_oproj) = {
5432            let _main = root.gpu.enter_main()?;
5433            (
5434                root.uninit(o_out)?,
5435                root.uninit(o_out)?,
5436                root.uninit(o_out)?,
5437                root.htod(&vec![0.0f32; o_out])?,
5438                root.uninit(ranks * local_kv_dim)?,
5439                root.uninit(ranks * local_kv_dim)?,
5440                root.ctx().new_event(None)?,
5441                root.ctx().new_event(None)?,
5442            )
5443        };
5444        let (gate_e, ev_entry) = {
5445            let _main = e.gpu.enter_main()?;
5446            (e.uninit(heads)?, e.ctx().new_event(None)?)
5447        };
5448        let raw_attn_in = Vec::new();
5449        let raw_pos = Vec::new();
5450        guard.push(StepTpDecodeV2Ws {
5451            tcol_q: Vec::new(),
5452            tcol_k: Vec::new(),
5453            tcol_v: Vec::new(),
5454            tcol_g: Vec::new(),
5455            tcol_in: Vec::new(),
5456            tcol_cap: 0,
5457            fa2_q: Vec::new(),
5458            fa2_gate: Vec::new(),
5459            fa2_gated: Vec::new(),
5460            fa2_cap: 0,
5461            rope_k_t: Vec::new(),
5462            rope_ctr_t: Vec::new(),
5463            rope_pos_t: Vec::new(),
5464            rows_tabs: Vec::new(),
5465            tcol_gated: Vec::new(),
5466            tcol_opart: Vec::new(),
5467            tcol_opeer: None,
5468            tcol_omix: None,
5469            tcol_ocap: 0,
5470            q_raw,
5471            k_raw,
5472            v_raw,
5473            q,
5474            k,
5475            pos,
5476            fuse_ctr,
5477            gate,
5478            attn_out,
5479            gated,
5480            o_partials,
5481            ev_rank,
5482            peer_partial,
5483            reduce_a,
5484            reduce_b,
5485            zeros,
5486            k_shadow,
5487            v_shadow,
5488            ev_refresh,
5489            ev_oproj,
5490            gate_e,
5491            attn_in: Vec::new(),
5492            h_stage: None,
5493            pos_stage: None,
5494            raw_h_stage: 0,
5495            raw_pos_stage: 0,
5496            raw_attn_in,
5497            raw_pos,
5498            raw_o_partial1: 0,
5499            raw_peer_partial: 0,
5500            raw_k1: 0,
5501            raw_v1: 0,
5502            raw_k_shadow: 0,
5503            raw_v_shadow: 0,
5504            raw_mixed_stage_e: 0,
5505            raw_reduce_a: 0,
5506            raw_shadow_stage_e: (0, 0),
5507            ev_entry,
5508            e_device: e.ctx().ordinal(),
5509            local_q_dim,
5510            local_kv_dim,
5511            heads,
5512            o_out,
5513            o_block_cols,
5514            blocks_per_rank,
5515        });
5516        eprintln!(
5517            "[step-tp-decode-v2] workspace ranks={ranks} local_q={local_q_dim} \
5518             local_kv={local_kv_dim} heads={heads} o_blocks={blocks_per_rank}x{o_block_cols} \
5519             residency=persistent ordering=evented performance_claim=false"
5520        );
5521        Ok(guard.len() - 1)
5522    }
5523
5524    /// v2 phase 1: replicate the layer input, project QKV, norm, rope, and stage the gate —
5525    /// all into the persistent workspace, ordered by events instead of host syncs.
5526    ///
5527    /// The caller must have queued every producer of `h`, `pos_d`, and `gate_raw` on `e`'s
5528    /// stream BEFORE this call: `ev_entry` is recorded once here and every rank stream waits
5529    /// on it (the entry fence also guards workspace reuse across layers — any consumer of the
5530    /// previous layer's outputs was queued on `e`'s stream before this record).
5531    #[allow(clippy::too_many_arguments)]
5532    /// T-COLUMN verify precompute (spec MTP): stage T input rows to every rank and run the
5533    /// weight-amortized qkvg_tcol per rank into the ws slabs. Rope/norm/append stay per
5534    /// column in the unmodified t=1 program (defer_norm_rope contract). Bit-exact per
5535    /// column vs the t=1 kernel by construction.
5536    #[allow(clippy::too_many_arguments)]
5537    pub fn decode_v2_input_qkv_tcol(
5538        &self,
5539        ws_index: usize,
5540        e: &Engine,
5541        h_t: &CudaSlice<f32>,
5542        t: usize,
5543        q_m: &ResidentBf16ColumnParallel,
5544        k_m: &ResidentBf16ColumnParallel,
5545        v_m: &ResidentBf16ColumnParallel,
5546        gate_shards: Option<StepTpGateShards<'_>>,
5547    ) -> Result<(), Box<dyn std::error::Error>> {
5548        let ranks = self.ranks.len();
5549        let mut guard = self
5550            .decode_v2
5551            .lock()
5552            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5553        let ws = guard
5554            .get_mut(ws_index)
5555            .ok_or("step TP decode v2 workspace index out of range")?;
5556        let in_f = q_m.in_features;
5557        if h_t.len() < t * in_f || t == 0 || t > 32 {
5558            return Err("decode_v2_input_qkv_tcol geometry".into());
5559        }
5560        // Lazily arm the slabs to capacity.
5561        if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5562            ws.tcol_q.clear();
5563            ws.tcol_k.clear();
5564            ws.tcol_v.clear();
5565            ws.tcol_g.clear();
5566            ws.tcol_in.clear();
5567            for engine in &self.ranks {
5568                let _m = engine.gpu.enter_main()?;
5569                ws.tcol_q.push(engine.uninit(32 * ws.local_q_dim)?);
5570                ws.tcol_k.push(engine.uninit(32 * ws.local_kv_dim)?);
5571                ws.tcol_v.push(engine.uninit(32 * ws.local_kv_dim)?);
5572                ws.tcol_g
5573                    .push(engine.uninit(32 * (ws.heads / ranks).max(1))?);
5574                ws.tcol_in.push(engine.uninit(32 * in_f)?);
5575            }
5576            ws.tcol_cap = 32;
5577        }
5578        // Stage the T input rows on e, fence, per-rank pull + tcol launch.
5579        use cudarc::driver::DevicePtr;
5580        let raw_src = {
5581            let _main = e.gpu.enter_main()?;
5582            let stream = e.stream();
5583            let (p, _g) = h_t.device_ptr(&stream);
5584            ws.ev_entry.record(&stream)?;
5585            p as u64
5586        };
5587        for rank in 0..ranks {
5588            let engine = &self.ranks[rank];
5589            let _main = engine.gpu.enter_main()?;
5590            engine.stream().wait(&ws.ev_entry)?;
5591            let raw_dst = {
5592                let stream = engine.stream();
5593                let (p, _g) = ws.tcol_in[rank].device_ptr(&stream);
5594                p as u64
5595            };
5596            raw_copy_bytes(raw_dst, raw_src, t * in_f * 4, engine)?;
5597            let out_g = match &gate_shards {
5598                Some(_) => ws.heads / ranks,
5599                None => 0,
5600            };
5601            match (
5602                &q_m.ranks[rank].weight,
5603                &k_m.ranks[rank].weight,
5604                &v_m.ranks[rank].weight,
5605            ) {
5606                (
5607                    ResidentBf16Weight::Bf16(wq),
5608                    ResidentBf16Weight::Bf16(wk),
5609                    ResidentBf16Weight::Bf16(wv),
5610                ) => {
5611                    let wg = match &gate_shards {
5612                        Some(StepTpGateShards::Bf16(shards)) => &shards[rank],
5613                        Some(StepTpGateShards::F32(_)) => {
5614                            return Err(
5615                                "tcol verify: gate shard class does not match bf16 QKV".into()
5616                            );
5617                        }
5618                        None => wq,
5619                    };
5620                    let StepTpDecodeV2Ws {
5621                        tcol_q,
5622                        tcol_k,
5623                        tcol_v,
5624                        tcol_g,
5625                        tcol_in,
5626                        local_q_dim,
5627                        local_kv_dim,
5628                        ..
5629                    } = &mut *ws;
5630                    // MEMRA_TCOL_REFKERN=1 (bisect): fill the slabs via the t=1 kernel per
5631                    // column — separates driver bugs from tcol-kernel bugs.
5632                    static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5633                    let refk = *REFK
5634                        .get_or_init(|| std::env::var("MEMRA_TCOL_REFKERN").as_deref() == Ok("1"));
5635                    if refk {
5636                        let lq = *local_q_dim;
5637                        let lkv = *local_kv_dim;
5638                        let mut hrow = engine.uninit(in_f)?;
5639                        let mut qr = engine.uninit(lq)?;
5640                        let mut kr = engine.uninit(lkv)?;
5641                        let mut vr = engine.uninit(lkv)?;
5642                        let mut gr = engine.uninit(out_g.max(1))?;
5643                        for c in 0..t {
5644                            {
5645                                let mut dst = hrow.slice_mut(0..in_f);
5646                                engine.stream().memcpy_dtod(
5647                                    &tcol_in[rank].slice(c * in_f..(c + 1) * in_f),
5648                                    &mut dst,
5649                                )?;
5650                            }
5651                            engine.matvec_bf16_qkvg_into(
5652                                wq, wk, wv, wg, &hrow, &mut qr, &mut kr, &mut vr, &mut gr, in_f,
5653                                lq, lkv, out_g,
5654                            )?;
5655                            let stream = engine.stream();
5656                            {
5657                                let mut dst = tcol_q[rank].slice_mut(c * lq..(c + 1) * lq);
5658                                stream.memcpy_dtod(&qr.slice(0..lq), &mut dst)?;
5659                            }
5660                            {
5661                                let mut dst = tcol_k[rank].slice_mut(c * lkv..(c + 1) * lkv);
5662                                stream.memcpy_dtod(&kr.slice(0..lkv), &mut dst)?;
5663                            }
5664                            {
5665                                let mut dst = tcol_v[rank].slice_mut(c * lkv..(c + 1) * lkv);
5666                                stream.memcpy_dtod(&vr.slice(0..lkv), &mut dst)?;
5667                            }
5668                            if out_g > 0 {
5669                                let mut dst = tcol_g[rank].slice_mut(c * out_g..(c + 1) * out_g);
5670                                stream.memcpy_dtod(&gr.slice(0..out_g), &mut dst)?;
5671                            }
5672                        }
5673                    } else {
5674                        engine.matvec_bf16_qkvg_tcol_into(
5675                            wq,
5676                            wk,
5677                            wv,
5678                            wg,
5679                            &tcol_in[rank],
5680                            &mut tcol_q[rank],
5681                            &mut tcol_k[rank],
5682                            &mut tcol_v[rank],
5683                            &mut tcol_g[rank],
5684                            in_f,
5685                            *local_q_dim,
5686                            *local_kv_dim,
5687                            out_g,
5688                            t,
5689                        )?;
5690                    }
5691                }
5692                _ => return Err("tcol verify requires bf16-resident fused QKV".into()),
5693            }
5694        }
5695        Ok(())
5696    }
5697
5698    /// MEMRA_TCOL_OPROJ eligibility: the defer replaces exactly the o_fused direct-join
5699    /// finish (bf16 b4 kernel, 2 ranks, 4 canonical blocks) with the shadow gathers
5700    /// skipped — so it requires the same doors that arm dictate that finish shape.
5701    pub(crate) fn decode_v2_oproj_tcol_eligible(
5702        &self,
5703        ws: &StepTpDecodeV2Ws,
5704        o_m: &ResidentStepBf16RowParallel,
5705    ) -> bool {
5706        self.ranks.len() == 2
5707            && ws.blocks_per_rank == 4
5708            && step_tp_qkv_fused_enabled().unwrap_or(false)
5709            && no_local_shadow_on()
5710            && std::env::var("MEMRA_B4_X2").as_deref() != Ok("1")
5711            && o_m
5712                .ranks
5713                .iter()
5714                .flatten()
5715                .all(|block| matches!(block.weight, ResidentBf16Weight::Bf16(_)))
5716    }
5717
5718    /// MEMRA_SPEC_FA2 stash: copy this column's per-rank post-rope q and gate rows into
5719    /// the fa2 slabs (rank-stream ordered behind the rope/append that produced them), and
5720    /// give `e` the same anti-dependency wait the skipped finish provided (next column's
5721    /// h/pos re-staging must not overtake this column's rank pulls).
5722    pub(crate) fn decode_v2_stash_fa2(
5723        &self,
5724        ws: &mut StepTpDecodeV2Ws,
5725        e: &Engine,
5726        col: usize,
5727    ) -> Result<(), Box<dyn std::error::Error>> {
5728        let ranks = self.ranks.len();
5729        if col >= 32 {
5730            return Err("decode_v2_stash_fa2 column out of range".into());
5731        }
5732        let lq = ws.local_q_dim;
5733        let lg = (ws.heads / ranks).max(1);
5734        if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks {
5735            ws.fa2_q.clear();
5736            ws.fa2_gate.clear();
5737            ws.fa2_gated.clear();
5738            ws.rope_k_t.clear();
5739            ws.rope_ctr_t.clear();
5740            ws.rope_pos_t.clear();
5741            for engine in &self.ranks {
5742                let _m = engine.gpu.enter_main()?;
5743                ws.fa2_q.push(engine.uninit(32 * lq)?);
5744                ws.fa2_gate.push(engine.uninit(32 * lg)?);
5745                ws.fa2_gated.push(engine.uninit(32 * lq)?);
5746                ws.rope_k_t.push(engine.uninit(32 * ws.local_kv_dim)?);
5747                ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
5748                ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
5749            }
5750            ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
5751            ws.fa2_cap = 32;
5752        }
5753        for rank in 0..ranks {
5754            let engine = &self.ranks[rank];
5755            let _main = engine.gpu.enter_main()?;
5756            {
5757                let mut dst = ws.fa2_q[rank].slice_mut(col * lq..(col + 1) * lq);
5758                engine
5759                    .stream()
5760                    .memcpy_dtod(&ws.q[rank].slice(0..lq), &mut dst)?;
5761            }
5762            {
5763                let mut dst = ws.fa2_gate[rank].slice_mut(col * lg..(col + 1) * lg);
5764                engine
5765                    .stream()
5766                    .memcpy_dtod(&ws.gate[rank].slice(0..lg), &mut dst)?;
5767            }
5768            ws.ev_rank[rank].record(&engine.stream())?;
5769        }
5770        {
5771            let _main = e.gpu.enter_main()?;
5772            for ev in ws.ev_rank.iter() {
5773                e.stream().wait(ev)?;
5774            }
5775        }
5776        Ok(())
5777    }
5778
5779    /// MEMRA_SPEC_FA2 join: after BOTH verify columns stashed (their appends landed in
5780    /// rank-stream order), run ONE fa_decode_dcw2 per rank over the shared KV stream —
5781    /// two query rows, per-row causal bounds, per-row combine+gate — then land the two
5782    /// gated rows in the o-tcol slabs and reuse the weight-amortized o_proj join.
5783    /// Returns the [2, o_out] `mixed` slab on `e`. The caller's precheck enforced the
5784    /// equal-partition guard (boundary rounds never arm the defer).
5785    #[allow(clippy::too_many_arguments)]
5786    pub(crate) fn decode_v2_spec_fa2_join(
5787        &self,
5788        ws_index: usize,
5789        e: &Engine,
5790        o_m: &ResidentStepBf16RowParallel,
5791        kv: &ResidentTpKvCache,
5792        head_dim: usize,
5793        window: usize,
5794        bucket_max: usize,
5795        scale: f32,
5796    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5797        let ranks = self.ranks.len();
5798        // Engagement receipt: a vacuous gate (precheck never passing) must be visible.
5799        static ONCE: std::sync::Once = std::sync::Once::new();
5800        ONCE.call_once(|| eprintln!("[spec-fa2] joined T=2 attention ENGAGED"));
5801        {
5802            let mut guard = self
5803                .decode_v2
5804                .lock()
5805                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5806            let ws = guard
5807                .get_mut(ws_index)
5808                .ok_or("step TP decode v2 workspace index out of range")?;
5809            if ws.fa2_cap < 2 || ws.fa2_q.len() != ranks {
5810                return Err("spec fa2 join without stashed columns".into());
5811            }
5812            let lq = ws.local_q_dim;
5813            let local_heads = (ws.heads / ranks).max(1);
5814            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
5815            let capacity = kv.physical_capacity();
5816            let (k_tok_bytes, v_tok_bytes) = (kv.k_tok_bytes(), kv.v_tok_bytes());
5817            // Arm the o-tcol slabs if the oproj door never ran this boot (same shapes).
5818            if ws.tcol_ocap < 2 || ws.tcol_gated.len() != ranks {
5819                ws.tcol_gated.clear();
5820                ws.tcol_opart.clear();
5821                for engine in &self.ranks {
5822                    let _m = engine.gpu.enter_main()?;
5823                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
5824                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
5825                }
5826                let root = &self.ranks[0];
5827                let _m = root.gpu.enter_main()?;
5828                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
5829                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
5830                ws.tcol_ocap = 32;
5831            }
5832            for rank in 0..ranks {
5833                let engine = &self.ranks[rank];
5834                let _main = engine.gpu.enter_main()?;
5835                let rank_cache = kv
5836                    .rank(rank)
5837                    .ok_or("spec fa2 join lost its KV cache rank")?;
5838                let k_ring = engine.view_u8_range(rank_cache.k(), 0, capacity * k_tok_bytes);
5839                let v_ring = engine.view_u8_range(rank_cache.v(), 0, capacity * v_tok_bytes);
5840                {
5841                    let StepTpDecodeV2Ws {
5842                        fa2_q,
5843                        fa2_gate,
5844                        fa2_gated,
5845                        ..
5846                    } = &mut *ws;
5847                    engine.fa_decode_dcw2(
5848                        &fa2_q[rank],
5849                        &k_ring,
5850                        &v_ring,
5851                        &mut fa2_gated[rank],
5852                        head_dim,
5853                        local_heads,
5854                        local_kv_heads,
5855                        rank_cache.len_d(),
5856                        rank_cache.base_d(),
5857                        window,
5858                        bucket_max,
5859                        scale,
5860                        k_tok_bytes,
5861                        v_tok_bytes,
5862                        &fa2_gate[rank],
5863                    )?;
5864                }
5865                // Both gated rows are contiguous [2, lq] — exactly columns 0..2 of the
5866                // o-tcol slab layout. One dtod, in rank-stream order behind the fa.
5867                let StepTpDecodeV2Ws {
5868                    fa2_gated,
5869                    tcol_gated,
5870                    ..
5871                } = &mut *ws;
5872                let mut dst = tcol_gated[rank].slice_mut(0..2 * lq);
5873                engine
5874                    .stream()
5875                    .memcpy_dtod(&fa2_gated[rank].slice(0..2 * lq), &mut dst)?;
5876            }
5877        }
5878        self.decode_v2_oproj_tcol(ws_index, e, o_m, 2)
5879    }
5880
5881    /// FULL T-ROW ATTENTION PASS over per-row session tables (batched serving): reads
5882    /// the tcol raw-projection slabs, runs ONE rope/append rows launch + ONE fa rows
5883    /// launch + ONE combine per rank (gate straight from the tcol gate slab), then the
5884    /// o_proj tcol join — the whole per-row attention loop in 3 launches/rank/layer.
5885    /// Per-(row, head) programs are the t=1 kernels verbatim; each row appends to and
5886    /// attends its OWN session. `session_parts[rank][row]` = {k_plane, v_plane, len_ptr,
5887    /// base_ptr}; `tab_keys[rank]` keys the per-rank combined-table cache (caller folds
5888    /// layer + session-set + base-arming into it); `stage_pos` stages the position slab
5889    /// (positions are constant across layers within a tick — stage on the first layer).
5890    #[allow(clippy::too_many_arguments)]
5891    pub(crate) fn decode_v2_rope_fa_rows(
5892        &self,
5893        ws_index: usize,
5894        e: &Engine,
5895        o_m: &ResidentStepBf16RowParallel,
5896        session_parts: &[Vec<[u64; 4]>],
5897        tab_keys: &[u64],
5898        positions: &[i32],
5899        stage_pos: bool,
5900        same_session: bool,
5901        q_norms: &[CudaSlice<f32>],
5902        k_norms: &[CudaSlice<f32>],
5903        rope_freqs: &[Option<&crate::CudaSlice<f32>>],
5904        t: usize,
5905        head_dim: usize,
5906        n_rot: usize,
5907        window: usize,
5908        max_ns: usize,
5909        scale: f32,
5910        k_tok_bytes: usize,
5911        v_tok_bytes: usize,
5912        eps: f32,
5913        rope_base: f32,
5914    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
5915        use cudarc::driver::DevicePtr;
5916        let ranks = self.ranks.len();
5917        if session_parts.len() != ranks || tab_keys.len() != ranks || positions.len() < t {
5918            return Err("rope fa rows geometry".into());
5919        }
5920        {
5921            let mut guard = self
5922                .decode_v2
5923                .lock()
5924                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
5925            let ws = guard
5926                .get_mut(ws_index)
5927                .ok_or("step TP decode v2 workspace index out of range")?;
5928            if ws.tcol_cap < t || ws.tcol_q.len() != ranks {
5929                return Err("rope fa rows without tcol slabs".into());
5930            }
5931            let lq = ws.local_q_dim;
5932            let lkv = ws.local_kv_dim;
5933            let lg = (ws.heads / ranks).max(1);
5934            let local_heads = (ws.heads / ranks).max(1);
5935            let local_kv_heads = (lkv / head_dim).max(1);
5936            // Arm the fa2/rope slabs (shared with the stash path).
5937            if ws.fa2_cap < 32 || ws.fa2_q.len() != ranks {
5938                ws.fa2_q.clear();
5939                ws.fa2_gate.clear();
5940                ws.fa2_gated.clear();
5941                ws.rope_k_t.clear();
5942                ws.rope_ctr_t.clear();
5943                ws.rope_pos_t.clear();
5944                for engine in &self.ranks {
5945                    let _m = engine.gpu.enter_main()?;
5946                    ws.fa2_q.push(engine.uninit(32 * lq)?);
5947                    ws.fa2_gate.push(engine.uninit(32 * lg)?);
5948                    ws.fa2_gated.push(engine.uninit(32 * lq)?);
5949                    ws.rope_k_t.push(engine.uninit(32 * lkv)?);
5950                    ws.rope_ctr_t.push(engine.stream().clone_htod(&[0u32; 32])?);
5951                    ws.rope_pos_t.push(engine.htod_i32(&[0i32; 32])?);
5952                }
5953                ws.rows_tabs = (0..ranks).map(|_| Default::default()).collect();
5954                ws.fa2_cap = 32;
5955            }
5956            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
5957                ws.tcol_gated.clear();
5958                ws.tcol_opart.clear();
5959                for engine in &self.ranks {
5960                    let _m = engine.gpu.enter_main()?;
5961                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
5962                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
5963                }
5964                let root = &self.ranks[0];
5965                let _m = root.gpu.enter_main()?;
5966                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
5967                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
5968                ws.tcol_ocap = 32;
5969            }
5970            for rank in 0..ranks {
5971                let engine = &self.ranks[rank];
5972                let _main = engine.gpu.enter_main()?;
5973                if stage_pos {
5974                    let host: Vec<i32> = positions[..t].to_vec();
5975                    let mut view = ws.rope_pos_t[rank].slice_mut(0..t);
5976                    engine.stream().memcpy_htod(&host, &mut view)?;
5977                }
5978                // Combined 6-word table {k, v, len, base, ctr, back=0}; ctr = this
5979                // rank's per-row counter slab.
5980                if !ws.rows_tabs[rank].contains_key(&tab_keys[rank]) {
5981                    let ctr_base = {
5982                        let s = engine.stream();
5983                        let (p, _g) = ws.rope_ctr_t[rank].device_ptr(&s);
5984                        p as u64
5985                    };
5986                    let mut host = Vec::with_capacity(t * 6);
5987                    for (r, parts) in session_parts[rank].iter().enumerate().take(t) {
5988                        host.extend_from_slice(&[
5989                            parts[0],
5990                            parts[1],
5991                            parts[2],
5992                            parts[3],
5993                            if same_session {
5994                                ctr_base
5995                            } else {
5996                                ctr_base + (r as u64) * 4
5997                            },
5998                            if same_session {
5999                                (t - 1 - r) as u64
6000                            } else {
6001                                0u64
6002                            },
6003                        ]);
6004                    }
6005                    let tab = engine.stream().clone_htod(&host)?;
6006                    ws.rows_tabs[rank].insert(tab_keys[rank], tab);
6007                }
6008                let StepTpDecodeV2Ws {
6009                    tcol_q,
6010                    tcol_k,
6011                    tcol_v,
6012                    tcol_g,
6013                    fa2_q,
6014                    fa2_gated,
6015                    rope_k_t,
6016                    rope_pos_t,
6017                    rows_tabs,
6018                    ..
6019                } = &mut *ws;
6020                let tab = rows_tabs[rank]
6021                    .get(&tab_keys[rank])
6022                    .expect("inserted above");
6023                engine.qk_norm_rope_append_inc_dcw_rows(
6024                    &tcol_q[rank],
6025                    &tcol_k[rank],
6026                    &tcol_v[rank],
6027                    &q_norms[rank],
6028                    &k_norms[rank],
6029                    &mut fa2_q[rank],
6030                    &mut rope_k_t[rank],
6031                    tab,
6032                    &rope_pos_t[rank],
6033                    same_session,
6034                    t,
6035                    lkv,
6036                    lkv,
6037                    k_tok_bytes,
6038                    v_tok_bytes,
6039                    head_dim,
6040                    n_rot,
6041                    local_heads,
6042                    local_kv_heads,
6043                    eps,
6044                    rope_base,
6045                    1.0,
6046                    rope_freqs[rank],
6047                )?;
6048                engine.fa_decode_dcw_rows(
6049                    &fa2_q[rank],
6050                    tab,
6051                    &mut fa2_gated[rank],
6052                    t,
6053                    head_dim,
6054                    local_heads,
6055                    local_kv_heads,
6056                    window,
6057                    max_ns,
6058                    scale,
6059                    k_tok_bytes,
6060                    v_tok_bytes,
6061                    &tcol_g[rank],
6062                )?;
6063                let StepTpDecodeV2Ws {
6064                    fa2_gated,
6065                    tcol_gated,
6066                    ..
6067                } = &mut *ws;
6068                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6069                engine
6070                    .stream()
6071                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6072            }
6073        }
6074        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6075    }
6076
6077    /// T-ROW fa join over per-row session tables (the per-session distributed-KV
6078    /// primitive): after all t rows stashed q+gate (their appends landed in rank-stream
6079    /// order), ONE fa_decode_dcw_rows per rank walks every row's own ring with its own
6080    /// geometry — bit-identical per row to its per-row launch — then the o_proj tcol
6081    /// join lands the [t, o_out] `mixed` slab on `e`. `tabs[rank]` is the pre-staged
6082    /// device table on that rank.
6083    #[allow(clippy::too_many_arguments)]
6084    pub(crate) fn decode_v2_fa_rows_join(
6085        &self,
6086        ws_index: usize,
6087        e: &Engine,
6088        o_m: &ResidentStepBf16RowParallel,
6089        tabs: &[&crate::CudaSlice<u64>],
6090        t: usize,
6091        head_dim: usize,
6092        window: usize,
6093        max_ns: usize,
6094        scale: f32,
6095        k_tok_bytes: usize,
6096        v_tok_bytes: usize,
6097    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
6098        let ranks = self.ranks.len();
6099        if tabs.len() != ranks {
6100            return Err("fa rows join needs one table per rank".into());
6101        }
6102        {
6103            let mut guard = self
6104                .decode_v2
6105                .lock()
6106                .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6107            let ws = guard
6108                .get_mut(ws_index)
6109                .ok_or("step TP decode v2 workspace index out of range")?;
6110            if ws.fa2_cap < t || ws.fa2_q.len() != ranks {
6111                return Err("fa rows join without stashed rows".into());
6112            }
6113            let lq = ws.local_q_dim;
6114            let local_heads = (ws.heads / ranks).max(1);
6115            let local_kv_heads = (ws.local_kv_dim / head_dim).max(1);
6116            if ws.tcol_ocap < t || ws.tcol_gated.len() != ranks {
6117                ws.tcol_gated.clear();
6118                ws.tcol_opart.clear();
6119                for engine in &self.ranks {
6120                    let _m = engine.gpu.enter_main()?;
6121                    ws.tcol_gated.push(engine.uninit(32 * lq)?);
6122                    ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6123                }
6124                let root = &self.ranks[0];
6125                let _m = root.gpu.enter_main()?;
6126                ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6127                ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6128                ws.tcol_ocap = 32;
6129            }
6130            for rank in 0..ranks {
6131                let engine = &self.ranks[rank];
6132                let _main = engine.gpu.enter_main()?;
6133                {
6134                    let StepTpDecodeV2Ws {
6135                        fa2_q,
6136                        fa2_gate,
6137                        fa2_gated,
6138                        ..
6139                    } = &mut *ws;
6140                    engine.fa_decode_dcw_rows(
6141                        &fa2_q[rank],
6142                        tabs[rank],
6143                        &mut fa2_gated[rank],
6144                        t,
6145                        head_dim,
6146                        local_heads,
6147                        local_kv_heads,
6148                        window,
6149                        max_ns,
6150                        scale,
6151                        k_tok_bytes,
6152                        v_tok_bytes,
6153                        &fa2_gate[rank],
6154                    )?;
6155                }
6156                let StepTpDecodeV2Ws {
6157                    fa2_gated,
6158                    tcol_gated,
6159                    ..
6160                } = &mut *ws;
6161                let mut dst = tcol_gated[rank].slice_mut(0..t * lq);
6162                engine
6163                    .stream()
6164                    .memcpy_dtod(&fa2_gated[rank].slice(0..t * lq), &mut dst)?;
6165            }
6166        }
6167        self.decode_v2_oproj_tcol(ws_index, e, o_m, t)
6168    }
6169
6170    /// MEMRA_TCOL_OPROJ stash: copy this column's per-rank `gated` rows into the o-tcol
6171    /// slabs (rank-stream ordered behind the attention kernels that produced them). The
6172    /// per-column finish choreography is skipped entirely; `decode_v2_oproj_tcol` joins
6173    /// every column afterwards.
6174    pub(crate) fn decode_v2_stash_gated(
6175        &self,
6176        ws: &mut StepTpDecodeV2Ws,
6177        e: &Engine,
6178        col: usize,
6179    ) -> Result<(), Box<dyn std::error::Error>> {
6180        let ranks = self.ranks.len();
6181        if col >= 8 {
6182            return Err("decode_v2_stash_gated column out of range".into());
6183        }
6184        let lq = ws.local_q_dim;
6185        if ws.tcol_ocap == 0 || ws.tcol_gated.len() != ranks {
6186            ws.tcol_gated.clear();
6187            ws.tcol_opart.clear();
6188            for engine in &self.ranks {
6189                let _m = engine.gpu.enter_main()?;
6190                ws.tcol_gated.push(engine.uninit(32 * lq)?);
6191                ws.tcol_opart.push(engine.uninit(32 * ws.o_out)?);
6192            }
6193            let root = &self.ranks[0];
6194            let _m = root.gpu.enter_main()?;
6195            ws.tcol_opeer = Some(root.uninit(32 * ws.o_out)?);
6196            ws.tcol_omix = Some(root.uninit(32 * ws.o_out)?);
6197            ws.tcol_ocap = 32;
6198        }
6199        for rank in 0..ranks {
6200            let engine = &self.ranks[rank];
6201            let _main = engine.gpu.enter_main()?;
6202            let mut dst = ws.tcol_gated[rank].slice_mut(col * lq..(col + 1) * lq);
6203            engine
6204                .stream()
6205                .memcpy_dtod(&ws.gated[rank].slice(0..lq), &mut dst)?;
6206            // The skipped finish's e-wait was ALSO the anti-dependency guard: it ordered
6207            // e's NEXT column's h/pos re-staging behind this column's rank-side raw pulls.
6208            // Record each rank here and make e wait — same protection, no o_proj work.
6209            ws.ev_rank[rank].record(&engine.stream())?;
6210        }
6211        {
6212            let _main = e.gpu.enter_main()?;
6213            for ev in ws.ev_rank.iter() {
6214                e.stream().wait(ev)?;
6215            }
6216        }
6217        Ok(())
6218    }
6219
6220    /// MEMRA_TCOL_OPROJ join: one weight-amortized b4_tcol per rank over the stashed
6221    /// `gated` slabs (per-column FP order == the t=1 b4 kernel), one peer pull of rank1's
6222    /// partial slab, one elementwise slab add on the root (independent elements — each
6223    /// column's add is the exact direct-join `add(p0, p1)`), then the joined `mixed` slab
6224    /// lands on `e`. Returns [t, o_out] on the model engine.
6225    pub(crate) fn decode_v2_oproj_tcol(
6226        &self,
6227        ws_index: usize,
6228        e: &Engine,
6229        o_m: &ResidentStepBf16RowParallel,
6230        t: usize,
6231    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6232        let ranks = self.ranks.len();
6233        let mut guard = self
6234            .decode_v2
6235            .lock()
6236            .map_err(|_| "step TP decode v2 workspace lock is poisoned")?;
6237        let ws = guard
6238            .get_mut(ws_index)
6239            .ok_or("step TP decode v2 workspace index out of range")?;
6240        if ranks != 2 || ws.blocks_per_rank != 4 || t == 0 || t > 32 || ws.tcol_ocap < t {
6241            return Err("decode_v2_oproj_tcol geometry".into());
6242        }
6243        for rank in 0..ranks {
6244            let engine = &self.ranks[rank];
6245            let _main = engine.gpu.enter_main()?;
6246            let mut weights = Vec::with_capacity(4);
6247            for block in 0..4 {
6248                let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6249                    return Err("tcol o_proj requires bf16-resident O blocks".into());
6250                };
6251                weights.push(weight);
6252            }
6253            {
6254                let StepTpDecodeV2Ws {
6255                    tcol_gated,
6256                    tcol_opart,
6257                    local_q_dim,
6258                    o_block_cols,
6259                    o_out,
6260                    ..
6261                } = &mut *ws;
6262                // MEMRA_TCOL_OPROJ_REF=1 (bisect): fill the partial slab via the t=1 b4
6263                // kernel per column — separates choreography bugs from tcol-kernel bugs.
6264                static REFK: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
6265                let refk = *REFK
6266                    .get_or_init(|| std::env::var("MEMRA_TCOL_OPROJ_REF").as_deref() == Ok("1"));
6267                if refk {
6268                    let lq = *local_q_dim;
6269                    let mut xr = engine.uninit(lq)?;
6270                    let mut yr = engine.uninit(*o_out)?;
6271                    for c in 0..t {
6272                        {
6273                            let mut dst = xr.slice_mut(0..lq);
6274                            engine.stream().memcpy_dtod(
6275                                &tcol_gated[rank].slice(c * lq..(c + 1) * lq),
6276                                &mut dst,
6277                            )?;
6278                        }
6279                        engine.matvec_bf16_b4_into(
6280                            [weights[0], weights[1], weights[2], weights[3]],
6281                            &xr,
6282                            &mut yr,
6283                            *o_block_cols,
6284                            *o_out,
6285                        )?;
6286                        let mut dst = tcol_opart[rank].slice_mut(c * *o_out..(c + 1) * *o_out);
6287                        engine
6288                            .stream()
6289                            .memcpy_dtod(&yr.slice(0..*o_out), &mut dst)?;
6290                    }
6291                } else {
6292                    engine.matvec_bf16_b4_tcol_into(
6293                        [weights[0], weights[1], weights[2], weights[3]],
6294                        &tcol_gated[rank],
6295                        &mut tcol_opart[rank],
6296                        *o_block_cols,
6297                        *o_out,
6298                        t,
6299                    )?;
6300                }
6301            }
6302            if rank != 0 {
6303                ws.ev_rank[rank].record(&engine.stream())?;
6304            }
6305        }
6306        let root = &self.ranks[0];
6307        {
6308            let _main = root.gpu.enter_main()?;
6309            for ev in ws.ev_rank.iter().skip(1) {
6310                root.stream().wait(ev)?;
6311            }
6312            {
6313                let StepTpDecodeV2Ws {
6314                    tcol_opart,
6315                    tcol_opeer,
6316                    tcol_omix,
6317                    o_out,
6318                    ..
6319                } = &mut *ws;
6320                let opeer = tcol_opeer.as_mut().ok_or("tcol o_proj slabs not armed")?;
6321                let omix = tcol_omix.as_mut().ok_or("tcol o_proj slabs not armed")?;
6322                {
6323                    let mut dst = opeer.slice_mut(0..t * *o_out);
6324                    root.stream()
6325                        .memcpy_dtod(&tcol_opart[1].slice(0..t * *o_out), &mut dst)?;
6326                }
6327                // Elementwise over the whole slab: per element identical to the per-column
6328                // direct-join add (independent lanes, same operand values).
6329                root.add(&tcol_opart[0], opeer, omix, t * *o_out)?;
6330            }
6331            ws.ev_oproj.record(&root.stream())?;
6332        }
6333        let _main = e.gpu.enter_main()?;
6334        e.stream().wait(&ws.ev_oproj)?;
6335        let mut out = e.uninit(t * ws.o_out)?;
6336        let omix = ws.tcol_omix.as_ref().ok_or("tcol o_proj slabs not armed")?;
6337        e.stream().memcpy_dtod(
6338            &omix.slice(0..t * ws.o_out),
6339            &mut out.slice_mut(0..t * ws.o_out),
6340        )?;
6341        Ok(out)
6342    }
6343
6344    pub(crate) fn decode_v2_input_qkv(
6345        &self,
6346        ws: &mut StepTpDecodeV2Ws,
6347        e: &Engine,
6348        h: &CudaSlice<f32>,
6349        pos_d: &CudaSlice<i32>,
6350        gate_raw: Option<&CudaSlice<f32>>,
6351        gate_shards: Option<StepTpGateShards<'_>>,
6352        decode_input: &mut ResidentReplicatedDeviceRows,
6353        q_m: &ResidentBf16ColumnParallel,
6354        k_m: &ResidentBf16ColumnParallel,
6355        v_m: &ResidentBf16ColumnParallel,
6356        q_norm: &[CudaSlice<f32>],
6357        k_norm: &[CudaSlice<f32>],
6358        head_dim: usize,
6359        n_rot: usize,
6360        rope_base: f32,
6361        rope_freqs: &[Option<&CudaSlice<f32>>],
6362        rms_eps: f32,
6363        defer_norm_rope: bool,
6364        tcol_col: Option<usize>,
6365    ) -> Result<(), Box<dyn std::error::Error>> {
6366        let ranks = self.ranks.len();
6367        validate_replicated_device_rows(&self.ranks, decode_input)?;
6368        if decode_input.tokens != 1
6369            || decode_input.width != q_m.in_features
6370            || pos_d.len() != 1
6371            || gate_raw.is_some_and(|gate| gate.len() != ws.heads)
6372            || gate_raw.is_none() != gate_shards.is_some()
6373            || gate_shards.as_ref().is_some_and(|shards| match shards {
6374                StepTpGateShards::F32(shards) => shards.len() != ranks,
6375                StepTpGateShards::Bf16(shards) => shards.len() != ranks,
6376            })
6377            || q_norm.len() != ranks
6378            || k_norm.len() != ranks
6379            || rope_freqs.len() != ranks
6380            || e.ctx().ordinal() != ws.e_device
6381        {
6382            return Err("step TP decode v2 input geometry mismatch".into());
6383        }
6384
6385        let qkv_fused = step_tp_qkv_fused_enabled()?;
6386        if gate_shards.is_some() && !qkv_fused {
6387            return Err("step TP decode v2 gate shards require MEMRA_STEP_TP_QKV_FUSED=1".into());
6388        }
6389        let values = decode_input.width;
6390        if h.len() != values {
6391            return Err(format!(
6392                "step TP decode v2 hidden width {} != replicated width {values}",
6393                h.len()
6394            )
6395            .into());
6396        }
6397
6398        if qkv_fused {
6399            // STAGE-BASED flow (graph increment A): h and pos land in fixed e-context stages
6400            // (one e-stream copy each), the entry event covers them, and every rank raw-copies
6401            // from the stages on its own stream — exactly the shape graph capture wraps.
6402            if ws.h_stage.is_none() {
6403                use cudarc::driver::DevicePtr;
6404                let _main = e.gpu.enter_main()?;
6405                let h_stage = e.uninit(values)?;
6406                let pos_stage = e.htod_i32(&[0])?;
6407                {
6408                    let stream = e.stream();
6409                    let (hp, _g0) = h_stage.device_ptr(&stream);
6410                    let (pp, _g1) = pos_stage.device_ptr(&stream);
6411                    ws.raw_h_stage = hp as u64;
6412                    ws.raw_pos_stage = pp as u64;
6413                }
6414                ws.h_stage = Some(h_stage);
6415                ws.pos_stage = Some(pos_stage);
6416                for rank in 0..ranks {
6417                    use cudarc::driver::DevicePtr;
6418                    let engine = &self.ranks[rank];
6419                    let _rmain = engine.gpu.enter_main()?;
6420                    let attn_in = engine.uninit(values)?;
6421                    let (dp, pp) = {
6422                        let stream = engine.stream();
6423                        let (dp, _g2) = attn_in.device_ptr(&stream);
6424                        let (pp, _g3) = ws.pos[rank].device_ptr(&stream);
6425                        (dp as u64, pp as u64)
6426                    };
6427                    ws.raw_attn_in.push(dp);
6428                    ws.raw_pos.push(pp);
6429                    ws.attn_in.push(attn_in);
6430                }
6431                {
6432                    use cudarc::driver::DevicePtr;
6433                    let root = &self.ranks[0];
6434                    let _rmain = root.gpu.enter_main()?;
6435                    let stream = root.stream();
6436                    let (a, _g) = ws.peer_partial.device_ptr(&stream);
6437                    let (b, _g) = ws.k_shadow.device_ptr(&stream);
6438                    let (c, _g) = ws.v_shadow.device_ptr(&stream);
6439                    ws.raw_peer_partial = a as u64;
6440                    ws.raw_k_shadow = b as u64;
6441                    ws.raw_v_shadow = c as u64;
6442                }
6443                {
6444                    use cudarc::driver::DevicePtr;
6445                    let rank1 = &self.ranks[1];
6446                    let _rmain = rank1.gpu.enter_main()?;
6447                    let stream = rank1.stream();
6448                    let (a, _g) = ws.o_partials[1][0].device_ptr(&stream);
6449                    let (b, _g) = ws.k[1].device_ptr(&stream);
6450                    let (c, _g) = ws.v_raw[1].device_ptr(&stream);
6451                    ws.raw_o_partial1 = a as u64;
6452                    ws.raw_k1 = b as u64;
6453                    ws.raw_v1 = c as u64;
6454                }
6455            }
6456            {
6457                let _main = e.gpu.enter_main()?;
6458                {
6459                    // (Always staged: a tcol column below the dcw floor falls back to the
6460                    // normal fused arm, which reads h through this stage.)
6461                    let h_stage = ws.h_stage.as_mut().expect("stage armed above");
6462                    let mut dst = h_stage.slice_mut(0..values);
6463                    e.stream().memcpy_dtod(&h.slice(0..values), &mut dst)?;
6464                }
6465                {
6466                    let pos_stage = ws.pos_stage.as_mut().expect("stage armed above");
6467                    let mut dst = pos_stage.slice_mut(0..1);
6468                    e.stream().memcpy_dtod(&pos_d.slice(0..1), &mut dst)?;
6469                }
6470                ws.ev_entry.record(&e.stream())?;
6471            }
6472            for rank in 0..ranks {
6473                let engine = &self.ranks[rank];
6474                let _main = engine.gpu.enter_main()?;
6475                engine.stream().wait(&ws.ev_entry)?;
6476            }
6477        } else {
6478            // Evented replicate flow (the pre-stage shape, kept for the non-fused class).
6479            {
6480                let _main = e.gpu.enter_main()?;
6481                if let Some(gate_raw) = gate_raw {
6482                    let mut gate_dst = ws.gate_e.slice_mut(0..ws.heads);
6483                    e.stream()
6484                        .memcpy_dtod(&gate_raw.slice(0..ws.heads), &mut gate_dst)?;
6485                }
6486                ws.ev_entry.record(&e.stream())?;
6487            }
6488            {
6489                let root = &self.ranks[0];
6490                let _main = root.gpu.enter_main()?;
6491                root.stream().wait(&ws.ev_entry)?;
6492                let mut destination = decode_input.ranks[0].slice_mut(0..values);
6493                root.stream()
6494                    .memcpy_dtod(&h.slice(0..values), &mut destination)?;
6495                ws.ev_refresh.record(&root.stream())?;
6496            }
6497            for rank in 1..ranks {
6498                let engine = &self.ranks[rank];
6499                let _main = engine.gpu.enter_main()?;
6500                engine.stream().wait(&ws.ev_refresh)?;
6501                let (root_rows, peer_rows) = decode_input.ranks.split_at_mut(rank);
6502                let mut destination = peer_rows[0].slice_mut(0..values);
6503                engine
6504                    .stream()
6505                    .memcpy_dtod(&root_rows[0].slice(0..values), &mut destination)?;
6506            }
6507        }
6508        for rank in 0..ranks {
6509            self.decode_v2_input_qkv_rank(
6510                ws,
6511                pos_d,
6512                decode_input,
6513                q_m,
6514                k_m,
6515                v_m,
6516                q_norm,
6517                k_norm,
6518                head_dim,
6519                n_rot,
6520                rope_base,
6521                rope_freqs,
6522                rms_eps,
6523                gate_shards.as_ref(),
6524                qkv_fused,
6525                defer_norm_rope,
6526                rank,
6527                tcol_col,
6528            )?;
6529        }
6530        Ok(())
6531    }
6532
6533    /// One rank's slice of `decode_v2_input_qkv` (projection, norm+rope, gate staging) — the
6534    /// per-device issue unit the whole-token graph captures on that rank's stream.
6535    #[allow(clippy::too_many_arguments)]
6536    pub(crate) fn decode_v2_input_qkv_rank(
6537        &self,
6538        ws: &mut StepTpDecodeV2Ws,
6539        pos_d: &CudaSlice<i32>,
6540        decode_input: &mut ResidentReplicatedDeviceRows,
6541        q_m: &ResidentBf16ColumnParallel,
6542        k_m: &ResidentBf16ColumnParallel,
6543        v_m: &ResidentBf16ColumnParallel,
6544        q_norm: &[CudaSlice<f32>],
6545        k_norm: &[CudaSlice<f32>],
6546        head_dim: usize,
6547        n_rot: usize,
6548        rope_base: f32,
6549        rope_freqs: &[Option<&CudaSlice<f32>>],
6550        rms_eps: f32,
6551        gate_shards: Option<&StepTpGateShards<'_>>,
6552        qkv_fused: bool,
6553        defer_norm_rope: bool,
6554        rank: usize,
6555        tcol_col: Option<usize>,
6556    ) -> Result<(), Box<dyn std::error::Error>> {
6557        let ranks = self.ranks.len();
6558        let local_heads = ws.local_q_dim / head_dim;
6559        let local_kv_heads = ws.local_kv_dim / head_dim;
6560        let engine = &self.ranks[rank];
6561        let _main = engine.gpu.enter_main()?;
6562        let ws_e_device = ws.e_device;
6563        // T-COLUMN SELECT (spec verify): the projections for this column were precomputed
6564        // by the weight-amortized tcol kernel — copy the column into the single-row buffers
6565        // (pure f32 moves, bit-exact) and skip the per-column matvec. Rope/norm/append run
6566        // below exactly as in the t=1 program.
6567        if qkv_fused && tcol_col.is_some() {
6568            let c = tcol_col.expect("checked");
6569            if ws.tcol_cap == 0 || ws.tcol_q.len() != ranks {
6570                return Err("tcol select without precompute".into());
6571            }
6572            // The select skips the matvec but NOT the position: rope/append below still
6573            // read this rank's pos buffer, which only the (skipped) stage path fills for
6574            // peer-device ranks. Stage it here or rank1 ropes at the previous position.
6575            if engine.ctx().ordinal() != ws_e_device {
6576                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6577            }
6578            let StepTpDecodeV2Ws {
6579                tcol_q,
6580                tcol_k,
6581                tcol_v,
6582                tcol_g,
6583                q_raw,
6584                k_raw,
6585                v_raw,
6586                gate,
6587                local_q_dim,
6588                local_kv_dim,
6589                heads,
6590                ..
6591            } = &mut *ws;
6592            let lg = *heads / ranks;
6593            let stream = engine.stream();
6594            {
6595                let mut dst = q_raw[rank].slice_mut(0..*local_q_dim);
6596                stream.memcpy_dtod(
6597                    &tcol_q[rank].slice(c * *local_q_dim..(c + 1) * *local_q_dim),
6598                    &mut dst,
6599                )?;
6600            }
6601            {
6602                let mut dst = k_raw[rank].slice_mut(0..*local_kv_dim);
6603                stream.memcpy_dtod(
6604                    &tcol_k[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6605                    &mut dst,
6606                )?;
6607            }
6608            {
6609                let mut dst = v_raw[rank].slice_mut(0..*local_kv_dim);
6610                stream.memcpy_dtod(
6611                    &tcol_v[rank].slice(c * *local_kv_dim..(c + 1) * *local_kv_dim),
6612                    &mut dst,
6613                )?;
6614            }
6615            if lg > 0 {
6616                let mut dst = gate[rank].slice_mut(0..lg);
6617                stream.memcpy_dtod(&tcol_g[rank].slice(c * lg..(c + 1) * lg), &mut dst)?;
6618            }
6619            if !defer_norm_rope {
6620                // Below the dcw floor (or a non-defer shape) the col-select cannot apply:
6621                // fall through and recompute this column's QKV from the REAL h row — the
6622                // caller always passes it. The slab copies above are dead stores.
6623            } else {
6624                return Ok(());
6625            }
6626        }
6627        if qkv_fused {
6628            // Stage-based input: raw copies from the fixed e-context stages (capture-safe;
6629            // eager ordering comes from the caller's ev_entry wait on this stream). The rank
6630            // SHARING e's device reads the stages directly — same context (probed), ordering
6631            // identical (ev_entry / graph edge), bytes identical: the copies are pure waste.
6632            let same_dev = engine.ctx().ordinal() == ws.e_device;
6633            if !same_dev {
6634                raw_copy_bytes(
6635                    ws.raw_attn_in[rank],
6636                    ws.raw_h_stage,
6637                    q_m.in_features * 4,
6638                    engine,
6639                )?;
6640                raw_copy_bytes(ws.raw_pos[rank], ws.raw_pos_stage, 4, engine)?;
6641            }
6642            let StepTpDecodeV2Ws {
6643                q_raw,
6644                k_raw,
6645                v_raw,
6646                gate,
6647                gate_e,
6648                attn_in,
6649                h_stage,
6650                heads,
6651                local_q_dim,
6652                local_kv_dim,
6653                ..
6654            } = &mut *ws;
6655            let input_ref: &CudaSlice<f32> = if same_dev {
6656                h_stage
6657                    .as_ref()
6658                    .ok_or("step TP decode v2 stage not armed")?
6659            } else {
6660                &attn_in[rank]
6661            };
6662            match (
6663                &q_m.ranks[rank].weight,
6664                &k_m.ranks[rank].weight,
6665                &v_m.ranks[rank].weight,
6666            ) {
6667                (
6668                    ResidentBf16Weight::F32(wq),
6669                    ResidentBf16Weight::F32(wk),
6670                    ResidentBf16Weight::F32(wv),
6671                ) => {
6672                    let (wg, out_g) = match &gate_shards {
6673                        Some(StepTpGateShards::F32(shards)) => (&shards[rank], *heads / ranks),
6674                        Some(StepTpGateShards::Bf16(_)) => {
6675                            return Err("step TP decode v2 gate shard class does not \
6676                                            match the F32 projections"
6677                                .into());
6678                        }
6679                        // out_g = 0: the kernel never reads wg; any resident buffer works.
6680                        None => (&*gate_e, 0),
6681                    };
6682                    engine.matvec_f32_qkv_into(
6683                        wq,
6684                        wk,
6685                        wv,
6686                        wg,
6687                        input_ref,
6688                        &mut q_raw[rank],
6689                        &mut k_raw[rank],
6690                        &mut v_raw[rank],
6691                        &mut gate[rank],
6692                        q_m.in_features,
6693                        *local_q_dim,
6694                        *local_kv_dim,
6695                        out_g,
6696                    )?;
6697                }
6698                (
6699                    ResidentBf16Weight::Bf16(wq),
6700                    ResidentBf16Weight::Bf16(wk),
6701                    ResidentBf16Weight::Bf16(wv),
6702                ) => {
6703                    let (wg, out_g) = match &gate_shards {
6704                        Some(StepTpGateShards::Bf16(shards)) => (&shards[rank], *heads / ranks),
6705                        Some(StepTpGateShards::F32(_)) => {
6706                            return Err("step TP decode v2 gate shard class does not \
6707                                            match the bf16 projections"
6708                                .into());
6709                        }
6710                        None => (wq, 0),
6711                    };
6712                    engine.matvec_bf16_qkvg_into(
6713                        wq,
6714                        wk,
6715                        wv,
6716                        wg,
6717                        input_ref,
6718                        &mut q_raw[rank],
6719                        &mut k_raw[rank],
6720                        &mut v_raw[rank],
6721                        &mut gate[rank],
6722                        q_m.in_features,
6723                        *local_q_dim,
6724                        *local_kv_dim,
6725                        out_g,
6726                    )?;
6727                }
6728                _ => {
6729                    return Err("step TP decode v2 QKV projections mix residency classes".into());
6730                }
6731            }
6732        } else {
6733            for (matrix, local_out, raw) in [
6734                (q_m, ws.local_q_dim, &mut ws.q_raw),
6735                (k_m, ws.local_kv_dim, &mut ws.k_raw),
6736                (v_m, ws.local_kv_dim, &mut ws.v_raw),
6737            ] {
6738                let ResidentBf16Weight::F32(values_w) = &matrix.ranks[rank].weight else {
6739                    return Err("step TP decode v2 lost its F32 projection residency".into());
6740                };
6741                let chunk_rows = matrix.canonical_chunk_rows.unwrap_or(local_out);
6742                engine.linear_f32_resident_canonical_rows_t1_into(
6743                    &decode_input.ranks[rank],
6744                    values_w,
6745                    &mut raw[rank],
6746                    matrix.in_features,
6747                    local_out,
6748                    chunk_rows,
6749                )?;
6750            }
6751        }
6752        if qkv_fused && defer_norm_rope {
6753            // FUSION #1 defers norm+rope to the caller's fused rope+append+inc launch.
6754        } else if qkv_fused {
6755            // Fused norm+rope: one launch; the position comes from the rank-local staged
6756            // copy (raw-copied above from the fixed e-context pos stage — capture-safe).
6757            let StepTpDecodeV2Ws {
6758                q_raw,
6759                k_raw,
6760                q,
6761                k,
6762                pos,
6763                pos_stage,
6764                ..
6765            } = &mut *ws;
6766            let same_dev = engine.ctx().ordinal() == ws_e_device;
6767            let pos_ref: &CudaSlice<i32> = if same_dev {
6768                pos_stage
6769                    .as_ref()
6770                    .ok_or("step TP decode v2 pos stage not armed")?
6771            } else {
6772                &pos[rank]
6773            };
6774            engine.qk_norm_rope_into(
6775                &q_raw[rank],
6776                &k_raw[rank],
6777                &q_norm[rank],
6778                &k_norm[rank],
6779                &mut q[rank],
6780                &mut k[rank],
6781                pos_ref,
6782                head_dim,
6783                n_rot,
6784                local_heads,
6785                local_kv_heads,
6786                rms_eps,
6787                rope_base,
6788                1.0,
6789                rope_freqs[rank],
6790            )?;
6791        } else {
6792            engine.rms_norm(
6793                &ws.q_raw[rank],
6794                &q_norm[rank],
6795                &mut ws.q[rank],
6796                head_dim,
6797                local_heads,
6798                rms_eps,
6799            )?;
6800            engine.rms_norm(
6801                &ws.k_raw[rank],
6802                &k_norm[rank],
6803                &mut ws.k[rank],
6804                head_dim,
6805                local_kv_heads,
6806                rms_eps,
6807            )?;
6808            {
6809                let mut pos_dst = ws.pos[rank].slice_mut(0..1);
6810                engine
6811                    .stream()
6812                    .memcpy_dtod(&pos_d.slice(0..1), &mut pos_dst)?;
6813            }
6814            engine.rope_neox2(
6815                &mut ws.q[rank],
6816                &mut ws.k[rank],
6817                &ws.pos[rank],
6818                head_dim,
6819                n_rot,
6820                local_heads,
6821                local_kv_heads,
6822                1,
6823                rope_base,
6824                1.0,
6825                rope_freqs[rank],
6826            )?;
6827        }
6828        if gate_shards.is_none() {
6829            let gate_start = rank * (ws.heads / ranks);
6830            let mut gate_dst = ws.gate[rank].slice_mut(0..ws.heads / ranks);
6831            engine.stream().memcpy_dtod(
6832                &ws.gate_e.slice(gate_start..gate_start + ws.heads / ranks),
6833                &mut gate_dst,
6834            )?;
6835        }
6836        Ok(())
6837    }
6838
6839    /// One rank's O-partial slice of `decode_v2_finish` — the per-device issue unit the
6840    /// whole-token graph captures on that rank's stream (the rank-done event stays with the
6841    /// eager caller; graphs order via parent edges instead).
6842    pub(crate) fn decode_v2_finish_rank_partial(
6843        &self,
6844        ws: &mut StepTpDecodeV2Ws,
6845        o_m: &ResidentStepBf16RowParallel,
6846        o_fused: bool,
6847        rank: usize,
6848    ) -> Result<(), Box<dyn std::error::Error>> {
6849        let engine = &self.ranks[rank];
6850        let _main = engine.gpu.enter_main()?;
6851        if o_fused {
6852            let StepTpDecodeV2Ws {
6853                gated,
6854                o_partials,
6855                o_block_cols,
6856                o_out,
6857                ..
6858            } = &mut *ws;
6859            let all_f32 = o_m.ranks[rank]
6860                .iter()
6861                .all(|block| matches!(block.weight, ResidentBf16Weight::F32(_)));
6862            if all_f32 {
6863                let mut weights = Vec::with_capacity(4);
6864                for block in 0..4 {
6865                    let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6866                        unreachable!("all_f32 checked above");
6867                    };
6868                    weights.push(weight);
6869                }
6870                engine.matvec_f32_b4_into(
6871                    [weights[0], weights[1], weights[2], weights[3]],
6872                    &gated[rank],
6873                    &mut o_partials[rank][0],
6874                    *o_block_cols,
6875                    *o_out,
6876                )?;
6877            } else {
6878                let mut weights = Vec::with_capacity(4);
6879                for block in 0..4 {
6880                    let ResidentBf16Weight::Bf16(weight) = &o_m.ranks[rank][block].weight else {
6881                        return Err("step TP decode v2 O projections mix residency classes".into());
6882                    };
6883                    weights.push(weight);
6884                }
6885                engine.matvec_bf16_b4_into(
6886                    [weights[0], weights[1], weights[2], weights[3]],
6887                    &gated[rank],
6888                    &mut o_partials[rank][0],
6889                    *o_block_cols,
6890                    *o_out,
6891                )?;
6892            }
6893        } else {
6894            for block in 0..ws.blocks_per_rank {
6895                let ResidentBf16Weight::F32(weight) = &o_m.ranks[rank][block].weight else {
6896                    return Err("step TP decode v2 lost its F32 O residency".into());
6897                };
6898                let x =
6899                    ws.gated[rank].slice(block * ws.o_block_cols..(block + 1) * ws.o_block_cols);
6900                let w = weight.slice(0..weight.len());
6901                let mut y = ws.o_partials[rank][block].slice_mut(0..ws.o_out);
6902                engine.linear_t1_into(&x, &w, &mut y, ws.o_block_cols, ws.o_out)?;
6903            }
6904        }
6905        Ok(())
6906    }
6907
6908    /// v2 phase 2: canonical-block O reduction on the root device plus the K/V shadow gathers,
6909    /// returning a fresh model-engine output ordered behind `ev_oproj` on `e`'s stream.
6910    ///
6911    /// The caller must have queued every rank's attention work (reading `ws.gated`, `ws.k`,
6912    /// `ws.v_raw`) on the rank streams before this call. Reduction order is identical to
6913    /// `step_bf16_row_parallel_resident_native`: zeros, then rank 0's blocks, then each peer
6914    /// rank's blocks, one `add` per block.
6915    pub(crate) fn decode_v2_finish(
6916        &self,
6917        ws: &mut StepTpDecodeV2Ws,
6918        e: &Engine,
6919        o_m: &ResidentStepBf16RowParallel,
6920    ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6921        let ranks = self.ranks.len();
6922        if e.ctx().ordinal() != ws.e_device {
6923            return Err("step TP decode v2 finish engine changed".into());
6924        }
6925        // MEMRA_STEP_TP_QKV_FUSED extends to the O path: one matvec_f32_b4 launch per rank
6926        // (in-order canonical block accumulation per element) and a single peer-copy + add on
6927        // the root, replacing 4 cuBLASLt launches per rank + the 4-copy/8-add chain. Same
6928        // numeric-class door and gate as the fused QKV projection.
6929        let o_fused = step_tp_qkv_fused_enabled()? && ws.blocks_per_rank == 4 && ranks == 2;
6930
6931        // Per-rank O block partials on the owning rank's stream (serial after the attention
6932        // kernels the driver queued there), then the rank-done event for root's peer reads.
6933        for rank in 0..ranks {
6934            self.decode_v2_finish_rank_partial(ws, o_m, o_fused, rank)?;
6935            if rank == 0 {
6936                // root == rank0: its own stream order covers the partial; only peers need
6937                // the record/wait pair (host-op diet, matches the routes-arm skip).
6938                continue;
6939            }
6940            let engine = &self.ranks[rank];
6941            let _main = engine.gpu.enter_main()?;
6942            ws.ev_rank[rank].record(&engine.stream())?;
6943        }
6944
6945        // Root reduce in canonical order + shadow gathers, all on the root stream.
6946        let root = &self.ranks[0];
6947        #[allow(unused_assignments)]
6948        let mut final_in_a = false;
6949        {
6950            let _main = root.gpu.enter_main()?;
6951            for ev in ws.ev_rank.iter().skip(1) {
6952                root.stream().wait(ev)?;
6953            }
6954            if o_fused && oproj_direct_on() && ranks == 2 && no_local_shadow_on() {
6955                // DIRECT JOIN: rank1's partial already sits in root memory (P2P kernel
6956                // stores; visibility guaranteed by the ev_rank[1] wait above), rank0's
6957                // partial is root-stream-ordered — record ONE event and let the model
6958                // engine do the single add itself, straight into its own output row.
6959                // Same operands, same add order as finish_root_fused: BIT-IDENTICAL.
6960                ws.ev_oproj.record(&root.stream())?;
6961                let _main = e.gpu.enter_main()?;
6962                e.stream().wait(&ws.ev_oproj)?;
6963                let mut output = e.uninit(ws.o_out)?;
6964                if oproj_tail_on() && oproj_tail_eligible() {
6965                    // M2: defer the add into the residual+norm consumer (waits stay HERE;
6966                    // only the arithmetic moves). `output` is returned unwritten.
6967                    use cudarc::driver::DevicePtr;
6968                    let stream = e.stream();
6969                    let (p0, _g0) = ws.o_partials[0][0].device_ptr(&stream);
6970                    let (p1, _g1) = ws.o_partials[1][0].device_ptr(&stream);
6971                    set_oproj_tail((p0 as u64, p1 as u64));
6972                    return Ok(output);
6973                }
6974                e.add(
6975                    &ws.o_partials[0][0],
6976                    &ws.o_partials[1][0],
6977                    &mut output,
6978                    ws.o_out,
6979                )?;
6980                return Ok(output);
6981            }
6982            if o_fused {
6983                self.decode_v2_finish_root_fused(ws)?;
6984                ws.ev_oproj.record(&root.stream())?;
6985                let _main = e.gpu.enter_main()?;
6986                e.stream().wait(&ws.ev_oproj)?;
6987                let mut output = e.uninit(ws.o_out)?;
6988                e.stream().memcpy_dtod(
6989                    &ws.reduce_a.slice(0..ws.o_out),
6990                    &mut output.slice_mut(0..ws.o_out),
6991                )?;
6992                return Ok(output);
6993            }
6994            let mut first = true;
6995            let mut current_is_a = false;
6996            for rank in 0..ranks {
6997                for block in 0..ws.blocks_per_rank {
6998                    let use_peer = rank != 0;
6999                    if use_peer {
7000                        root.stream()
7001                            .memcpy_dtod(&ws.o_partials[rank][block], &mut ws.peer_partial)?;
7002                    }
7003                    // add(prev, partial) -> the other reduce buffer, exactly one add per block
7004                    match (first, current_is_a, use_peer) {
7005                        (true, _, true) => {
7006                            root.add(&ws.zeros, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7007                        }
7008                        (true, _, false) => root.add(
7009                            &ws.zeros,
7010                            &ws.o_partials[0][block],
7011                            &mut ws.reduce_a,
7012                            ws.o_out,
7013                        )?,
7014                        (false, true, true) => {
7015                            root.add(&ws.reduce_a, &ws.peer_partial, &mut ws.reduce_b, ws.o_out)?
7016                        }
7017                        (false, true, false) => root.add(
7018                            &ws.reduce_a,
7019                            &ws.o_partials[0][block],
7020                            &mut ws.reduce_b,
7021                            ws.o_out,
7022                        )?,
7023                        (false, false, true) => {
7024                            root.add(&ws.reduce_b, &ws.peer_partial, &mut ws.reduce_a, ws.o_out)?
7025                        }
7026                        (false, false, false) => root.add(
7027                            &ws.reduce_b,
7028                            &ws.o_partials[0][block],
7029                            &mut ws.reduce_a,
7030                            ws.o_out,
7031                        )?,
7032                    }
7033                    current_is_a = first || !current_is_a;
7034                    first = false;
7035                }
7036            }
7037            final_in_a = current_is_a;
7038
7039            for rank in 0..ranks {
7040                let start = rank * ws.local_kv_dim;
7041                let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
7042                root.stream().memcpy_dtod(&ws.k[rank], &mut k_dst)?;
7043                let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
7044                root.stream().memcpy_dtod(&ws.v_raw[rank], &mut v_dst)?;
7045            }
7046            ws.ev_oproj.record(&root.stream())?;
7047        }
7048
7049        // Model-engine output: e waits the root event, then copies the reduced row into a
7050        // fresh e-context buffer (same ownership contract as v1's `e.htod`). The same wait
7051        // orders the driver's shadow append (it reads ws.k_shadow/ws.v_shadow on e's stream).
7052        let _main = e.gpu.enter_main()?;
7053        e.stream().wait(&ws.ev_oproj)?;
7054        let mut output = e.uninit(ws.o_out)?;
7055        let source = if final_in_a {
7056            &ws.reduce_a
7057        } else {
7058            &ws.reduce_b
7059        };
7060        e.stream().memcpy_dtod(
7061            &source.slice(0..ws.o_out),
7062            &mut output.slice_mut(0..ws.o_out),
7063        )?;
7064        Ok(output)
7065    }
7066
7067    pub fn run_routed_experts(
7068        &self,
7069        experts: &ResidentExpertParallel,
7070        input: &[f32],
7071        tokens: usize,
7072        selected: &[usize],
7073        route_weights: &[f32],
7074        experts_per_token: usize,
7075        activation_limit: Option<f32>,
7076    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7077        validate_step_expert_activation_limit(activation_limit)?;
7078        validate_ep_residency(&self.ranks, experts)?;
7079        validate_activations(input, tokens, experts.input_width)?;
7080        let pairs = tokens
7081            .checked_mul(experts_per_token)
7082            .ok_or("EP route count overflow")?;
7083        if selected.len() != pairs || route_weights.len() != pairs {
7084            return Err(format!(
7085                "EP routes selected={} weights={} != tokens {tokens} x experts/token \
7086                 {experts_per_token} ({pairs})",
7087                selected.len(),
7088                route_weights.len(),
7089            )
7090            .into());
7091        }
7092        if !route_weights.iter().all(|weight| weight.is_finite()) {
7093            return Err("EP route weights contain a non-finite value".into());
7094        }
7095        if self.native_p2p {
7096            return self.run_routed_experts_native(
7097                experts,
7098                input,
7099                tokens,
7100                selected,
7101                route_weights,
7102                experts_per_token,
7103                activation_limit,
7104            );
7105        }
7106
7107        let mut output = vec![0.0f32; tokens * experts.input_width];
7108        let per_rank = experts.expert_count / experts.ranks.len();
7109        for token in 0..tokens {
7110            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7111            for slot in 0..experts_per_token {
7112                let pair = token * experts_per_token + slot;
7113                let expert = selected[pair];
7114                if expert >= experts.expert_count {
7115                    return Err(format!(
7116                        "EP selected expert {expert} outside 0..{}",
7117                        experts.expert_count
7118                    )
7119                    .into());
7120                }
7121                let owner = expert / per_rank;
7122                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7123                let rank = &experts.ranks[owner];
7124                let engine = &self.ranks[owner];
7125                let gate =
7126                    run_resident_bank_expert(engine, &rank.gate, local_expert, input_row, 1)?;
7127                let up = run_resident_bank_expert(engine, &rank.up, local_expert, input_row, 1)?;
7128                let activated: Vec<f32> = gate
7129                    .iter()
7130                    .zip(&up)
7131                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7132                    .collect();
7133                debug_assert_eq!(activated.len(), experts.expert_width);
7134                let down =
7135                    run_resident_bank_expert(engine, &rank.down, local_expert, &activated, 1)?;
7136                let weight = route_weights[pair];
7137                for (sum, value) in output
7138                    [token * experts.input_width..(token + 1) * experts.input_width]
7139                    .iter_mut()
7140                    .zip(down)
7141                {
7142                    *sum += weight * value;
7143                }
7144            }
7145        }
7146        Ok(output)
7147    }
7148
7149    fn run_routed_experts_native(
7150        &self,
7151        experts: &ResidentExpertParallel,
7152        input: &[f32],
7153        tokens: usize,
7154        selected: &[usize],
7155        route_weights: &[f32],
7156        experts_per_token: usize,
7157        activation_limit: Option<f32>,
7158    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7159        if !self.native_p2p || self.ranks.len() < 2 {
7160            return Err("native EP execution requires at least two P2P ranks".into());
7161        }
7162        if self.ep_device_arithmetic {
7163            return self.run_routed_experts_native_device(
7164                experts,
7165                input,
7166                tokens,
7167                selected,
7168                route_weights,
7169                experts_per_token,
7170                activation_limit,
7171            );
7172        }
7173        let mut output = vec![0.0f32; tokens * experts.input_width];
7174        let per_rank = experts.expert_count / experts.ranks.len();
7175        for token in 0..tokens {
7176            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7177            let mut rank_inputs = (0..self.ranks.len())
7178                .map(|_| None)
7179                .collect::<Vec<Option<CudaSlice<f32>>>>();
7180            rank_inputs[0] = Some({
7181                let root = &self.ranks[0];
7182                let _main = root.gpu.enter_main()?;
7183                root.htod(input_row)?
7184            });
7185
7186            for slot in 0..experts_per_token {
7187                let pair = token * experts_per_token + slot;
7188                let expert = selected[pair];
7189                if expert >= experts.expert_count {
7190                    return Err(format!(
7191                        "EP selected expert {expert} outside 0..{}",
7192                        experts.expert_count
7193                    )
7194                    .into());
7195                }
7196                let owner = expert / per_rank;
7197                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7198                if rank_inputs[owner].is_none() {
7199                    let peer_input = {
7200                        let root_input = rank_inputs[0]
7201                            .as_ref()
7202                            .ok_or("native EP lost its root input")?;
7203                        let engine = &self.ranks[owner];
7204                        let _main = engine.gpu.enter_main()?;
7205                        let mut peer_input = engine.uninit(experts.input_width)?;
7206                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7207                        peer_input
7208                    };
7209                    rank_inputs[owner] = Some(peer_input);
7210                }
7211
7212                let rank = &experts.ranks[owner];
7213                let engine = &self.ranks[owner];
7214                let owner_input = rank_inputs[owner]
7215                    .as_ref()
7216                    .ok_or("native EP owner input is absent after dispatch")?;
7217                let gate = run_resident_bank_expert_device(
7218                    engine,
7219                    &rank.gate,
7220                    local_expert,
7221                    owner_input,
7222                    1,
7223                )?;
7224                let up = run_resident_bank_expert_device(
7225                    engine,
7226                    &rank.up,
7227                    local_expert,
7228                    owner_input,
7229                    1,
7230                )?;
7231                let (gate, up) = {
7232                    let _main = engine.gpu.enter_main()?;
7233                    (engine.dtoh(&gate)?, engine.dtoh(&up)?)
7234                };
7235                let activated = gate
7236                    .iter()
7237                    .zip(&up)
7238                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
7239                    .collect::<Vec<_>>();
7240                debug_assert_eq!(activated.len(), experts.expert_width);
7241                let activated = {
7242                    let _main = engine.gpu.enter_main()?;
7243                    engine.htod(&activated)?
7244                };
7245                let down = run_resident_bank_expert_device(
7246                    engine,
7247                    &rank.down,
7248                    local_expert,
7249                    &activated,
7250                    1,
7251                )?;
7252                let down = if owner == 0 {
7253                    let _main = engine.gpu.enter_main()?;
7254                    engine.dtoh(&down)?
7255                } else {
7256                    let root = &self.ranks[0];
7257                    let _main = root.gpu.enter_main()?;
7258                    let mut root_down = root.uninit(experts.input_width)?;
7259                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7260                    root.dtoh(&root_down)?
7261                };
7262                let weight = route_weights[pair];
7263                for (sum, value) in output
7264                    [token * experts.input_width..(token + 1) * experts.input_width]
7265                    .iter_mut()
7266                    .zip(down)
7267                {
7268                    *sum += weight * value;
7269                }
7270            }
7271        }
7272        Ok(output)
7273    }
7274
7275    fn run_routed_experts_native_device(
7276        &self,
7277        experts: &ResidentExpertParallel,
7278        input: &[f32],
7279        tokens: usize,
7280        selected: &[usize],
7281        route_weights: &[f32],
7282        experts_per_token: usize,
7283        activation_limit: Option<f32>,
7284    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7285        if !self.native_p2p || !self.ep_device_arithmetic || self.ranks.len() < 2 {
7286            return Err(
7287                "device-resident EP arithmetic requires at least two native P2P ranks".into(),
7288            );
7289        }
7290        let mut output = Vec::with_capacity(tokens * experts.input_width);
7291        let per_rank = experts.expert_count / experts.ranks.len();
7292        let root = &self.ranks[0];
7293        for token in 0..tokens {
7294            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
7295            let mut rank_inputs = (0..self.ranks.len())
7296                .map(|_| None)
7297                .collect::<Vec<Option<CudaSlice<f32>>>>();
7298            rank_inputs[0] = Some({
7299                let _main = root.gpu.enter_main()?;
7300                root.htod(input_row)?
7301            });
7302            let mut root_output = {
7303                let _main = root.gpu.enter_main()?;
7304                root.zeros(experts.input_width)?
7305            };
7306            let mut remote_down_keepalive = Vec::new();
7307
7308            for slot in 0..experts_per_token {
7309                let pair = token * experts_per_token + slot;
7310                let expert = selected[pair];
7311                if expert >= experts.expert_count {
7312                    return Err(format!(
7313                        "EP selected expert {expert} outside 0..{}",
7314                        experts.expert_count
7315                    )
7316                    .into());
7317                }
7318                let owner = expert / per_rank;
7319                let local_expert = expert - experts.ranks[owner].gate.expert_range.start;
7320                if rank_inputs[owner].is_none() {
7321                    let peer_input = {
7322                        let root_input = rank_inputs[0]
7323                            .as_ref()
7324                            .ok_or("native EP lost its root input")?;
7325                        let engine = &self.ranks[owner];
7326                        let _main = engine.gpu.enter_main()?;
7327                        let mut peer_input = engine.uninit(experts.input_width)?;
7328                        engine.stream().memcpy_dtod(root_input, &mut peer_input)?;
7329                        peer_input
7330                    };
7331                    rank_inputs[owner] = Some(peer_input);
7332                }
7333
7334                let rank = &experts.ranks[owner];
7335                let engine = &self.ranks[owner];
7336                let owner_input = rank_inputs[owner]
7337                    .as_ref()
7338                    .ok_or("native EP owner input is absent after dispatch")?;
7339                let gate = run_resident_bank_expert_device(
7340                    engine,
7341                    &rank.gate,
7342                    local_expert,
7343                    owner_input,
7344                    1,
7345                )?;
7346                let up = run_resident_bank_expert_device(
7347                    engine,
7348                    &rank.up,
7349                    local_expert,
7350                    owner_input,
7351                    1,
7352                )?;
7353                let activated = {
7354                    let _main = engine.gpu.enter_main()?;
7355                    let mut activated = engine.uninit(experts.expert_width)?;
7356                    if let Some(limit) = activation_limit {
7357                        engine.silu_clamped_mul_host_expf(
7358                            &gate,
7359                            &up,
7360                            limit,
7361                            &mut activated,
7362                            experts.expert_width,
7363                        )?;
7364                    } else {
7365                        engine.silu_mul_host_expf(
7366                            &gate,
7367                            &up,
7368                            &mut activated,
7369                            experts.expert_width,
7370                        )?;
7371                    }
7372                    activated
7373                };
7374                let down = run_resident_bank_expert_device(
7375                    engine,
7376                    &rank.down,
7377                    local_expert,
7378                    &activated,
7379                    1,
7380                )?;
7381                let root_down = if owner == 0 {
7382                    down
7383                } else {
7384                    let _main = root.gpu.enter_main()?;
7385                    let mut root_down = root.uninit(experts.input_width)?;
7386                    root.stream().memcpy_dtod(&down, &mut root_down)?;
7387                    // The peer copy runs on the root stream. Keep its remote source alive until
7388                    // the final root readback synchronizes that stream; otherwise async free can
7389                    // recycle the owner's allocation while cuMemcpyPeerAsync is still reading it.
7390                    remote_down_keepalive.push(down);
7391                    root_down
7392                };
7393                let _main = root.gpu.enter_main()?;
7394                let mut destination = root_output.slice_mut(0..experts.input_width);
7395                root.axpy_host_into(
7396                    &root_down.slice(0..root_down.len()),
7397                    route_weights[pair],
7398                    &mut destination,
7399                    experts.input_width,
7400                )?;
7401            }
7402
7403            let _main = root.gpu.enter_main()?;
7404            let root_output = root.dtoh(&root_output)?;
7405            drop(remote_down_keepalive);
7406            output.extend(root_output);
7407        }
7408        Ok(output)
7409    }
7410}
7411
7412fn validate_column_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7413    if matrix.out_features % tp != 0 {
7414        return Err(format!(
7415            "column-parallel out_features {} is not divisible by TP={tp}",
7416            matrix.out_features
7417        ));
7418    }
7419    let local_out = matrix.out_features / tp;
7420    if local_out % FP8_BLOCK != 0 {
7421        return Err(format!(
7422            "column-parallel output shard {local_out} cuts through a {FP8_BLOCK}-row \
7423             E4M3 scale block"
7424        ));
7425    }
7426    Ok(())
7427}
7428
7429fn step_bf16_canonical_chunk_rows(out_features: usize, tp: usize) -> Result<usize, String> {
7430    if !matches!(tp, 1 | 2 | 4 | 8) {
7431        return Err(format!(
7432            "Step BF16 canonical projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7433        ));
7434    }
7435    if out_features == 0 || out_features % PRODUCT_MAX_CARDS != 0 {
7436        return Err(format!(
7437            "Step BF16 output width {out_features} is not divisible by the TP8 product envelope"
7438        ));
7439    }
7440    let canonical_rows = out_features / PRODUCT_MAX_CARDS;
7441    let local_out = out_features / tp;
7442    if local_out % canonical_rows != 0 {
7443        return Err(format!(
7444            "Step BF16 TP={tp} output shard {local_out} is not divisible by canonical \
7445             {canonical_rows}-row chunks"
7446        ));
7447    }
7448    Ok(canonical_rows)
7449}
7450
7451fn step_bf16_canonical_chunk_cols(in_features: usize, tp: usize) -> Result<usize, String> {
7452    if !matches!(tp, 1 | 2 | 4 | 8) {
7453        return Err(format!(
7454            "Step BF16 canonical row projection requires TP1/TP2/TP4/TP8, got TP={tp}"
7455        ));
7456    }
7457    if in_features == 0 || in_features % PRODUCT_MAX_CARDS != 0 {
7458        return Err(format!(
7459            "Step BF16 input width {in_features} is not divisible by the TP8 product envelope"
7460        ));
7461    }
7462    let canonical_cols = in_features / PRODUCT_MAX_CARDS;
7463    let local_in = in_features / tp;
7464    if local_in % canonical_cols != 0 {
7465        return Err(format!(
7466            "Step BF16 TP={tp} input shard {local_in} is not divisible by canonical \
7467             {canonical_cols}-column chunks"
7468        ));
7469    }
7470    Ok(canonical_cols)
7471}
7472
7473fn validate_row_shape(matrix: E4m3BlockMatrix<'_>, tp: usize) -> Result<(), String> {
7474    if matrix.in_features % tp != 0 {
7475        return Err(format!(
7476            "row-parallel in_features {} is not divisible by TP={tp}",
7477            matrix.in_features
7478        ));
7479    }
7480    let local_in = matrix.in_features / tp;
7481    if local_in % FP8_BLOCK != 0 {
7482        return Err(format!(
7483            "row-parallel input shard {local_in} cuts through a {FP8_BLOCK}-column \
7484             E4M3 scale block"
7485        ));
7486    }
7487    Ok(())
7488}
7489
7490fn upload_rank(
7491    engine: &Engine,
7492    matrix: E4m3BlockMatrix<'_>,
7493) -> Result<ResidentE4m3Rank, Box<dyn std::error::Error>> {
7494    let _main = engine.gpu.enter_main()?;
7495    matrix.validate()?;
7496    Ok(ResidentE4m3Rank {
7497        codes: engine.htod_bytes(matrix.codes)?,
7498        scales: engine.htod(matrix.scales)?,
7499        out_features: matrix.out_features,
7500        in_features: matrix.in_features,
7501    })
7502}
7503
7504fn upload_bf16_rank(
7505    engine: &Engine,
7506    matrix: Bf16Matrix<'_>,
7507    f32_mirror: bool,
7508) -> Result<ResidentBf16Rank, Box<dyn std::error::Error>> {
7509    let _main = engine.gpu.enter_main()?;
7510    matrix.validate()?;
7511    let bytes = engine.htod_bytes(matrix.bytes)?;
7512    let weight = if f32_mirror {
7513        let values = matrix
7514            .out_features
7515            .checked_mul(matrix.in_features)
7516            .ok_or("resident BF16 mirror element count overflow")?;
7517        ResidentBf16Weight::F32(engine.bf16_to_f32(&bytes.slice(0..bytes.len()), values)?)
7518    } else {
7519        ResidentBf16Weight::Bf16(bytes)
7520    };
7521    Ok(ResidentBf16Rank {
7522        weight,
7523        out_features: matrix.out_features,
7524        in_features: matrix.in_features,
7525    })
7526}
7527
7528fn upload_expert_bank_rank(
7529    engine: &Engine,
7530    bank: E4m3ExpertBank<'_>,
7531    expert_range: Range<usize>,
7532) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7533    let _main = engine.gpu.enter_main()?;
7534    bank.validate()?;
7535    if expert_range.start >= expert_range.end || expert_range.end > bank.expert_count {
7536        return Err(format!(
7537            "invalid EP expert range {expert_range:?} for {} experts",
7538            bank.expert_count
7539        )
7540        .into());
7541    }
7542    let code_stride = bank.out_features * bank.in_features;
7543    let scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * bank.in_features.div_ceil(FP8_BLOCK);
7544    Ok(ResidentE4m3ExpertBankRank {
7545        codes: engine.htod_bytes(
7546            &bank.codes[expert_range.start * code_stride..expert_range.end * code_stride],
7547        )?,
7548        scales: engine.htod(
7549            &bank.scales[expert_range.start * scale_stride..expert_range.end * scale_stride],
7550        )?,
7551        expert_range,
7552        out_features: bank.out_features,
7553        in_features: bank.in_features,
7554        code_stride,
7555        scale_stride,
7556        k_blocks: None,
7557    })
7558}
7559
7560fn validate_column_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7561    if bank.out_features % tp != 0 {
7562        return Err(format!(
7563            "TP expert output width {} is not divisible by TP={tp}",
7564            bank.out_features
7565        ));
7566    }
7567    let local_out = bank.out_features / tp;
7568    if local_out % FP8_BLOCK != 0 {
7569        return Err(format!(
7570            "TP expert output shard {local_out} cuts through a {FP8_BLOCK}-row E4M3 scale block"
7571        ));
7572    }
7573    Ok(())
7574}
7575
7576fn validate_row_bank_shape(bank: E4m3ExpertBank<'_>, tp: usize) -> Result<(), String> {
7577    if bank.in_features % tp != 0 {
7578        return Err(format!(
7579            "TP expert input width {} is not divisible by TP={tp}",
7580            bank.in_features
7581        ));
7582    }
7583    let local_in = bank.in_features / tp;
7584    if local_in % FP8_BLOCK != 0 {
7585        return Err(format!(
7586            "TP expert input shard {local_in} cuts through a {FP8_BLOCK}-column E4M3 scale block"
7587        ));
7588    }
7589    Ok(())
7590}
7591
7592fn upload_column_bank_rank(
7593    engine: &Engine,
7594    bank: E4m3ExpertBank<'_>,
7595    tp: usize,
7596    rank: usize,
7597) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7598    let _main = engine.gpu.enter_main()?;
7599    let packed = pack_column_bank_rank(bank, tp, rank)?;
7600    Ok(ResidentE4m3ExpertBankRank {
7601        codes: engine.htod_bytes(&packed.codes)?,
7602        scales: engine.htod(&packed.scales)?,
7603        expert_range: packed.expert_range,
7604        out_features: packed.out_features,
7605        in_features: packed.in_features,
7606        code_stride: packed.code_stride,
7607        scale_stride: packed.scale_stride,
7608        k_blocks: packed.k_blocks,
7609    })
7610}
7611
7612fn pack_column_bank_rank(
7613    bank: E4m3ExpertBank<'_>,
7614    tp: usize,
7615    rank: usize,
7616) -> Result<PackedE4m3ExpertBankRank, String> {
7617    bank.validate()?;
7618    validate_column_bank_shape(bank, tp)?;
7619    if rank >= tp {
7620        return Err(format!("TP rank {rank} outside 0..{tp}"));
7621    }
7622    let local_out = bank.out_features / tp;
7623    let full_code_stride = bank.out_features * bank.in_features;
7624    let local_code_stride = local_out * bank.in_features;
7625    let scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
7626    let full_scale_stride = bank.out_features.div_ceil(FP8_BLOCK) * scale_cols;
7627    let local_scale_rows = local_out / FP8_BLOCK;
7628    let local_scale_stride = local_scale_rows * scale_cols;
7629    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7630    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7631    let row_start = rank * local_out;
7632    let scale_row_start = rank * local_scale_rows;
7633    for expert in 0..bank.expert_count {
7634        let code_start = expert * full_code_stride + row_start * bank.in_features;
7635        codes.extend_from_slice(&bank.codes[code_start..code_start + local_code_stride]);
7636        let scale_start = expert * full_scale_stride + scale_row_start * scale_cols;
7637        scales.extend_from_slice(&bank.scales[scale_start..scale_start + local_scale_stride]);
7638    }
7639    Ok(PackedE4m3ExpertBankRank {
7640        codes,
7641        scales,
7642        expert_range: 0..bank.expert_count,
7643        out_features: local_out,
7644        in_features: bank.in_features,
7645        code_stride: local_code_stride,
7646        scale_stride: local_scale_stride,
7647        k_blocks: None,
7648    })
7649}
7650
7651fn upload_row_bank_rank(
7652    engine: &Engine,
7653    bank: E4m3ExpertBank<'_>,
7654    tp: usize,
7655    rank: usize,
7656) -> Result<ResidentE4m3ExpertBankRank, Box<dyn std::error::Error>> {
7657    let _main = engine.gpu.enter_main()?;
7658    let packed = pack_row_bank_rank(bank, tp, rank)?;
7659    Ok(ResidentE4m3ExpertBankRank {
7660        codes: engine.htod_bytes(&packed.codes)?,
7661        scales: engine.htod(&packed.scales)?,
7662        expert_range: packed.expert_range,
7663        out_features: packed.out_features,
7664        in_features: packed.in_features,
7665        code_stride: packed.code_stride,
7666        scale_stride: packed.scale_stride,
7667        k_blocks: packed.k_blocks,
7668    })
7669}
7670
7671fn pack_row_bank_rank(
7672    bank: E4m3ExpertBank<'_>,
7673    tp: usize,
7674    rank: usize,
7675) -> Result<PackedE4m3ExpertBankRank, String> {
7676    bank.validate()?;
7677    validate_row_bank_shape(bank, tp)?;
7678    if rank >= tp {
7679        return Err(format!("TP rank {rank} outside 0..{tp}"));
7680    }
7681    let local_in = bank.in_features / tp;
7682    let full_code_stride = bank.out_features * bank.in_features;
7683    let local_code_stride = bank.out_features * local_in;
7684    let full_scale_cols = bank.in_features.div_ceil(FP8_BLOCK);
7685    let local_scale_cols = local_in / FP8_BLOCK;
7686    let scale_rows = bank.out_features.div_ceil(FP8_BLOCK);
7687    let full_scale_stride = scale_rows * full_scale_cols;
7688    let local_scale_stride = scale_rows * local_scale_cols;
7689    let global_block_start = rank * local_scale_cols;
7690    let mut codes = Vec::with_capacity(bank.expert_count * local_code_stride);
7691    let mut scales = Vec::with_capacity(bank.expert_count * local_scale_stride);
7692    for expert in 0..bank.expert_count {
7693        let expert_code_start = expert * full_code_stride;
7694        let expert_scale_start = expert * full_scale_stride;
7695        for local_block in 0..local_scale_cols {
7696            let global_block = global_block_start + local_block;
7697            let column_start = global_block * FP8_BLOCK;
7698            for row in 0..bank.out_features {
7699                let start = expert_code_start + row * bank.in_features + column_start;
7700                codes.extend_from_slice(&bank.codes[start..start + FP8_BLOCK]);
7701            }
7702            for row in 0..scale_rows {
7703                scales.push(bank.scales[expert_scale_start + row * full_scale_cols + global_block]);
7704            }
7705        }
7706    }
7707    Ok(PackedE4m3ExpertBankRank {
7708        codes,
7709        scales,
7710        expert_range: 0..bank.expert_count,
7711        out_features: bank.out_features,
7712        in_features: local_in,
7713        code_stride: local_code_stride,
7714        scale_stride: local_scale_stride,
7715        k_blocks: Some(local_scale_cols),
7716    })
7717}
7718
7719fn validate_resident_ranks(engines: &[Engine], ranks: &[ResidentE4m3Rank]) -> Result<(), String> {
7720    if engines.len() != ranks.len() {
7721        return Err(format!(
7722            "resident TP rank count {} != runtime rank count {}",
7723            ranks.len(),
7724            engines.len()
7725        ));
7726    }
7727    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7728        let device = engine.ctx().ordinal();
7729        if matrix.codes.ordinal() != device || matrix.scales.ordinal() != device {
7730            return Err(format!(
7731                "resident TP rank {rank} is not owned by runtime device {device}"
7732            ));
7733        }
7734    }
7735    Ok(())
7736}
7737
7738fn validate_tp_bank_residency(
7739    engines: &[Engine],
7740    experts: &ResidentTpExpertBank,
7741) -> Result<(), String> {
7742    if engines.len() != experts.gate.len()
7743        || engines.len() != experts.up.len()
7744        || engines.len() != experts.down.len()
7745    {
7746        return Err(format!(
7747            "resident TP expert-bank rank counts gate={} up={} down={} != runtime {}",
7748            experts.gate.len(),
7749            experts.up.len(),
7750            experts.down.len(),
7751            engines.len()
7752        ));
7753    }
7754    for (rank, engine) in engines.iter().enumerate() {
7755        let device = engine.ctx().ordinal();
7756        for (projection, bank) in [
7757            ("gate", &experts.gate[rank]),
7758            ("up", &experts.up[rank]),
7759            ("down", &experts.down[rank]),
7760        ] {
7761            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7762                return Err(format!(
7763                    "resident TP rank {rank} {projection} bank is not owned by runtime device \
7764                     {device}"
7765                ));
7766            }
7767        }
7768    }
7769    Ok(())
7770}
7771
7772fn validate_ep_residency(
7773    engines: &[Engine],
7774    experts: &ResidentExpertParallel,
7775) -> Result<(), String> {
7776    if engines.len() != experts.ranks.len() {
7777        return Err(format!(
7778            "resident EP rank count {} != runtime rank count {}",
7779            experts.ranks.len(),
7780            engines.len()
7781        ));
7782    }
7783    for (rank, (engine, resident)) in engines.iter().zip(&experts.ranks).enumerate() {
7784        let device = engine.ctx().ordinal();
7785        for (projection, bank) in [
7786            ("gate", &resident.gate),
7787            ("up", &resident.up),
7788            ("down", &resident.down),
7789        ] {
7790            if bank.codes.ordinal() != device || bank.scales.ordinal() != device {
7791                return Err(format!(
7792                    "resident EP rank {rank} {projection} bank is not owned by runtime device \
7793                     {device}"
7794                ));
7795            }
7796        }
7797    }
7798    Ok(())
7799}
7800
7801fn run_rank(
7802    engine: &Engine,
7803    matrix: E4m3BlockMatrix<'_>,
7804    activations: &[f32],
7805    tokens: usize,
7806) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7807    let _main = engine.gpu.enter_main()?;
7808    let codes = engine.htod_bytes(matrix.codes)?;
7809    let scales = engine.htod(matrix.scales)?;
7810    let activations = engine.htod(activations)?;
7811    let output = engine.qmatvec_mmq_fp8_blk(
7812        &codes,
7813        &scales,
7814        &activations,
7815        tokens,
7816        matrix.in_features,
7817        matrix.out_features,
7818    )?;
7819    engine.dtoh(&output)
7820}
7821
7822fn run_resident_rank(
7823    engine: &Engine,
7824    matrix: &ResidentE4m3Rank,
7825    activations: &[f32],
7826    tokens: usize,
7827) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7828    let _main = engine.gpu.enter_main()?;
7829    let activations = engine.htod(activations)?;
7830    let output = engine.qmatvec_mmq_fp8_blk(
7831        &matrix.codes,
7832        &matrix.scales,
7833        &activations,
7834        tokens,
7835        matrix.in_features,
7836        matrix.out_features,
7837    )?;
7838    engine.dtoh(&output)
7839}
7840
7841fn run_resident_bf16_rank(
7842    engine: &Engine,
7843    matrix: &ResidentBf16Rank,
7844    activations: &[f32],
7845    tokens: usize,
7846    canonical_chunk_rows: Option<usize>,
7847) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
7848    let _main = engine.gpu.enter_main()?;
7849    let activations = engine.htod(activations)?;
7850    let output = run_resident_bf16_rank_device(
7851        engine,
7852        matrix,
7853        &activations,
7854        tokens,
7855        canonical_chunk_rows,
7856        false,
7857    )?;
7858    engine.dtoh(&output)
7859}
7860
7861fn run_resident_bf16_rank_device(
7862    engine: &Engine,
7863    matrix: &ResidentBf16Rank,
7864    activations: &CudaSlice<f32>,
7865    tokens: usize,
7866    canonical_chunk_rows: Option<usize>,
7867    strided_chunk_output: bool,
7868) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7869    let _main = engine.gpu.enter_main()?;
7870    if activations.ordinal() != engine.ctx().ordinal() {
7871        return Err(format!(
7872            "resident BF16 activation device {} != rank device {}",
7873            activations.ordinal(),
7874            engine.ctx().ordinal()
7875        )
7876        .into());
7877    }
7878    if activations.len() != tokens * matrix.in_features {
7879        return Err(format!(
7880            "resident BF16 activation count {} != {tokens}x{}",
7881            activations.len(),
7882            matrix.in_features
7883        )
7884        .into());
7885    }
7886    match (&matrix.weight, canonical_chunk_rows) {
7887        (ResidentBf16Weight::Bf16(bytes), Some(rows)) => engine
7888            .linear_bf16_resident_canonical_rows(
7889                activations,
7890                bytes,
7891                tokens,
7892                matrix.in_features,
7893                matrix.out_features,
7894                rows,
7895            ),
7896        (ResidentBf16Weight::Bf16(bytes), None) => engine.linear_bf16_resident(
7897            activations,
7898            bytes,
7899            tokens,
7900            matrix.in_features,
7901            matrix.out_features,
7902        ),
7903        (ResidentBf16Weight::F32(values), Some(rows)) if strided_chunk_output => engine
7904            .linear_f32_resident_canonical_rows_strided(
7905                activations,
7906                values,
7907                tokens,
7908                matrix.in_features,
7909                matrix.out_features,
7910                rows,
7911            ),
7912        (ResidentBf16Weight::F32(values), Some(rows)) => engine.linear_f32_resident_canonical_rows(
7913            activations,
7914            values,
7915            tokens,
7916            matrix.in_features,
7917            matrix.out_features,
7918            rows,
7919        ),
7920        (ResidentBf16Weight::F32(values), None) => engine.linear(
7921            activations,
7922            values,
7923            tokens,
7924            matrix.in_features,
7925            matrix.out_features,
7926        ),
7927    }
7928}
7929
7930fn validate_resident_bf16_ranks(
7931    engines: &[Engine],
7932    ranks: &[ResidentBf16Rank],
7933) -> Result<(), String> {
7934    if engines.len() != ranks.len() {
7935        return Err(format!(
7936            "resident BF16 TP rank count {} != runtime rank count {}",
7937            ranks.len(),
7938            engines.len(),
7939        ));
7940    }
7941    for (rank, (engine, matrix)) in engines.iter().zip(ranks).enumerate() {
7942        let device = engine.ctx().ordinal();
7943        if matrix.weight.ordinal() != device {
7944            return Err(format!(
7945                "resident BF16 TP rank {rank} is not owned by runtime device {device}"
7946            ));
7947        }
7948    }
7949    Ok(())
7950}
7951
7952fn validate_step_bf16_row_residency(
7953    engines: &[Engine],
7954    matrix: &ResidentStepBf16RowParallel,
7955) -> Result<(), String> {
7956    if engines.len() != matrix.ranks.len() {
7957        return Err(format!(
7958            "resident Step BF16 row rank count {} != runtime rank count {}",
7959            matrix.ranks.len(),
7960            engines.len(),
7961        ));
7962    }
7963    let canonical_cols = step_bf16_canonical_chunk_cols(matrix.in_features, engines.len())?;
7964    if matrix.canonical_chunk_cols != canonical_cols {
7965        return Err(format!(
7966            "resident Step BF16 row canonical columns {} != registered {canonical_cols}",
7967            matrix.canonical_chunk_cols
7968        ));
7969    }
7970    let blocks_per_rank = PRODUCT_MAX_CARDS / engines.len();
7971    for (rank, (engine, blocks)) in engines.iter().zip(&matrix.ranks).enumerate() {
7972        if blocks.len() != blocks_per_rank {
7973            return Err(format!(
7974                "resident Step BF16 row rank {rank} has {} blocks, expected {blocks_per_rank}",
7975                blocks.len()
7976            ));
7977        }
7978        let device = engine.ctx().ordinal();
7979        for (block, resident) in blocks.iter().enumerate() {
7980            if resident.weight.ordinal() != device
7981                || resident.in_features != canonical_cols
7982                || resident.out_features != matrix.out_features
7983            {
7984                return Err(format!(
7985                    "resident Step BF16 row rank {rank} block {block} has inconsistent \
7986                     device or geometry"
7987                ));
7988            }
7989        }
7990    }
7991    Ok(())
7992}
7993
7994fn validate_replicated_device_rows(
7995    engines: &[Engine],
7996    rows: &ResidentReplicatedDeviceRows,
7997) -> Result<(), String> {
7998    let rank_lengths = rows
7999        .ranks
8000        .iter()
8001        .map(|rank_rows| rank_rows.len())
8002        .collect::<Vec<_>>();
8003    replicated_device_row_values(rows.tokens, rows.width, engines.len(), &rank_lengths)?;
8004    if rows
8005        .ranks
8006        .iter()
8007        .zip(engines)
8008        .any(|(rank_rows, engine)| rank_rows.ordinal() != engine.ctx().ordinal())
8009    {
8010        return Err("replicated device rows are owned by the wrong CUDA contexts".into());
8011    }
8012    Ok(())
8013}
8014
8015fn replicated_device_row_values(
8016    tokens: usize,
8017    width: usize,
8018    expected_ranks: usize,
8019    rank_lengths: &[usize],
8020) -> Result<usize, String> {
8021    let values = tokens
8022        .checked_mul(width)
8023        .ok_or("replicated device row size overflow")?;
8024    if tokens == 0
8025        || width == 0
8026        || expected_ranks == 0
8027        || rank_lengths.len() != expected_ranks
8028        || rank_lengths.iter().any(|&rank_len| rank_len != values)
8029    {
8030        return Err(format!(
8031            "replicated device rows have inconsistent geometry tokens={} width={} ranks={}/{}",
8032            tokens,
8033            width,
8034            rank_lengths.len(),
8035            expected_ranks
8036        ));
8037    }
8038    Ok(values)
8039}
8040
8041fn replicated_device_row_source_values(
8042    tokens: usize,
8043    width: usize,
8044    source_len: usize,
8045    source_device: usize,
8046    root_device: usize,
8047) -> Result<usize, String> {
8048    let values = tokens
8049        .checked_mul(width)
8050        .ok_or("replicated device row size overflow")?;
8051    if tokens == 0 || width == 0 || source_len != values || source_device != root_device {
8052        return Err(format!(
8053            "replicated device row source has inconsistent geometry/device \
8054             tokens={tokens} width={width} source={source_len}@{source_device} root={root_device}"
8055        ));
8056    }
8057    Ok(values)
8058}
8059
8060fn bf16_column_shard(
8061    matrix: Bf16Matrix<'_>,
8062    tp: usize,
8063    rank: usize,
8064) -> Result<Bf16Matrix<'_>, String> {
8065    matrix.validate()?;
8066    if tp == 0 || rank >= tp || matrix.out_features % tp != 0 {
8067        return Err(format!(
8068            "invalid BF16 column shard out={} TP={tp} rank={rank}",
8069            matrix.out_features
8070        ));
8071    }
8072    let local_out = matrix.out_features / tp;
8073    let row_bytes = matrix.in_features * 2;
8074    let start = rank * local_out * row_bytes;
8075    Ok(Bf16Matrix {
8076        bytes: &matrix.bytes[start..start + local_out * row_bytes],
8077        out_features: local_out,
8078        in_features: matrix.in_features,
8079    })
8080}
8081
8082fn bf16_row_shard(matrix: Bf16Matrix<'_>, tp: usize, rank: usize) -> Result<Vec<u8>, String> {
8083    matrix.validate()?;
8084    if tp == 0 || rank >= tp || matrix.in_features % tp != 0 {
8085        return Err(format!(
8086            "invalid BF16 row shard in={} TP={tp} rank={rank}",
8087            matrix.in_features
8088        ));
8089    }
8090    let local_in = matrix.in_features / tp;
8091    let mut bytes = Vec::with_capacity(matrix.out_features * local_in * 2);
8092    for row in 0..matrix.out_features {
8093        let start = (row * matrix.in_features + rank * local_in) * 2;
8094        bytes.extend_from_slice(&matrix.bytes[start..start + local_in * 2]);
8095    }
8096    Ok(bytes)
8097}
8098
8099fn bf16_row_block(
8100    matrix: Bf16Matrix<'_>,
8101    col_start: usize,
8102    block_cols: usize,
8103) -> Result<Vec<u8>, String> {
8104    matrix.validate()?;
8105    let col_end = col_start
8106        .checked_add(block_cols)
8107        .ok_or("BF16 row block column overflow")?;
8108    if block_cols == 0 || col_end > matrix.in_features {
8109        return Err(format!(
8110            "invalid BF16 row block columns {col_start}..{col_end} for input width {}",
8111            matrix.in_features
8112        ));
8113    }
8114    let mut bytes = Vec::with_capacity(matrix.out_features * block_cols * 2);
8115    for row in 0..matrix.out_features {
8116        let start = (row * matrix.in_features + col_start) * 2;
8117        bytes.extend_from_slice(&matrix.bytes[start..start + block_cols * 2]);
8118    }
8119    Ok(bytes)
8120}
8121
8122fn run_resident_bank_expert(
8123    engine: &Engine,
8124    bank: &ResidentE4m3ExpertBankRank,
8125    local_expert: usize,
8126    activations: &[f32],
8127    tokens: usize,
8128) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8129    let _main = engine.gpu.enter_main()?;
8130    if bank.k_blocks.is_some() {
8131        return Err("block-major TP row bank requires canonical block execution".into());
8132    }
8133    let local_count = bank.expert_range.end - bank.expert_range.start;
8134    if local_expert >= local_count {
8135        return Err(format!(
8136            "local EP expert {local_expert} outside 0..{local_count} for range {:?}",
8137            bank.expert_range
8138        )
8139        .into());
8140    }
8141    validate_activations(activations, tokens, bank.in_features)?;
8142    let activations = engine.htod(activations)?;
8143    let weight = bank
8144        .codes
8145        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8146    let scales = bank
8147        .scales
8148        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8149    let input = activations.slice(0..activations.len());
8150    let output = engine.qmatvec_mmq_fp8_blk_view(
8151        &weight,
8152        &scales,
8153        &input,
8154        tokens,
8155        bank.in_features,
8156        bank.out_features,
8157    )?;
8158    engine.dtoh(&output)
8159}
8160
8161fn run_resident_bank_expert_block(
8162    engine: &Engine,
8163    bank: &ResidentE4m3ExpertBankRank,
8164    local_expert: usize,
8165    block: usize,
8166    activations: &[f32],
8167) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8168    let _main = engine.gpu.enter_main()?;
8169    let local_count = bank.expert_range.end - bank.expert_range.start;
8170    if local_expert >= local_count {
8171        return Err(format!(
8172            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8173            bank.expert_range
8174        )
8175        .into());
8176    }
8177    let blocks = bank
8178        .k_blocks
8179        .ok_or("TP row bank is not packed in native K-block order")?;
8180    if block >= blocks {
8181        return Err(format!("TP row block {block} outside 0..{blocks}").into());
8182    }
8183    validate_activations(activations, 1, FP8_BLOCK)?;
8184    let block_code_stride = bank.out_features * FP8_BLOCK;
8185    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8186    if bank.in_features != blocks * FP8_BLOCK
8187        || bank.code_stride != blocks * block_code_stride
8188        || bank.scale_stride != blocks * block_scale_stride
8189    {
8190        return Err("TP row bank block-major geometry is inconsistent".into());
8191    }
8192
8193    let expert_code_start = local_expert * bank.code_stride;
8194    let expert_scale_start = local_expert * bank.scale_stride;
8195    let weight = bank.codes.slice(
8196        expert_code_start + block * block_code_stride
8197            ..expert_code_start + (block + 1) * block_code_stride,
8198    );
8199    let scales = bank.scales.slice(
8200        expert_scale_start + block * block_scale_stride
8201            ..expert_scale_start + (block + 1) * block_scale_stride,
8202    );
8203    let activations = engine.htod(activations)?;
8204    let input = activations.slice(0..activations.len());
8205    let output = engine.qmatvec_mmq_fp8_blk_view(
8206        &weight,
8207        &scales,
8208        &input,
8209        1,
8210        FP8_BLOCK,
8211        bank.out_features,
8212    )?;
8213    engine.dtoh(&output)
8214}
8215
8216fn run_resident_bank_expert_device(
8217    engine: &Engine,
8218    bank: &ResidentE4m3ExpertBankRank,
8219    local_expert: usize,
8220    activations: &CudaSlice<f32>,
8221    tokens: usize,
8222) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8223    let _main = engine.gpu.enter_main()?;
8224    if bank.k_blocks.is_some() {
8225        return Err("block-major TP row bank requires canonical block execution".into());
8226    }
8227    let local_count = bank.expert_range.end - bank.expert_range.start;
8228    if local_expert >= local_count {
8229        return Err(format!(
8230            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8231            bank.expert_range
8232        )
8233        .into());
8234    }
8235    let expected = tokens
8236        .checked_mul(bank.in_features)
8237        .ok_or("native TP activation size overflow")?;
8238    if activations.len() != expected || activations.ordinal() != engine.ctx().ordinal() {
8239        return Err(format!(
8240            "native TP activation len/device {}/{} != expected {expected}/{}",
8241            activations.len(),
8242            activations.ordinal(),
8243            engine.ctx().ordinal()
8244        )
8245        .into());
8246    }
8247    let weight = bank
8248        .codes
8249        .slice(local_expert * bank.code_stride..(local_expert + 1) * bank.code_stride);
8250    let scales = bank
8251        .scales
8252        .slice(local_expert * bank.scale_stride..(local_expert + 1) * bank.scale_stride);
8253    let input = activations.slice(0..activations.len());
8254    engine.qmatvec_mmq_fp8_blk_view(
8255        &weight,
8256        &scales,
8257        &input,
8258        tokens,
8259        bank.in_features,
8260        bank.out_features,
8261    )
8262}
8263
8264fn run_resident_bank_expert_block_device(
8265    engine: &Engine,
8266    bank: &ResidentE4m3ExpertBankRank,
8267    local_expert: usize,
8268    block: usize,
8269    activations: &cudarc::driver::CudaView<'_, f32>,
8270) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8271    let _main = engine.gpu.enter_main()?;
8272    let local_count = bank.expert_range.end - bank.expert_range.start;
8273    if local_expert >= local_count {
8274        return Err(format!(
8275            "local TP expert {local_expert} outside 0..{local_count} for range {:?}",
8276            bank.expert_range
8277        )
8278        .into());
8279    }
8280    let blocks = bank
8281        .k_blocks
8282        .ok_or("native TP row bank is not packed in checkpoint-block order")?;
8283    if block >= blocks {
8284        return Err(format!("native TP row block {block} outside 0..{blocks}").into());
8285    }
8286    let activation_device = activations.stream().context().ordinal();
8287    if activations.len() != FP8_BLOCK || activation_device != engine.ctx().ordinal() {
8288        return Err(format!(
8289            "native TP block activation len/device {}/{} != expected {FP8_BLOCK}/{}",
8290            activations.len(),
8291            activation_device,
8292            engine.ctx().ordinal()
8293        )
8294        .into());
8295    }
8296    let block_code_stride = bank.out_features * FP8_BLOCK;
8297    let block_scale_stride = bank.out_features.div_ceil(FP8_BLOCK);
8298    if bank.in_features != blocks * FP8_BLOCK
8299        || bank.code_stride != blocks * block_code_stride
8300        || bank.scale_stride != blocks * block_scale_stride
8301    {
8302        return Err("native TP row bank block-major geometry is inconsistent".into());
8303    }
8304    let expert_code_start = local_expert * bank.code_stride;
8305    let expert_scale_start = local_expert * bank.scale_stride;
8306    let weight = bank.codes.slice(
8307        expert_code_start + block * block_code_stride
8308            ..expert_code_start + (block + 1) * block_code_stride,
8309    );
8310    let scales = bank.scales.slice(
8311        expert_scale_start + block * block_scale_stride
8312            ..expert_scale_start + (block + 1) * block_scale_stride,
8313    );
8314    engine.qmatvec_mmq_fp8_blk_view(
8315        &weight,
8316        &scales,
8317        activations,
8318        1,
8319        FP8_BLOCK,
8320        bank.out_features,
8321    )
8322}
8323
8324fn configure_native_p2p(
8325    ranks: &[Engine],
8326    devices: &[usize],
8327) -> Result<(), Box<dyn std::error::Error>> {
8328    if ranks.len() != devices.len() || ranks.len() < 2 {
8329        return Err("native TP P2P setup requires matching multi-rank devices".into());
8330    }
8331    for (rank, (&device, engine)) in devices.iter().zip(ranks).enumerate() {
8332        if engine.ctx().ordinal() != device {
8333            return Err(format!(
8334                "native TP rank {rank} context device {} != requested device {device}",
8335                engine.ctx().ordinal()
8336            )
8337            .into());
8338        }
8339    }
8340
8341    for src in 0..ranks.len() {
8342        for dst in 0..ranks.len() {
8343            if src == dst {
8344                continue;
8345            }
8346            let mut can_access = 0;
8347            unsafe {
8348                cudarc::driver::sys::cuDeviceCanAccessPeer(
8349                    &mut can_access,
8350                    ranks[src].ctx().cu_device(),
8351                    ranks[dst].ctx().cu_device(),
8352                )
8353                .result()?;
8354            }
8355            if can_access == 0 {
8356                return Err(format!(
8357                    "native TP requires P2P, but dev{} cannot access dev{}",
8358                    devices[src], devices[dst]
8359                )
8360                .into());
8361            }
8362            ranks[src].ctx().bind_to_thread()?;
8363            let rc =
8364                unsafe { cudarc::driver::sys::cuCtxEnablePeerAccess(ranks[dst].ctx().cu_ctx(), 0) };
8365            use cudarc::driver::sys::cudaError_enum as E;
8366            if rc != E::CUDA_SUCCESS && rc != E::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED {
8367                return Err(format!(
8368                    "native TP cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
8369                    devices[src], devices[dst]
8370                )
8371                .into());
8372            }
8373        }
8374    }
8375
8376    for &owner in devices {
8377        for &accessor in devices {
8378            if owner == accessor {
8379                continue;
8380            }
8381            let device = cudarc::driver::result::device::get(owner as i32)?;
8382            let mut pool: cudarc::driver::sys::CUmemoryPool = std::ptr::null_mut();
8383            unsafe {
8384                cudarc::driver::sys::cuDeviceGetDefaultMemPool(&mut pool, device).result()?;
8385            }
8386            let desc = cudarc::driver::sys::CUmemAccessDesc {
8387                location: cudarc::driver::sys::CUmemLocation {
8388                    type_: cudarc::driver::sys::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
8389                    id: accessor as i32,
8390                },
8391                flags: cudarc::driver::sys::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
8392            };
8393            let rc = unsafe { cudarc::driver::sys::cuMemPoolSetAccess(pool, &desc, 1) };
8394            if rc != cudarc::driver::sys::cudaError_enum::CUDA_SUCCESS {
8395                return Err(format!(
8396                    "native TP cuMemPoolSetAccess(dev{owner} pool -> dev{accessor}) failed: \
8397                     {rc:?}"
8398                )
8399                .into());
8400            }
8401        }
8402    }
8403
8404    for src in 0..ranks.len() {
8405        for dst in 0..ranks.len() {
8406            if src == dst {
8407                continue;
8408            }
8409            let expected = (0..NATIVE_P2P_PROBE_WORDS)
8410                .map(|index| {
8411                    (index as u32)
8412                        .wrapping_mul(0x9e37_79b9)
8413                        .wrapping_add(((src as u32) << 16) | dst as u32)
8414                })
8415                .collect::<Vec<_>>();
8416            let poison = expected.iter().map(|value| !value).collect::<Vec<_>>();
8417            let source = ranks[src].htod_u32_v(&expected)?;
8418            let mut destination = ranks[dst].htod_u32_v(&poison)?;
8419            ranks[dst].stream().memcpy_dtod(&source, &mut destination)?;
8420            let actual = ranks[dst].dtoh_u32(&destination)?;
8421            if actual != expected {
8422                let mismatches = actual
8423                    .iter()
8424                    .zip(&expected)
8425                    .filter(|(actual, expected)| actual != expected)
8426                    .count();
8427                return Err(format!(
8428                    "native TP peer probe dev{}->dev{} failed: {mismatches}/{} words differ",
8429                    devices[src],
8430                    devices[dst],
8431                    expected.len()
8432                )
8433                .into());
8434            }
8435        }
8436    }
8437    ranks[0].ctx().bind_to_thread()?;
8438    eprintln!(
8439        "[tp] native peer byte-integrity probe PASS: devices={devices:?} \
8440         directions={} bytes={} mismatches=0",
8441        ranks.len() * (ranks.len() - 1),
8442        NATIVE_P2P_PROBE_WORDS * std::mem::size_of::<u32>(),
8443    );
8444    Ok(())
8445}
8446
8447fn validate_activations(
8448    activations: &[f32],
8449    tokens: usize,
8450    in_features: usize,
8451) -> Result<(), String> {
8452    let expected = tokens
8453        .checked_mul(in_features)
8454        .ok_or_else(|| "activation size overflow".to_string())?;
8455    if activations.len() != expected {
8456        return Err(format!(
8457            "activation count {} != {tokens}x{in_features} ({expected})",
8458            activations.len()
8459        ));
8460    }
8461    if !activations.iter().all(|value| value.is_finite()) {
8462        return Err("activations contain a non-finite value".to_string());
8463    }
8464    Ok(())
8465}
8466
8467fn column_shard(
8468    matrix: E4m3BlockMatrix<'_>,
8469    tp: usize,
8470    rank: usize,
8471) -> Result<E4m3BlockMatrix<'_>, String> {
8472    let local_out = matrix.out_features / tp;
8473    let row_start = rank * local_out;
8474    let code_start = row_start * matrix.in_features;
8475    let code_end = code_start + local_out * matrix.in_features;
8476    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8477    let local_scale_rows = local_out / FP8_BLOCK;
8478    let scale_start = rank * local_scale_rows * scale_cols;
8479    let scale_end = scale_start + local_scale_rows * scale_cols;
8480    Ok(E4m3BlockMatrix {
8481        codes: &matrix.codes[code_start..code_end],
8482        scales: &matrix.scales[scale_start..scale_end],
8483        out_features: local_out,
8484        in_features: matrix.in_features,
8485    })
8486}
8487
8488fn row_shard(
8489    matrix: E4m3BlockMatrix<'_>,
8490    tp: usize,
8491    rank: usize,
8492) -> Result<(Vec<u8>, Vec<f32>), String> {
8493    let local_in = matrix.in_features / tp;
8494    let col_start = rank * local_in;
8495    let mut codes = Vec::with_capacity(matrix.out_features * local_in);
8496    for row in 0..matrix.out_features {
8497        let start = row * matrix.in_features + col_start;
8498        codes.extend_from_slice(&matrix.codes[start..start + local_in]);
8499    }
8500
8501    let scale_rows = matrix.out_features.div_ceil(FP8_BLOCK);
8502    let scale_cols = matrix.in_features.div_ceil(FP8_BLOCK);
8503    let local_scale_cols = local_in / FP8_BLOCK;
8504    let scale_col_start = rank * local_scale_cols;
8505    let mut scales = Vec::with_capacity(scale_rows * local_scale_cols);
8506    for row in 0..scale_rows {
8507        let start = row * scale_cols + scale_col_start;
8508        scales.extend_from_slice(&matrix.scales[start..start + local_scale_cols]);
8509    }
8510    Ok((codes, scales))
8511}
8512
8513fn activation_shard(
8514    activations: &[f32],
8515    tokens: usize,
8516    in_features: usize,
8517    tp: usize,
8518    rank: usize,
8519) -> Vec<f32> {
8520    let local_in = in_features / tp;
8521    let col_start = rank * local_in;
8522    let mut shard = Vec::with_capacity(tokens * local_in);
8523    for token in 0..tokens {
8524        let start = token * in_features + col_start;
8525        shard.extend_from_slice(&activations[start..start + local_in]);
8526    }
8527    shard
8528}
8529
8530// ─── Step NVFP4 expert TP program (official Step-3.7-Flash-NVFP4 checkpoint class) ─────────────
8531//
8532// The routed experts of the NVFP4 checkpoint are modelopt-packed: e2m1 codes (2/byte), per-16
8533// UE4M3 sub-scales, and a per-EXPERT `weight_scale_2` f32 macro (~1e-5..1e-4, LOAD-BEARING).
8534// Rank compute repacks each shard host-side into memra block_nvfp4 rows (nibble reorder only —
8535// value-exact, see nvfp4_repack.rs) and runs the proven `qmatvec_nvfp4_fast` dp4a kernel; the
8536// activation q8_1 quantization uses per-32 blocks, and every shard cut here is 64-aligned, so a
8537// rank-local partial is bit-identical to the corresponding slice of the unsharded kernel.
8538//
8539// MACRO CANONICAL ORDER: the macro multiplies each assembled f32 output exactly ONCE — after the
8540// column gather (gate/up) and after the FULL row-parallel reduce (down), never per-partial.
8541// `(a + b) * m` and `a * m + b * m` differ in f32, so applying it per-rank would break the
8542// TP1-vs-TP2 bit gate. Every entry point below follows this order.
8543//
8544// TP2 shard legality is NVFP4-native: column parallelism splits whole output rows (scale rows
8545// ride along, nothing cuts), row parallelism splits input columns at 64-element superblock
8546// boundaries (16-element scale groups nest inside). The 128-block E4M3 constraint does not apply.
8547
8548/// One expert's modelopt NVFP4 projection: packed codes + per-16 UE4M3 scale bytes + macro.
8549#[derive(Clone, Copy)]
8550pub struct Nvfp4BlockMatrix<'a> {
8551    pub codes: &'a [u8],  // [out_features, in_features/2] packed e2m1, row-major
8552    pub scales: &'a [u8], // [out_features, in_features/16] UE4M3 bytes, row-major
8553    pub macro_scale: f32, // per-expert weight_scale_2 dequant multiplier
8554    pub out_features: usize,
8555    pub in_features: usize,
8556}
8557
8558impl Nvfp4BlockMatrix<'_> {
8559    pub fn validate(&self) -> Result<(), String> {
8560        if self.in_features == 0 || self.out_features == 0 {
8561            return Err("NVFP4 matrix has a zero dimension".to_string());
8562        }
8563        if self.in_features % 64 != 0 {
8564            return Err(format!(
8565                "NVFP4 in_features {} is not 64-aligned (memra block_nvfp4 superblock)",
8566                self.in_features
8567            ));
8568        }
8569        if self.codes.len() != self.out_features * self.in_features / 2 {
8570            return Err(format!(
8571                "NVFP4 code bytes {} != {}x{}/2",
8572                self.codes.len(),
8573                self.out_features,
8574                self.in_features
8575            ));
8576        }
8577        if self.scales.len() != self.out_features * self.in_features / 16 {
8578            return Err(format!(
8579                "NVFP4 scale bytes {} != {}x{}/16",
8580                self.scales.len(),
8581                self.out_features,
8582                self.in_features
8583            ));
8584        }
8585        if !self.macro_scale.is_finite() || self.macro_scale <= 0.0 {
8586            return Err(format!(
8587                "NVFP4 macro scale {} is not finite-positive",
8588                self.macro_scale
8589            ));
8590        }
8591        Ok(())
8592    }
8593}
8594
8595/// Stacked modelopt NVFP4 expert bank (host view over the checkpoint bytes).
8596#[derive(Clone, Copy)]
8597pub struct Nvfp4ExpertBank<'a> {
8598    pub codes: &'a [u8],   // [expert_count, out_features, in_features/2]
8599    pub scales: &'a [u8],  // [expert_count, out_features, in_features/16]
8600    pub macros: &'a [f32], // [expert_count] weight_scale_2
8601    pub expert_count: usize,
8602    pub out_features: usize,
8603    pub in_features: usize,
8604}
8605
8606impl Nvfp4ExpertBank<'_> {
8607    pub fn validate(&self) -> Result<(), String> {
8608        if self.expert_count == 0 {
8609            return Err("NVFP4 expert bank is empty".to_string());
8610        }
8611        if self.macros.len() != self.expert_count {
8612            return Err(format!(
8613                "NVFP4 bank macros {} != expert count {}",
8614                self.macros.len(),
8615                self.expert_count
8616            ));
8617        }
8618        self.expert(0).map(|_| ())
8619    }
8620
8621    pub fn expert(&self, expert: usize) -> Result<Nvfp4BlockMatrix<'_>, String> {
8622        if expert >= self.expert_count {
8623            return Err(format!("expert {expert} outside 0..{}", self.expert_count));
8624        }
8625        let code_stride = self.out_features * self.in_features / 2;
8626        let scale_stride = self.out_features * self.in_features / 16;
8627        if self.codes.len() != self.expert_count * code_stride
8628            || self.scales.len() != self.expert_count * scale_stride
8629        {
8630            return Err("NVFP4 bank byte extents do not match the declared geometry".to_string());
8631        }
8632        let matrix = Nvfp4BlockMatrix {
8633            codes: &self.codes[expert * code_stride..(expert + 1) * code_stride],
8634            scales: &self.scales[expert * scale_stride..(expert + 1) * scale_stride],
8635            macro_scale: self.macros[expert],
8636            out_features: self.out_features,
8637            in_features: self.in_features,
8638        };
8639        matrix.validate()?;
8640        Ok(matrix)
8641    }
8642}
8643
8644/// One rank's resident repacked NVFP4 shard: memra block_nvfp4 rows on device.
8645pub struct ResidentNvfp4Rank {
8646    blocks: crate::CudaSlice<u8>,
8647    macro_scale: f32,
8648    out_features: usize,
8649    in_features: usize,
8650    row_bytes: usize,
8651}
8652
8653pub struct ResidentNvfp4ColumnParallel {
8654    ranks: Vec<ResidentNvfp4Rank>,
8655    pub out_features: usize,
8656    pub in_features: usize,
8657}
8658
8659pub struct ResidentNvfp4RowParallel {
8660    ranks: Vec<ResidentNvfp4Rank>,
8661    pub out_features: usize,
8662    pub in_features: usize,
8663}
8664
8665pub struct ResidentTpNvfp4Expert {
8666    gate: ResidentNvfp4ColumnParallel,
8667    up: ResidentNvfp4ColumnParallel,
8668    down: ResidentNvfp4RowParallel,
8669    pub input_width: usize,
8670    pub expert_width: usize,
8671}
8672
8673/// One rank's resident NVFP4 expert bank shard: one repacked block buffer PER expert (per-expert
8674/// device allocations keep this increment off any new strided-kernel API; the strided twin is a
8675/// later perf rung, mirroring the FP8 bank's history).
8676pub struct ResidentNvfp4ColumnBankRank {
8677    /// Contiguous per-rank expert bank: `expert_count` repacked shards of `expert_bytes` each.
8678    /// Contiguity is what lets the device-routes program cover every selected expert with ONE
8679    /// launch (`qmatvec_nvfp4_dp4a_sel` indexes `sel[t] * expert_bytes`).
8680    bank: crate::CudaSlice<u8>,
8681    expert_bytes: usize,
8682    local_out: usize,
8683    in_features: usize,
8684    row_bytes: usize,
8685}
8686
8687impl ResidentNvfp4ColumnBankRank {
8688    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8689        self.bank
8690            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8691    }
8692}
8693
8694/// Canonical row-shard count for the NVFP4 down projection. The down reduction ALWAYS executes
8695/// as exactly this many input-column windows summed in shard order, at every world size: a
8696/// single full-width dot and a two-half-dots-plus-add differ in f32 parenthesization, so pinning
8697/// the shard grid (not the world size) is what makes the TP1-oracle-vs-TP2 bit gate meaningful.
8698/// This is the NVFP4 twin of the FP8 bank's canonical checkpoint-block reduction.
8699pub const NVFP4_CANONICAL_ROW_SHARDS: usize = 2;
8700
8701pub struct ResidentNvfp4RowBankRank {
8702    /// Contiguous per-shard expert bank (see `ResidentNvfp4ColumnBankRank::bank`).
8703    bank: crate::CudaSlice<u8>,
8704    expert_bytes: usize,
8705    device_rank: usize, // index into the runtime's rank engines this canonical shard lives on
8706    out_features: usize,
8707    local_in: usize,
8708    row_bytes: usize,
8709}
8710
8711impl ResidentNvfp4RowBankRank {
8712    fn expert(&self, index: usize) -> cudarc::driver::CudaView<'_, u8> {
8713        self.bank
8714            .slice(index * self.expert_bytes..(index + 1) * self.expert_bytes)
8715    }
8716}
8717
8718impl ResidentNvfp4TensorParallel {
8719    pub(crate) fn device_workspace_handle(
8720        &self,
8721    ) -> &std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>> {
8722        &self.device_workspace
8723    }
8724}
8725
8726pub struct ResidentNvfp4TensorParallel {
8727    gate: Vec<ResidentNvfp4ColumnBankRank>,
8728    up: Vec<ResidentNvfp4ColumnBankRank>,
8729    down: Vec<ResidentNvfp4RowBankRank>,
8730    macros_gate: Vec<f32>,
8731    macros_up: Vec<f32>,
8732    macros_down: Vec<f32>,
8733    /// Per-rank device copies of the gate/up macro-scales (E f32 each), indexed by the
8734    /// batched SwiGLU kernel via the selection array. Down macros stay host-side — they fold
8735    /// into the route-weight axpy scalar.
8736    macros_gate_dev: Vec<crate::CudaSlice<f32>>,
8737    macros_up_dev: Vec<crate::CudaSlice<f32>>,
8738    macros_down_dev: Vec<crate::CudaSlice<f32>>,
8739    pub expert_count: usize,
8740    pub input_width: usize,
8741    pub expert_width: usize,
8742    /// Lazily-built persistent decode workspace (device routes program). Interior mutability
8743    /// mirrors StepEpGroupedDecode: the forward holds the bank behind a shared reference.
8744    device_workspace: std::sync::Mutex<Option<Nvfp4DeviceRoutesWorkspace>>,
8745    /// Lazily-built spec-verify t=2 workspace (MEMRA_TCOL_FFN): the two-column routed
8746    /// sweep's slabs and events, kept apart from the serving workspace so the verify walk
8747    /// never perturbs serving state.
8748    t2_workspace: std::sync::Mutex<Option<Nvfp4T2Workspace>>,
8749    /// MEMRA_STEP_NVFP4_EP2: the rank banks above hold WHOLE experts (owner = id & 1,
8750    /// slot = id >> 1) at full width instead of TP shards. Consumers must branch on this;
8751    /// shard-semantics paths refuse loudly.
8752    pub(crate) ep2: bool,
8753}
8754
8755/// Persistent buffers for the two-column (spec verify) NVFP4 device-routed program: every
8756/// slab is the t=1 workspace shape doubled along the pair axis, plus per-column
8757/// accumulators. One per expert bank, reused every (round, layer) call.
8758pub struct Nvfp4T2Workspace {
8759    input2: Vec<crate::CudaSlice<f32>>,
8760    in_q2: Vec<crate::CudaSlice<i8>>,
8761    in_d2: Vec<crate::CudaSlice<f32>>,
8762    sel2: Vec<crate::CudaSlice<i32>>,
8763    route_w2: Vec<crate::CudaSlice<f32>>,
8764    gate_out2: Vec<crate::CudaSlice<f32>>,
8765    up_out2: Vec<crate::CudaSlice<f32>>,
8766    act_q2: Vec<crate::CudaSlice<i8>>,
8767    act_d2: Vec<crate::CudaSlice<f32>>,
8768    partial2: Vec<crate::CudaSlice<f32>>,
8769    /// Per-rank per-column combine accumulators ([width] each).
8770    acc_a: Vec<crate::CudaSlice<f32>>,
8771    acc_b: Vec<crate::CudaSlice<f32>>,
8772    /// down8_t2 arm: per-rank [2, width] combined slab, root peer pull and joined slab —
8773    /// the fused kernel writes both columns, so the join is ONE pull + ONE add.
8774    acc2: Vec<crate::CudaSlice<f32>>,
8775    peer2: crate::CudaSlice<f32>,
8776    omix2: crate::CudaSlice<f32>,
8777    /// Root-side pulls of rank1's accumulators and the joined columns.
8778    peer_a: crate::CudaSlice<f32>,
8779    peer_b: crate::CudaSlice<f32>,
8780    omix_a: crate::CudaSlice<f32>,
8781    omix_b: crate::CudaSlice<f32>,
8782    ev_entry: CudaEvent,
8783    ev_rank: Vec<CudaEvent>,
8784    ev_root: CudaEvent,
8785    n_sel: usize,
8786    e_device: usize,
8787}
8788
8789/// Persistent per-call device buffers for the NVFP4 device routes program: one gate/up output,
8790/// one down partial, and one shard accumulator per rank, plus root combine staging. Reused every
8791/// (token, layer) call so the decode loop performs zero output allocations.
8792/// A stitched multi-device parent graph for one layer's device-routed expert program, plus
8793/// the children it was built from (retained: AddChildGraphNode clones, but the probe retains
8794/// conservatively) and the persistent e-context input staging its copies read.
8795struct RoutesGraph {
8796    exec: cudarc::driver::sys::CUgraphExec,
8797    parent: cudarc::driver::sys::CUgraph,
8798    _children: Vec<cudarc::driver::CudaGraph>,
8799}
8800// SAFETY: the raw handles are only used from the single decode thread; CUDA graph handles are
8801// context-agnostic process handles.
8802unsafe impl Send for RoutesGraph {}
8803
8804impl Drop for RoutesGraph {
8805    fn drop(&mut self) {
8806        unsafe {
8807            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
8808            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
8809        }
8810    }
8811}
8812
8813impl Nvfp4DeviceRoutesWorkspace {
8814    pub(crate) fn in_stage_handle(&self) -> Option<&crate::CudaSlice<f32>> {
8815        self.in_stage_e.as_ref()
8816    }
8817    pub(crate) fn in_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8818        self.in_stage_e.as_mut()
8819    }
8820    pub(crate) fn out_stage_mut(&mut self) -> Option<&mut crate::CudaSlice<f32>> {
8821        self.out_stage_e.as_mut()
8822    }
8823    /// Arm the e-context stages + router staging pair when absent (token-graph entry).
8824    pub(crate) fn arm_stages(
8825        &mut self,
8826        e: &Engine,
8827        width: usize,
8828        n_sel: usize,
8829    ) -> Result<(), Box<dyn std::error::Error>> {
8830        let _main = e.gpu.enter_main()?;
8831        if self.in_stage_e.is_none() {
8832            self.in_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8833            self.out_stage_e = Some(e.htod(&vec![0.0f32; width])?);
8834        }
8835        if self.dev_route_e.is_none() {
8836            self.dev_route_e = Some((
8837                e.htod_i32(&vec![0i32; n_sel])?,
8838                e.htod(&vec![0.0f32; n_sel])?,
8839            ));
8840        }
8841        Ok(())
8842    }
8843
8844    /// Split-borrow: the routes input (shared) + output (mut) stages together.
8845    pub(crate) fn in_and_out_stages_mut(
8846        &mut self,
8847    ) -> Option<(&crate::CudaSlice<f32>, &mut crate::CudaSlice<f32>)> {
8848        match (self.in_stage_e.as_ref(), self.out_stage_e.as_mut()) {
8849            (Some(input), Some(output)) => Some((input, output)),
8850            _ => None,
8851        }
8852    }
8853    pub(crate) fn dev_route_e_mut(
8854        &mut self,
8855    ) -> Option<(&mut crate::CudaSlice<i32>, &mut crate::CudaSlice<f32>)> {
8856        self.dev_route_e.as_mut().map(|(a, b)| (a, b))
8857    }
8858}
8859
8860pub struct Nvfp4DeviceRoutesWorkspace {
8861    /// [n_sel, local_out] batched gate/up outputs and the SwiGLU q8_1 pair; [n_sel, width]
8862    /// down partials. Sized for `n_sel` selected experts per token (pinned at first call).
8863    gate_out: Vec<crate::CudaSlice<f32>>,
8864    up_out: Vec<crate::CudaSlice<f32>>,
8865    act_q: Vec<crate::CudaSlice<i8>>,
8866    act_d: Vec<crate::CudaSlice<f32>>,
8867    sel: Vec<crate::CudaSlice<i32>>,
8868    partial: Vec<crate::CudaSlice<f32>>,
8869    accumulator: Vec<crate::CudaSlice<f32>>,
8870    /// Per-rank folded combine weights (route_weight x down macro), one htod per call.
8871    combine_w: Vec<crate::CudaSlice<f32>>,
8872    /// Device-routed extension: per-rank raw route weights (the down-macro fold happens
8873    /// in-kernel via sel + macros_down_dev).
8874    route_w: Vec<crate::CudaSlice<f32>>,
8875    /// Persistent q8_1 pair of the shared layer input (one quantize per rank per call, no
8876    /// per-call allocation).
8877    in_q: Vec<crate::CudaSlice<i8>>,
8878    in_d: Vec<crate::CudaSlice<f32>>,
8879    /// e-context staging for the device router outputs (persistent — rank streams peer-read
8880    /// them, so the router's fresh outputs are copied here on e's stream first; the pp.rs
8881    /// never-free discipline).
8882    dev_route_e: Option<(crate::CudaSlice<i32>, crate::CudaSlice<f32>)>,
8883    /// Prestage door state: input pull + quantize already issued for this layer's call
8884    /// (nvfp4_routes_prestage), so the routed run skips them. Reset per call.
8885    prestaged: bool,
8886    /// Peer-router door state: rank1's sel/route_w were computed locally in prestage;
8887    /// the routed run skips rank1's sel pull. Reset per call.
8888    rank1_routed: bool,
8889    /// Doorbell fences (MEMRA_FENCE_MEMOPS): raw cuMemAlloc'd [rank1_flag, root_flag]
8890    /// u32 pair in ROOT memory (async-pool memory is memop-INELIGIBLE — receipted
8891    /// CUDA_ERROR_INVALID_VALUE) + the host-side monotonic ticket. 0 = unarmed.
8892    fence_flags_raw: u64,
8893    fence_ticket: u32,
8894    /// Prestage input fence, recorded on e after the input's producer.
8895    ev_input: Option<(CudaEvent, usize)>,
8896    /// Graph-door staging: persistent e-context input row + output row (fixed addresses the
8897    /// captured copies read/write), and the per-layer stitched parent.
8898    in_stage_e: Option<crate::CudaSlice<f32>>,
8899    out_stage_e: Option<crate::CudaSlice<f32>>,
8900    routes_graph: Option<RoutesGraph>,
8901    /// Token-graph raw pointer sets (armed once by routes_arm_raw).
8902    raw_dev_route_e: Option<(u64, u64)>,
8903    raw_combine: Option<(u64, u64, u64, u64)>,
8904    raw_input: Vec<u64>,
8905    raw_sel: Vec<u64>,
8906    raw_route_w: Vec<u64>,
8907    remote: crate::CudaSlice<f32>,
8908    combined: crate::CudaSlice<f32>,
8909    n_sel: usize,
8910    /// Device-IO extension (lazily built by `run_tensor_parallel_routes_nvfp4_device_io`):
8911    /// persistent per-rank input rows plus the evented ordering pair — the pp.rs
8912    /// BoundarySlot discipline, same as the v2 attention workspace.
8913    input: Vec<crate::CudaSlice<f32>>,
8914    ev_rank: Vec<CudaEvent>,
8915    ev_done: Option<CudaEvent>,
8916    ev_entry: Option<(CudaEvent, usize)>,
8917}
8918
8919/// One rank's whole-expert NVFP4 residency (expert-parallel ownership).
8920struct ResidentNvfp4EpRank {
8921    gate: Vec<crate::CudaSlice<u8>>,
8922    up: Vec<crate::CudaSlice<u8>>,
8923    down: Vec<crate::CudaSlice<u8>>,
8924    #[allow(dead_code)]
8925    expert_range: Range<usize>,
8926}
8927
8928pub struct ResidentNvfp4ExpertParallel {
8929    ranks: Vec<ResidentNvfp4EpRank>,
8930    macros_gate: Vec<f32>,
8931    macros_up: Vec<f32>,
8932    macros_down: Vec<f32>,
8933    pub expert_count: usize,
8934    pub input_width: usize,
8935    pub expert_width: usize,
8936    gate_row_bytes: usize,
8937    down_row_bytes: usize,
8938}
8939
8940fn nvfp4_repack_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
8941    memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
8942        matrix.codes,
8943        matrix.scales,
8944        matrix.out_features,
8945        matrix.in_features,
8946    )
8947}
8948
8949fn nvfp4_row_bytes(in_features: usize) -> usize {
8950    in_features / 64 * 36 // memra block_nvfp4: 64 elems -> 36 bytes (4 UE4M3 + 32 packed e2m1)
8951}
8952
8953/// MEMRA_NVFP4_BANK_V2=1: store the contiguous expert banks in the slot-major layout the
8954/// coalesced `*_v2` kernels read (see qmatvec.cu). Pure byte permutation — value-exact.
8955/// MEMRA_NO_LOCAL_SHADOW=1: skip the per-layer local-KV shadow gathers and appends in the
8956/// eager v2 decode (lengths still advance) — the graph door proved contents-stale local KV
8957/// is decode-identical (12/12). The local contents feed spec/MTP scratch only.
8958/// MEMRA_FUSE_ROPE_APPEND=1: fuse qk norms + rope + dcw KV append + len inc into one
8959/// launch per rank per layer (bit-identical; identity-gated). dcw path only.
8960pub(crate) fn fuse_rope_append_on() -> bool {
8961    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8962    *ON.get_or_init(|| std::env::var("MEMRA_FUSE_ROPE_APPEND").as_deref() == Ok("1"))
8963}
8964
8965pub(crate) fn no_local_shadow_on() -> bool {
8966    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8967    *ON.get_or_init(|| std::env::var("MEMRA_NO_LOCAL_SHADOW").as_deref() == Ok("1"))
8968}
8969
8970pub(crate) fn nvfp4_bank_v2_on() -> bool {
8971    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8972    *ON.get_or_init(|| std::env::var("MEMRA_NVFP4_BANK_V2").as_deref() == Ok("1"))
8973}
8974
8975/// Permute one repacked block_nvfp4 matrix (out_features rows of `nvfp4_row_bytes(in_f)`)
8976/// into the slot-major v2 row layout: per row, slot g's 16 qs bytes at g*16, then the two
8977/// UE4M3 scale bytes per slot at nslots*16 + g*2. Row byte count unchanged.
8978fn nvfp4_matrix_v2_permute(v1: &[u8], out_features: usize, in_features: usize) -> Vec<u8> {
8979    let row_bytes = nvfp4_row_bytes(in_features);
8980    assert_eq!(v1.len(), out_features * row_bytes, "v2 permute geometry");
8981    let n_slots = in_features / 32;
8982    let mut out = Vec::with_capacity(v1.len());
8983    for row in 0..out_features {
8984        let r = &v1[row * row_bytes..(row + 1) * row_bytes];
8985        for g in 0..n_slots {
8986            let (sblk, h) = (g / 2, g % 2);
8987            let b = &r[sblk * 36..sblk * 36 + 36];
8988            out.extend_from_slice(&b[4 + 16 * h..4 + 16 * h + 16]);
8989        }
8990        for g in 0..n_slots {
8991            let (sblk, h) = (g / 2, g % 2);
8992            let b = &r[sblk * 36..sblk * 36 + 36];
8993            out.push(b[2 * h]);
8994            out.push(b[2 * h + 1]);
8995        }
8996    }
8997    out
8998}
8999
9000/// Repack + (optionally) v2-permute one expert shard for the contiguous banks.
9001fn nvfp4_repack_bank_matrix(matrix: Nvfp4BlockMatrix<'_>) -> Vec<u8> {
9002    let (out_features, in_features) = (matrix.out_features, matrix.in_features);
9003    let v1 = nvfp4_repack_matrix(matrix);
9004    if nvfp4_bank_v2_on() {
9005        nvfp4_matrix_v2_permute(&v1, out_features, in_features)
9006    } else {
9007        v1
9008    }
9009}
9010
9011/// Column shard: whole output rows per rank (codes and scales are row-major, so both slices are
9012/// contiguous borrows). The macro rides unchanged — it is applied post-gather by the caller.
9013fn nvfp4_column_shard<'a>(
9014    matrix: Nvfp4BlockMatrix<'a>,
9015    tp: usize,
9016    rank: usize,
9017) -> Result<Nvfp4BlockMatrix<'a>, String> {
9018    if matrix.out_features % tp != 0 {
9019        return Err(format!(
9020            "NVFP4 column-parallel out_features {} is not divisible by TP={tp}",
9021            matrix.out_features
9022        ));
9023    }
9024    let local_out = matrix.out_features / tp;
9025    let code_row = matrix.in_features / 2;
9026    let scale_row = matrix.in_features / 16;
9027    Ok(Nvfp4BlockMatrix {
9028        codes: &matrix.codes[rank * local_out * code_row..(rank + 1) * local_out * code_row],
9029        scales: &matrix.scales[rank * local_out * scale_row..(rank + 1) * local_out * scale_row],
9030        macro_scale: matrix.macro_scale,
9031        out_features: local_out,
9032        in_features: matrix.in_features,
9033    })
9034}
9035
9036/// Row shard: input-column windows per rank, 64-superblock aligned. Owned buffers: each output
9037/// row contributes one contiguous byte window, gathered across rows.
9038fn nvfp4_row_shard(
9039    matrix: Nvfp4BlockMatrix<'_>,
9040    tp: usize,
9041    rank: usize,
9042) -> Result<(Vec<u8>, Vec<u8>, usize), String> {
9043    if matrix.in_features % tp != 0 {
9044        return Err(format!(
9045            "NVFP4 row-parallel in_features {} is not divisible by TP={tp}",
9046            matrix.in_features
9047        ));
9048    }
9049    let local_in = matrix.in_features / tp;
9050    if local_in % 64 != 0 {
9051        return Err(format!(
9052            "NVFP4 row-parallel input shard {local_in} cuts through a 64-element superblock"
9053        ));
9054    }
9055    let code_row = matrix.in_features / 2;
9056    let scale_row = matrix.in_features / 16;
9057    let local_code = local_in / 2;
9058    let local_scale = local_in / 16;
9059    let mut codes = Vec::with_capacity(matrix.out_features * local_code);
9060    let mut scales = Vec::with_capacity(matrix.out_features * local_scale);
9061    for row in 0..matrix.out_features {
9062        let code_start = row * code_row + rank * local_code;
9063        codes.extend_from_slice(&matrix.codes[code_start..code_start + local_code]);
9064        let scale_start = row * scale_row + rank * local_scale;
9065        scales.extend_from_slice(&matrix.scales[scale_start..scale_start + local_scale]);
9066    }
9067    Ok((codes, scales, local_in))
9068}
9069
9070/// Rank compute leaf: repack modelopt -> block_nvfp4, upload, run the proven dp4a kernel. The
9071/// macro is NOT applied here — callers apply it once at the canonical post-gather/post-reduce
9072/// point (see the section header).
9073fn run_rank_nvfp4(
9074    engine: &Engine,
9075    matrix: Nvfp4BlockMatrix<'_>,
9076    activations: &[f32],
9077    tokens: usize,
9078) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9079    matrix.validate()?;
9080    validate_activations(activations, tokens, matrix.in_features)?;
9081    let _main = engine.gpu.enter_main()?;
9082    let blocks = engine.htod_bytes(&nvfp4_repack_matrix(matrix))?;
9083    let activations = engine.htod(activations)?;
9084    let output = engine.qmatvec_nvfp4_fast(
9085        &blocks.slice(0..blocks.len()),
9086        &activations,
9087        tokens,
9088        matrix.in_features,
9089        matrix.out_features,
9090        nvfp4_row_bytes(matrix.in_features),
9091    )?;
9092    engine.dtoh(&output)
9093}
9094
9095fn upload_rank_nvfp4(
9096    engine: &Engine,
9097    matrix: Nvfp4BlockMatrix<'_>,
9098) -> Result<ResidentNvfp4Rank, Box<dyn std::error::Error>> {
9099    matrix.validate()?;
9100    let _main = engine.gpu.enter_main()?;
9101    Ok(ResidentNvfp4Rank {
9102        blocks: engine.htod_bytes(&nvfp4_repack_matrix(matrix))?,
9103        macro_scale: matrix.macro_scale,
9104        out_features: matrix.out_features,
9105        in_features: matrix.in_features,
9106        row_bytes: nvfp4_row_bytes(matrix.in_features),
9107    })
9108}
9109
9110fn run_resident_rank_nvfp4(
9111    engine: &Engine,
9112    rank: &ResidentNvfp4Rank,
9113    activations: &[f32],
9114    tokens: usize,
9115) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9116    validate_activations(activations, tokens, rank.in_features)?;
9117    let _main = engine.gpu.enter_main()?;
9118    let activations = engine.htod(activations)?;
9119    let output = engine.qmatvec_nvfp4_fast(
9120        &rank.blocks.slice(0..rank.blocks.len()),
9121        &activations,
9122        tokens,
9123        rank.in_features,
9124        rank.out_features,
9125        rank.row_bytes,
9126    )?;
9127    engine.dtoh(&output)
9128}
9129
9130fn apply_macro(values: &mut [f32], macro_scale: f32) {
9131    for value in values.iter_mut() {
9132        *value *= macro_scale;
9133    }
9134}
9135
9136impl TpE4m3HostBounce {
9137    /// Unsharded NVFP4 projection on rank 0 (compatibility oracle). Macro applied post-kernel.
9138    pub fn full_nvfp4(
9139        &self,
9140        matrix: Nvfp4BlockMatrix<'_>,
9141        activations: &[f32],
9142        tokens: usize,
9143    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9144        let mut output = run_rank_nvfp4(&self.ranks[0], matrix, activations, tokens)?;
9145        apply_macro(&mut output, matrix.macro_scale);
9146        Ok(output)
9147    }
9148
9149    /// Column-parallel NVFP4 projection: output rows partition across ranks, host gather in rank
9150    /// order, macro applied ONCE post-gather.
9151    pub fn column_parallel_nvfp4(
9152        &self,
9153        matrix: Nvfp4BlockMatrix<'_>,
9154        activations: &[f32],
9155        tokens: usize,
9156    ) -> Result<ColumnParallelResult, Box<dyn std::error::Error>> {
9157        matrix.validate()?;
9158        validate_activations(activations, tokens, matrix.in_features)?;
9159        let tp = self.ranks.len();
9160        let local_out = matrix.out_features / tp;
9161        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9162        let mut rank_outputs = Vec::with_capacity(tp);
9163        for (rank_index, rank) in self.ranks.iter().enumerate() {
9164            let shard = nvfp4_column_shard(matrix, tp, rank_index)?;
9165            let output = run_rank_nvfp4(rank, shard, activations, tokens)?;
9166            let row_start = rank_index * local_out;
9167            for token in 0..tokens {
9168                gathered[token * matrix.out_features + row_start
9169                    ..token * matrix.out_features + row_start + local_out]
9170                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9171            }
9172            rank_outputs.push(output);
9173        }
9174        apply_macro(&mut gathered, matrix.macro_scale);
9175        Ok(ColumnParallelResult {
9176            gathered,
9177            rank_outputs,
9178        })
9179    }
9180
9181    /// Row-parallel NVFP4 projection: input columns partition at 64-superblock boundaries,
9182    /// rank-local partials reduce in stable rank order, macro applied ONCE post-reduce.
9183    pub fn row_parallel_nvfp4(
9184        &self,
9185        matrix: Nvfp4BlockMatrix<'_>,
9186        activations: &[f32],
9187        tokens: usize,
9188    ) -> Result<RowParallelResult, Box<dyn std::error::Error>> {
9189        matrix.validate()?;
9190        validate_activations(activations, tokens, matrix.in_features)?;
9191        let tp = self.ranks.len();
9192        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9193        let mut rank_partials = Vec::with_capacity(tp);
9194        for (rank_index, rank) in self.ranks.iter().enumerate() {
9195            let (codes, scales, local_in) = nvfp4_row_shard(matrix, tp, rank_index)?;
9196            let local_activations =
9197                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9198            let shard = Nvfp4BlockMatrix {
9199                codes: &codes,
9200                scales: &scales,
9201                macro_scale: matrix.macro_scale,
9202                out_features: matrix.out_features,
9203                in_features: local_in,
9204            };
9205            let partial = run_rank_nvfp4(rank, shard, &local_activations, tokens)?;
9206            for (sum, value) in reduced.iter_mut().zip(&partial) {
9207                *sum += *value;
9208            }
9209            rank_partials.push(partial);
9210        }
9211        apply_macro(&mut reduced, matrix.macro_scale);
9212        Ok(RowParallelResult {
9213            reduced,
9214            rank_partials,
9215        })
9216    }
9217
9218    pub fn upload_expert_nvfp4(
9219        &self,
9220        gate: Nvfp4BlockMatrix<'_>,
9221        up: Nvfp4BlockMatrix<'_>,
9222        down: Nvfp4BlockMatrix<'_>,
9223    ) -> Result<ResidentTpNvfp4Expert, Box<dyn std::error::Error>> {
9224        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9225            return Err("NVFP4 TP expert gate/up dimensions differ".into());
9226        }
9227        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9228            return Err(format!(
9229                "NVFP4 TP expert down {}x{} does not invert gate/up {}x{}",
9230                down.out_features, down.in_features, gate.out_features, gate.in_features
9231            )
9232            .into());
9233        }
9234        let tp = self.ranks.len();
9235        let mut gate_ranks = Vec::with_capacity(tp);
9236        let mut up_ranks = Vec::with_capacity(tp);
9237        let mut down_ranks = Vec::with_capacity(tp);
9238        for (rank_index, engine) in self.ranks.iter().enumerate() {
9239            gate_ranks.push(upload_rank_nvfp4(
9240                engine,
9241                nvfp4_column_shard(gate, tp, rank_index)?,
9242            )?);
9243            up_ranks.push(upload_rank_nvfp4(
9244                engine,
9245                nvfp4_column_shard(up, tp, rank_index)?,
9246            )?);
9247            let (codes, scales, local_in) = nvfp4_row_shard(down, tp, rank_index)?;
9248            down_ranks.push(upload_rank_nvfp4(
9249                engine,
9250                Nvfp4BlockMatrix {
9251                    codes: &codes,
9252                    scales: &scales,
9253                    macro_scale: down.macro_scale,
9254                    out_features: down.out_features,
9255                    in_features: local_in,
9256                },
9257            )?);
9258        }
9259        Ok(ResidentTpNvfp4Expert {
9260            gate: ResidentNvfp4ColumnParallel {
9261                ranks: gate_ranks,
9262                out_features: gate.out_features,
9263                in_features: gate.in_features,
9264            },
9265            up: ResidentNvfp4ColumnParallel {
9266                ranks: up_ranks,
9267                out_features: up.out_features,
9268                in_features: up.in_features,
9269            },
9270            down: ResidentNvfp4RowParallel {
9271                ranks: down_ranks,
9272                out_features: down.out_features,
9273                in_features: down.in_features,
9274            },
9275            input_width: gate.in_features,
9276            expert_width: gate.out_features,
9277        })
9278    }
9279
9280    fn column_parallel_resident_nvfp4(
9281        &self,
9282        matrix: &ResidentNvfp4ColumnParallel,
9283        activations: &[f32],
9284        tokens: usize,
9285    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9286        validate_activations(activations, tokens, matrix.in_features)?;
9287        let local_out = matrix.out_features / self.ranks.len();
9288        let mut gathered = vec![0.0f32; tokens * matrix.out_features];
9289        let mut macro_scale = None;
9290        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9291            let output = run_resident_rank_nvfp4(engine, shard, activations, tokens)?;
9292            let row_start = rank_index * local_out;
9293            for token in 0..tokens {
9294                gathered[token * matrix.out_features + row_start
9295                    ..token * matrix.out_features + row_start + local_out]
9296                    .copy_from_slice(&output[token * local_out..(token + 1) * local_out]);
9297            }
9298            macro_scale = Some(shard.macro_scale);
9299        }
9300        apply_macro(
9301            &mut gathered,
9302            macro_scale.ok_or("NVFP4 column-parallel matrix has no ranks")?,
9303        );
9304        Ok(gathered)
9305    }
9306
9307    fn row_parallel_resident_nvfp4(
9308        &self,
9309        matrix: &ResidentNvfp4RowParallel,
9310        activations: &[f32],
9311        tokens: usize,
9312    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9313        validate_activations(activations, tokens, matrix.in_features)?;
9314        let tp = self.ranks.len();
9315        let local_in = matrix.in_features / tp;
9316        let mut reduced = vec![0.0f32; tokens * matrix.out_features];
9317        let mut macro_scale = None;
9318        for (rank_index, (engine, shard)) in self.ranks.iter().zip(&matrix.ranks).enumerate() {
9319            if shard.in_features != local_in {
9320                return Err(format!(
9321                    "NVFP4 resident row shard in_features {} != expected {local_in}",
9322                    shard.in_features
9323                )
9324                .into());
9325            }
9326            let local_activations =
9327                activation_shard(activations, tokens, matrix.in_features, tp, rank_index);
9328            let partial = run_resident_rank_nvfp4(engine, shard, &local_activations, tokens)?;
9329            for (sum, value) in reduced.iter_mut().zip(&partial) {
9330                *sum += *value;
9331            }
9332            macro_scale = Some(shard.macro_scale);
9333        }
9334        apply_macro(
9335            &mut reduced,
9336            macro_scale.ok_or("NVFP4 row-parallel matrix has no ranks")?,
9337        );
9338        Ok(reduced)
9339    }
9340
9341    pub fn run_expert_nvfp4(
9342        &self,
9343        expert: &ResidentTpNvfp4Expert,
9344        input: &[f32],
9345        tokens: usize,
9346    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9347        validate_activations(input, tokens, expert.input_width)?;
9348        let gate = self.column_parallel_resident_nvfp4(&expert.gate, input, tokens)?;
9349        let up = self.column_parallel_resident_nvfp4(&expert.up, input, tokens)?;
9350        let activated: Vec<f32> = gate
9351            .iter()
9352            .zip(&up)
9353            .map(|(&gate, &up)| gate / (1.0 + (-gate).exp()) * up)
9354            .collect();
9355        debug_assert_eq!(activated.len(), tokens * expert.expert_width);
9356        self.row_parallel_resident_nvfp4(&expert.down, &activated, tokens)
9357    }
9358
9359    /// Upload every expert's TP shards resident (one repacked block buffer per expert per rank).
9360    pub fn upload_tensor_parallel_nvfp4(
9361        &self,
9362        gate: Nvfp4ExpertBank<'_>,
9363        up: Nvfp4ExpertBank<'_>,
9364        down: Nvfp4ExpertBank<'_>,
9365    ) -> Result<ResidentNvfp4TensorParallel, Box<dyn std::error::Error>> {
9366        gate.validate()?;
9367        up.validate()?;
9368        down.validate()?;
9369        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9370            return Err("NVFP4 TP gate/up/down expert counts differ".into());
9371        }
9372        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9373            return Err("NVFP4 TP gate/up dimensions differ".into());
9374        }
9375        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9376            return Err(format!(
9377                "NVFP4 TP down {}x{} does not invert gate/up {}x{}",
9378                down.out_features, down.in_features, gate.out_features, gate.in_features
9379            )
9380            .into());
9381        }
9382        let tp = self.ranks.len();
9383        if gate.out_features % tp != 0 {
9384            return Err(format!(
9385                "NVFP4 TP expert output width {} is not divisible by TP={tp}",
9386                gate.out_features
9387            )
9388            .into());
9389        }
9390        if down.in_features % NVFP4_CANONICAL_ROW_SHARDS != 0
9391            || (down.in_features / NVFP4_CANONICAL_ROW_SHARDS) % 64 != 0
9392        {
9393            return Err(format!(
9394                "NVFP4 TP expert input width {} does not split into 64-aligned canonical \
9395                 shards ({NVFP4_CANONICAL_ROW_SHARDS})",
9396                down.in_features
9397            )
9398            .into());
9399        }
9400        if tp > NVFP4_CANONICAL_ROW_SHARDS {
9401            return Err(format!(
9402                "NVFP4 TP world {tp} exceeds the canonical row-shard grid \
9403                 ({NVFP4_CANONICAL_ROW_SHARDS})"
9404            )
9405            .into());
9406        }
9407
9408        let ep2 = step_nvfp4_ep2_on() && tp == 2;
9409        let mut gate_ranks = Vec::with_capacity(tp);
9410        let mut up_ranks = Vec::with_capacity(tp);
9411        let mut macros_gate_dev = Vec::with_capacity(tp);
9412        let mut macros_up_dev = Vec::with_capacity(tp);
9413        let mut macros_down_dev = Vec::with_capacity(tp);
9414        for (rank_index, engine) in self.ranks.iter().enumerate() {
9415            let _main = engine.gpu.enter_main()?;
9416            // Contiguous per-rank banks: repack every expert shard into one host buffer, one
9417            // upload. Contiguity feeds the batched selected-experts launch; per-expert bytes
9418            // are unchanged (same repack).
9419            // EP2: this rank holds the FULL matrices of the experts it owns (id & 1 ==
9420            // rank_index), stacked at slot id >> 1 — same total bytes as the shard bank.
9421            let mut gate_host: Vec<u8> = Vec::new();
9422            let mut up_host: Vec<u8> = Vec::new();
9423            let mut owned = 0usize;
9424            for expert in 0..gate.expert_count {
9425                if ep2 {
9426                    if expert % 2 != rank_index {
9427                        continue;
9428                    }
9429                    owned += 1;
9430                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate.expert(expert)?));
9431                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up.expert(expert)?));
9432                } else {
9433                    let gate_shard = nvfp4_column_shard(gate.expert(expert)?, tp, rank_index)?;
9434                    gate_host.extend_from_slice(&nvfp4_repack_bank_matrix(gate_shard));
9435                    let up_shard = nvfp4_column_shard(up.expert(expert)?, tp, rank_index)?;
9436                    up_host.extend_from_slice(&nvfp4_repack_bank_matrix(up_shard));
9437                }
9438            }
9439            let bank_experts = if ep2 { owned } else { gate.expert_count };
9440            let gate_expert_bytes = gate_host.len() / bank_experts.max(1);
9441            let up_expert_bytes = up_host.len() / bank_experts.max(1);
9442            let local_out = if ep2 {
9443                gate.out_features
9444            } else {
9445                gate.out_features / tp
9446            };
9447            gate_ranks.push(ResidentNvfp4ColumnBankRank {
9448                bank: engine.htod_bytes(&gate_host)?,
9449                expert_bytes: gate_expert_bytes,
9450                local_out,
9451                in_features: gate.in_features,
9452                row_bytes: nvfp4_row_bytes(gate.in_features),
9453            });
9454            up_ranks.push(ResidentNvfp4ColumnBankRank {
9455                bank: engine.htod_bytes(&up_host)?,
9456                expert_bytes: up_expert_bytes,
9457                local_out,
9458                in_features: up.in_features,
9459                row_bytes: nvfp4_row_bytes(up.in_features),
9460            });
9461            macros_gate_dev.push(engine.htod(gate.macros)?);
9462            macros_up_dev.push(engine.htod(up.macros)?);
9463            macros_down_dev.push(engine.htod(down.macros)?);
9464        }
9465        // Down: canonical shard grid, NOT the world size (see NVFP4_CANONICAL_ROW_SHARDS).
9466        // Shard s lives on rank s % world, so TP1 holds both shards and TP2 one each, while the
9467        // execution and reduction order stay identical.
9468        let mut down_ranks = Vec::with_capacity(NVFP4_CANONICAL_ROW_SHARDS);
9469        for shard_index in 0..NVFP4_CANONICAL_ROW_SHARDS {
9470            let device_rank = shard_index % tp;
9471            let engine = &self.ranks[device_rank];
9472            let _main = engine.gpu.enter_main()?;
9473            let mut down_host: Vec<u8> = Vec::new();
9474            let mut owned = 0usize;
9475            for expert in 0..down.expert_count {
9476                let down_matrix = down.expert(expert)?;
9477                if ep2 {
9478                    // EP2: shard_index doubles as the owner rank; full-width down matrices
9479                    // of the owned experts, stacked at slot id >> 1.
9480                    if expert % 2 != device_rank {
9481                        continue;
9482                    }
9483                    owned += 1;
9484                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(down_matrix));
9485                } else {
9486                    let (codes, scales, local_in) =
9487                        nvfp4_row_shard(down_matrix, NVFP4_CANONICAL_ROW_SHARDS, shard_index)?;
9488                    down_host.extend_from_slice(&nvfp4_repack_bank_matrix(Nvfp4BlockMatrix {
9489                        codes: &codes,
9490                        scales: &scales,
9491                        macro_scale: down_matrix.macro_scale,
9492                        out_features: down_matrix.out_features,
9493                        in_features: local_in,
9494                    }));
9495                }
9496            }
9497            let bank_experts = if ep2 { owned } else { down.expert_count };
9498            let down_expert_bytes = down_host.len() / bank_experts.max(1);
9499            let local_in = if ep2 {
9500                down.in_features
9501            } else {
9502                down.in_features / NVFP4_CANONICAL_ROW_SHARDS
9503            };
9504            down_ranks.push(ResidentNvfp4RowBankRank {
9505                bank: engine.htod_bytes(&down_host)?,
9506                expert_bytes: down_expert_bytes,
9507                device_rank,
9508                out_features: down.out_features,
9509                local_in,
9510                row_bytes: nvfp4_row_bytes(local_in),
9511            });
9512        }
9513        Ok(ResidentNvfp4TensorParallel {
9514            gate: gate_ranks,
9515            up: up_ranks,
9516            down: down_ranks,
9517            macros_gate: gate.macros.to_vec(),
9518            macros_up: up.macros.to_vec(),
9519            macros_down: down.macros.to_vec(),
9520            macros_gate_dev,
9521            macros_up_dev,
9522            macros_down_dev,
9523            expert_count: gate.expert_count,
9524            input_width: gate.in_features,
9525            expert_width: gate.out_features,
9526            device_workspace: std::sync::Mutex::new(None),
9527            t2_workspace: std::sync::Mutex::new(None),
9528            ep2,
9529        })
9530    }
9531
9532    /// EP2 host-canonical: the whole expert executes on its owning rank at full width
9533    /// (owner = expert & 1, bank slot = expert >> 1). Per-row program == the column-bank
9534    /// path's kernel, so gate/up are bit-equal to the TP layout.
9535    fn run_full_bank_expert_nvfp4(
9536        &self,
9537        ranks: &[ResidentNvfp4ColumnBankRank],
9538        macros: &[f32],
9539        expert: usize,
9540        input: &[f32],
9541    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9542        let owner = expert & 1;
9543        let slot = expert >> 1;
9544        let bank = ranks
9545            .get(owner)
9546            .ok_or("NVFP4 EP2 column bank missing owner rank")?;
9547        let engine = &self.ranks[owner];
9548        let _main = engine.gpu.enter_main()?;
9549        let activations = engine.htod(input)?;
9550        let output = if nvfp4_bank_v2_on() {
9551            engine.qmatvec_nvfp4_fast_v2(
9552                &bank.expert(slot),
9553                &activations,
9554                1,
9555                bank.in_features,
9556                bank.local_out,
9557                bank.row_bytes,
9558            )?
9559        } else {
9560            engine.qmatvec_nvfp4_fast(
9561                &bank.expert(slot),
9562                &activations,
9563                1,
9564                bank.in_features,
9565                bank.local_out,
9566                bank.row_bytes,
9567            )?
9568        };
9569        let mut out = engine.dtoh(&output)?;
9570        apply_macro(&mut out, macros[expert]);
9571        Ok(out)
9572    }
9573
9574    /// EP2 host-canonical down: one full-width dot on the owner (NUMERIC-CLASS vs the
9575    /// canonical 2-shard sum — the parenthesization this door declares).
9576    fn run_full_down_expert_nvfp4(
9577        &self,
9578        shards: &[ResidentNvfp4RowBankRank],
9579        macros: &[f32],
9580        expert: usize,
9581        input: &[f32],
9582    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9583        let owner = expert & 1;
9584        let slot = expert >> 1;
9585        let shard = shards
9586            .get(owner)
9587            .ok_or("NVFP4 EP2 down bank missing owner rank")?;
9588        let engine = &self.ranks[owner];
9589        let _main = engine.gpu.enter_main()?;
9590        let activations = engine.htod(input)?;
9591        let output = if nvfp4_bank_v2_on() {
9592            engine.qmatvec_nvfp4_fast_v2(
9593                &shard.expert(slot),
9594                &activations,
9595                1,
9596                shard.local_in,
9597                shard.out_features,
9598                shard.row_bytes,
9599            )?
9600        } else {
9601            engine.qmatvec_nvfp4_fast(
9602                &shard.expert(slot),
9603                &activations,
9604                1,
9605                shard.local_in,
9606                shard.out_features,
9607                shard.row_bytes,
9608            )?
9609        };
9610        let mut out = engine.dtoh(&output)?;
9611        apply_macro(&mut out, macros[expert]);
9612        Ok(out)
9613    }
9614
9615    fn run_column_bank_expert_nvfp4(
9616        &self,
9617        ranks: &[ResidentNvfp4ColumnBankRank],
9618        macros: &[f32],
9619        expert: usize,
9620        input: &[f32],
9621    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9622        let local_out = ranks
9623            .first()
9624            .ok_or("NVFP4 TP column bank has no ranks")?
9625            .local_out;
9626        let mut gathered = vec![0.0f32; local_out * ranks.len()];
9627        for (rank_index, (engine, bank)) in self.ranks.iter().zip(ranks).enumerate() {
9628            let _main = engine.gpu.enter_main()?;
9629            let activations = engine.htod(input)?;
9630            let output = if nvfp4_bank_v2_on() {
9631                engine.qmatvec_nvfp4_fast_v2(
9632                    &bank.expert(expert),
9633                    &activations,
9634                    1,
9635                    bank.in_features,
9636                    bank.local_out,
9637                    bank.row_bytes,
9638                )?
9639            } else {
9640                engine.qmatvec_nvfp4_fast(
9641                    &bank.expert(expert),
9642                    &activations,
9643                    1,
9644                    bank.in_features,
9645                    bank.local_out,
9646                    bank.row_bytes,
9647                )?
9648            };
9649            let output = engine.dtoh(&output)?;
9650            gathered[rank_index * local_out..(rank_index + 1) * local_out].copy_from_slice(&output);
9651        }
9652        apply_macro(&mut gathered, macros[expert]);
9653        Ok(gathered)
9654    }
9655
9656    /// Canonical-shard row reduction: iterate the FIXED shard grid in shard order (each shard
9657    /// executes on its owning rank engine), so the reduction parenthesization is identical at
9658    /// every world size — that identity is what the TP1-oracle-vs-TP2 bit gate proves.
9659    fn run_row_bank_expert_nvfp4(
9660        &self,
9661        shards: &[ResidentNvfp4RowBankRank],
9662        macros: &[f32],
9663        expert: usize,
9664        input: &[f32],
9665    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9666        let out_features = shards
9667            .first()
9668            .ok_or("NVFP4 TP row bank has no canonical shards")?
9669            .out_features;
9670        let in_features = shards.iter().map(|shard| shard.local_in).sum::<usize>();
9671        let mut reduced = vec![0.0f32; out_features];
9672        for (shard_index, shard) in shards.iter().enumerate() {
9673            let engine = self
9674                .ranks
9675                .get(shard.device_rank)
9676                .ok_or("NVFP4 canonical shard names a rank outside this runtime")?;
9677            let _main = engine.gpu.enter_main()?;
9678            let local_activations =
9679                activation_shard(input, 1, in_features, shards.len(), shard_index);
9680            let activations = engine.htod(&local_activations)?;
9681            let output = if nvfp4_bank_v2_on() {
9682                engine.qmatvec_nvfp4_fast_v2(
9683                    &shard.expert(expert),
9684                    &activations,
9685                    1,
9686                    shard.local_in,
9687                    shard.out_features,
9688                    shard.row_bytes,
9689                )?
9690            } else {
9691                engine.qmatvec_nvfp4_fast(
9692                    &shard.expert(expert),
9693                    &activations,
9694                    1,
9695                    shard.local_in,
9696                    shard.out_features,
9697                    shard.row_bytes,
9698                )?
9699            };
9700            let partial = engine.dtoh(&output)?;
9701            for (sum, value) in reduced.iter_mut().zip(&partial) {
9702                *sum += *value;
9703            }
9704        }
9705        apply_macro(&mut reduced, macros[expert]);
9706        Ok(reduced)
9707    }
9708
9709    /// Upload whole experts per owning rank (NVFP4 expert-parallel: the layout the clamped tail
9710    /// layers require — clamp semantics do not distribute across a tensor shard). Each owned
9711    /// expert keeps its full gate/up/down as one repacked block buffer on its owner.
9712    pub fn upload_expert_parallel_nvfp4(
9713        &self,
9714        gate: Nvfp4ExpertBank<'_>,
9715        up: Nvfp4ExpertBank<'_>,
9716        down: Nvfp4ExpertBank<'_>,
9717    ) -> Result<ResidentNvfp4ExpertParallel, Box<dyn std::error::Error>> {
9718        gate.validate()?;
9719        up.validate()?;
9720        down.validate()?;
9721        if gate.expert_count != up.expert_count || gate.expert_count != down.expert_count {
9722            return Err("NVFP4 EP gate/up/down expert counts differ".into());
9723        }
9724        if gate.in_features != up.in_features || gate.out_features != up.out_features {
9725            return Err("NVFP4 EP gate/up dimensions differ".into());
9726        }
9727        if down.in_features != gate.out_features || down.out_features != gate.in_features {
9728            return Err(format!(
9729                "NVFP4 EP down {}x{} does not invert gate/up {}x{}",
9730                down.out_features, down.in_features, gate.out_features, gate.in_features
9731            )
9732            .into());
9733        }
9734        let world = self.ranks.len();
9735        if gate.expert_count % world != 0 {
9736            return Err(format!(
9737                "NVFP4 EP expert count {} is not divisible by {world} ranks",
9738                gate.expert_count
9739            )
9740            .into());
9741        }
9742        let experts_per_rank = gate.expert_count / world;
9743        let mut ranks = Vec::with_capacity(world);
9744        for (rank_index, engine) in self.ranks.iter().enumerate() {
9745            let _main = engine.gpu.enter_main()?;
9746            let expert_range = rank_index * experts_per_rank..(rank_index + 1) * experts_per_rank;
9747            let mut gate_experts = Vec::with_capacity(experts_per_rank);
9748            let mut up_experts = Vec::with_capacity(experts_per_rank);
9749            let mut down_experts = Vec::with_capacity(experts_per_rank);
9750            for expert in expert_range.clone() {
9751                gate_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(gate.expert(expert)?))?);
9752                up_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(up.expert(expert)?))?);
9753                down_experts.push(engine.htod_bytes(&nvfp4_repack_matrix(down.expert(expert)?))?);
9754            }
9755            ranks.push(ResidentNvfp4EpRank {
9756                gate: gate_experts,
9757                up: up_experts,
9758                down: down_experts,
9759                expert_range,
9760            });
9761        }
9762        Ok(ResidentNvfp4ExpertParallel {
9763            ranks,
9764            macros_gate: gate.macros.to_vec(),
9765            macros_up: up.macros.to_vec(),
9766            macros_down: down.macros.to_vec(),
9767            expert_count: gate.expert_count,
9768            input_width: gate.in_features,
9769            expert_width: gate.out_features,
9770            gate_row_bytes: nvfp4_row_bytes(gate.in_features),
9771            down_row_bytes: nvfp4_row_bytes(down.in_features),
9772        })
9773    }
9774
9775    /// Routed NVFP4 expert-parallel program, host-canonical: every selected expert executes WHOLE
9776    /// on its owning rank (gate -> up -> clamped-or-plain SwiGLU on host -> down), each projection
9777    /// macro applied once post-kernel, route-weighted accumulate on the host in slot order. The
9778    /// activation uses `step_expert_activation_host`, so the clamped tail layers keep the official
9779    /// contract. Exactness-first; no throughput claim.
9780    #[allow(clippy::too_many_arguments)]
9781    pub fn run_routed_experts_nvfp4(
9782        &self,
9783        experts: &ResidentNvfp4ExpertParallel,
9784        input: &[f32],
9785        tokens: usize,
9786        selected: &[usize],
9787        route_weights: &[f32],
9788        experts_per_token: usize,
9789        activation_limit: Option<f32>,
9790    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9791        validate_activations(input, tokens, experts.input_width)?;
9792        let pairs = tokens
9793            .checked_mul(experts_per_token)
9794            .ok_or("NVFP4 EP route count overflow")?;
9795        if selected.len() != pairs || route_weights.len() != pairs {
9796            return Err(format!(
9797                "NVFP4 EP routes selected={} weights={} != tokens {tokens} x experts/token \
9798                 {experts_per_token} ({pairs})",
9799                selected.len(),
9800                route_weights.len(),
9801            )
9802            .into());
9803        }
9804        if !route_weights.iter().all(|weight| weight.is_finite()) {
9805            return Err("NVFP4 EP route weights contain a non-finite value".into());
9806        }
9807        let experts_per_rank = experts.expert_count / experts.ranks.len();
9808        let mut output = vec![0.0f32; tokens * experts.input_width];
9809        for token in 0..tokens {
9810            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
9811            for slot in 0..experts_per_token {
9812                let pair = token * experts_per_token + slot;
9813                let expert = selected[pair];
9814                if expert >= experts.expert_count {
9815                    return Err(format!(
9816                        "NVFP4 EP selected expert {expert} outside 0..{}",
9817                        experts.expert_count
9818                    )
9819                    .into());
9820                }
9821                let owner = expert / experts_per_rank;
9822                let local = expert - owner * experts_per_rank;
9823                let rank = &experts.ranks[owner];
9824                let engine = &self.ranks[owner];
9825                let _main = engine.gpu.enter_main()?;
9826                let device_input = engine.htod(input_row)?;
9827                let gate_out = engine.qmatvec_nvfp4_fast(
9828                    &rank.gate[local].slice(0..rank.gate[local].len()),
9829                    &device_input,
9830                    1,
9831                    experts.input_width,
9832                    experts.expert_width,
9833                    experts.gate_row_bytes,
9834                )?;
9835                let up_out = engine.qmatvec_nvfp4_fast(
9836                    &rank.up[local].slice(0..rank.up[local].len()),
9837                    &device_input,
9838                    1,
9839                    experts.input_width,
9840                    experts.expert_width,
9841                    experts.gate_row_bytes,
9842                )?;
9843                let mut gate_host = engine.dtoh(&gate_out)?;
9844                let mut up_host = engine.dtoh(&up_out)?;
9845                apply_macro(&mut gate_host, experts.macros_gate[expert]);
9846                apply_macro(&mut up_host, experts.macros_up[expert]);
9847                let activated: Vec<f32> = gate_host
9848                    .iter()
9849                    .zip(&up_host)
9850                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
9851                    .collect();
9852                let device_activated = engine.htod(&activated)?;
9853                let down_out = engine.qmatvec_nvfp4_fast(
9854                    &rank.down[local].slice(0..rank.down[local].len()),
9855                    &device_activated,
9856                    1,
9857                    experts.expert_width,
9858                    experts.input_width,
9859                    experts.down_row_bytes,
9860                )?;
9861                let mut down_host = engine.dtoh(&down_out)?;
9862                apply_macro(&mut down_host, experts.macros_down[expert]);
9863                let weight = route_weights[pair];
9864                for (sum, value) in output
9865                    [token * experts.input_width..(token + 1) * experts.input_width]
9866                    .iter_mut()
9867                    .zip(down_host)
9868                {
9869                    *sum += weight * value;
9870                }
9871            }
9872        }
9873        Ok(output)
9874    }
9875
9876    /// Device-resident routed NVFP4 expert program (decode shape, t=1 rows). The geometry gift
9877    /// this exploits: gate/up column halves land on the SAME rank that owns the matching down
9878    /// canonical shard (act[rank r] is exactly down-shard r's input-column window), so the whole
9879    /// expert interior — gate, up, macro-scaled SwiGLU, down partial, route-weighted accumulate —
9880    /// runs rank-local with ZERO cross-rank transfer. Per (token, layer): one input upload per
9881    /// rank, one fenced peer copy of the remote accumulator, one root add, one readback.
9882    ///
9883    /// Numeric class: device silu (silu_mul_scaled) with gate/up macros folded as gs/us and the
9884    /// down macro folded into the accumulate scalar (weight * macro_down — exact, both are
9885    /// per-expert constants). This matches the owning-stage MoE dev-path semantics, NOT the
9886    /// host-canonical program bit-for-bit; gate it with argmax + relative bounds against the
9887    /// host-canonical oracle, and with repeat determinism against itself.
9888    /// Clamped layers refuse (they stay on the EP program).
9889    pub fn run_tensor_parallel_routes_nvfp4_device(
9890        &self,
9891        experts: &ResidentNvfp4TensorParallel,
9892        input: &[f32],
9893        selected: &[usize],
9894        route_weights: &[f32],
9895        experts_per_token: usize,
9896        activation_limit: Option<f32>,
9897    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
9898        validate_activations(input, 1, experts.input_width)?;
9899        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
9900            return Err(format!(
9901                "NVFP4 device routes selected={} weights={} != experts/token {experts_per_token}",
9902                selected.len(),
9903                route_weights.len(),
9904            )
9905            .into());
9906        }
9907        if !route_weights.iter().all(|weight| weight.is_finite()) {
9908            return Err("NVFP4 device route weights contain a non-finite value".into());
9909        }
9910        let world = self.ranks.len();
9911        if world != NVFP4_CANONICAL_ROW_SHARDS {
9912            return Err(format!(
9913                "NVFP4 device routes require world == canonical shard grid \
9914                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
9915            )
9916            .into());
9917        }
9918        let local_out = if experts.ep2 {
9919            experts.expert_width
9920        } else {
9921            experts.expert_width / world
9922        };
9923
9924        // MEMRA_STEP_TP_TIMING=1: cumulative wall-clock of this program, printed every 430 calls
9925        // (~one 43-layer decode step's worth) so a bench run decomposes expert-program time vs
9926        // everything else without Nsight.
9927        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9928        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
9929        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
9930        let started = timing.then(std::time::Instant::now);
9931
9932        let n_sel = experts_per_token;
9933        let mut workspace_guard = experts
9934            .device_workspace
9935            .lock()
9936            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
9937        if workspace_guard.is_none() {
9938            let mut gate_out = Vec::with_capacity(world);
9939            let mut up_out = Vec::with_capacity(world);
9940            let mut act_q = Vec::with_capacity(world);
9941            let mut act_d = Vec::with_capacity(world);
9942            let mut sel = Vec::with_capacity(world);
9943            let mut partial = Vec::with_capacity(world);
9944            let mut accumulator = Vec::with_capacity(world);
9945            let mut combine_w = Vec::with_capacity(world);
9946            let mut route_w = Vec::with_capacity(world);
9947            let mut in_q = Vec::with_capacity(world);
9948            let mut in_d = Vec::with_capacity(world);
9949            let mut input = Vec::with_capacity(world);
9950            let mut ev_rank = Vec::with_capacity(world);
9951            let moe_direct = moe_direct_on();
9952            for (rank, engine) in self.ranks.iter().enumerate() {
9953                let _main = engine.gpu.enter_main()?;
9954                gate_out.push(engine.uninit(n_sel * local_out)?);
9955                up_out.push(engine.uninit(n_sel * local_out)?);
9956                act_q.push(engine.uninit_i8(n_sel * local_out)?);
9957                act_d.push(engine.uninit(n_sel * local_out / 32)?);
9958                sel.push(engine.htod_i32(&vec![0i32; n_sel])?);
9959                partial.push(engine.uninit(n_sel * experts.input_width)?);
9960                // Direct join: peer accumulators live on ROOT (single P2P store pass).
9961                if moe_direct && rank != 0 {
9962                    let root = &self.ranks[0];
9963                    let _root_main = root.gpu.enter_main()?;
9964                    accumulator.push(root.zeros(experts.input_width)?);
9965                } else {
9966                    accumulator.push(engine.zeros(experts.input_width)?);
9967                }
9968                combine_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9969                route_w.push(engine.htod(&vec![0.0f32; n_sel])?);
9970                in_q.push(engine.uninit_i8(experts.input_width)?);
9971                in_d.push(engine.uninit(experts.input_width / 32)?);
9972                input.push(engine.uninit(experts.input_width)?);
9973                ev_rank.push(engine.ctx().new_event(None)?);
9974            }
9975            let root = &self.ranks[0];
9976            let _main = root.gpu.enter_main()?;
9977            *workspace_guard = Some(Nvfp4DeviceRoutesWorkspace {
9978                prestaged: false,
9979                rank1_routed: false,
9980                ev_input: None,
9981                fence_flags_raw: 0,
9982                fence_ticket: 0,
9983                gate_out,
9984                up_out,
9985                act_q,
9986                act_d,
9987                sel,
9988                partial,
9989                accumulator,
9990                combine_w,
9991                route_w,
9992                in_q,
9993                in_d,
9994                dev_route_e: None,
9995                in_stage_e: None,
9996                out_stage_e: None,
9997                routes_graph: None,
9998                raw_dev_route_e: None,
9999                raw_combine: None,
10000                raw_input: Vec::new(),
10001                raw_sel: Vec::new(),
10002                raw_route_w: Vec::new(),
10003                remote: root.uninit(experts.input_width)?,
10004                combined: root.uninit(experts.input_width)?,
10005                n_sel,
10006                input,
10007                ev_rank,
10008                ev_done: Some(root.ctx().new_event(None)?),
10009                ev_entry: None,
10010            });
10011        }
10012        let workspace = workspace_guard
10013            .as_mut()
10014            .expect("NVFP4 device routes workspace initialized above");
10015        // EP2 uses this call only as the workspace-arming warmup (the prejoin path drives
10016        // decode); its host-routed sweep semantics do not apply to whole-expert banks.
10017        if experts.ep2 {
10018            return Ok(vec![0.0f32; experts.input_width]);
10019        }
10020        if workspace.n_sel != n_sel {
10021            return Err(format!(
10022                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10023                workspace.n_sel
10024            )
10025            .into());
10026        }
10027        for &expert in selected {
10028            if expert >= experts.expert_count {
10029                return Err(format!(
10030                    "NVFP4 device selected expert {expert} outside 0..{}",
10031                    experts.expert_count
10032                )
10033                .into());
10034            }
10035        }
10036        let sel_i32 = selected
10037            .iter()
10038            .map(|&expert| expert as i32)
10039            .collect::<Vec<_>>();
10040
10041        // BATCHED program (2026-08-20): per rank, ONE launch per sweep (gate, up, SwiGLU,
10042        // down) covers every selected expert via the selection array and the contiguous bank —
10043        // the per-expert launch loop was pure host latency (~100 sequential launches/layer,
10044        // 291us wall for ~35us of arithmetic). Per (expert, row) the kernels are bit-identical
10045        // to the per-expert forms, and the route-weight axpy chain keeps its exact sequential
10046        // accumulation order — the program's values are unchanged.
10047        for (rank_index, engine) in self.ranks.iter().enumerate() {
10048            let _main = engine.gpu.enter_main()?;
10049            let device_input = engine.htod(input)?;
10050            let Nvfp4DeviceRoutesWorkspace { in_q, in_d, .. } = &mut *workspace;
10051            engine.quantize_q8_1_into(
10052                &device_input,
10053                1,
10054                experts.input_width,
10055                &mut in_q[rank_index],
10056                &mut in_d[rank_index],
10057            )?;
10058            // device_input frees on this rank's stream after the quantize — same-stream order.
10059        }
10060        self.nvfp4_routes_batched_sweeps(
10061            experts,
10062            workspace,
10063            selected,
10064            route_weights,
10065            &sel_i32,
10066            local_out,
10067            n_sel,
10068            activation_limit,
10069            false,
10070        )?;
10071
10072        // Combine: fence the remote shard's producer stream, peer-copy its accumulator to root,
10073        // reduce in canonical shard order, read back once.
10074        let root = &self.ranks[0];
10075        for engine in &self.ranks[1..] {
10076            let _main = engine.gpu.enter_main()?;
10077            engine.stream().synchronize()?;
10078        }
10079        let _main = root.gpu.enter_main()?;
10080        root.stream()
10081            .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10082        root.add(
10083            &workspace.accumulator[0],
10084            &workspace.remote,
10085            &mut workspace.combined,
10086            experts.input_width,
10087        )?;
10088        let output = root.dtoh(&workspace.combined)?;
10089        if let Some(started) = started {
10090            use std::sync::atomic::Ordering;
10091            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10092                + started.elapsed().as_nanos() as u64;
10093            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10094            if calls % 430 == 0 {
10095                eprintln!(
10096                    "[nvfp4-dev-routes-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10097                    ns as f64 / 1.0e6,
10098                    ns as f64 / calls as f64 / 1.0e3,
10099                );
10100            }
10101        }
10102        Ok(output)
10103    }
10104
10105    /// The shared batched sweeps of the device routes program: per rank, upload the selection,
10106    /// reset the accumulator, run the gate/up/SwiGLU/down batched launches, then the
10107    /// route-weight axpy chain in exact sequential per-pair order. Every op queues on the
10108    /// owning rank's stream; callers own input acquisition and the combine.
10109    #[allow(clippy::too_many_arguments)]
10110    fn nvfp4_routes_batched_sweeps(
10111        &self,
10112        experts: &ResidentNvfp4TensorParallel,
10113        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10114        selected: &[usize],
10115        route_weights: &[f32],
10116        sel_i32: &[i32],
10117        local_out: usize,
10118        n_sel: usize,
10119        activation_limit: Option<f32>,
10120        device_routed: bool,
10121    ) -> Result<(), Box<dyn std::error::Error>> {
10122        for rank_index in 0..self.ranks.len() {
10123            self.nvfp4_routes_batched_sweeps_rank(
10124                experts,
10125                workspace,
10126                selected,
10127                route_weights,
10128                sel_i32,
10129                local_out,
10130                n_sel,
10131                activation_limit,
10132                device_routed,
10133                rank_index,
10134            )?;
10135        }
10136        Ok(())
10137    }
10138
10139    /// One rank's sweeps (the per-rank body of `nvfp4_routes_batched_sweeps`) — separated so
10140    /// the graph door can capture each rank's segment on its own stream.
10141    #[allow(clippy::too_many_arguments)]
10142    fn nvfp4_routes_batched_sweeps_rank(
10143        &self,
10144        experts: &ResidentNvfp4TensorParallel,
10145        workspace: &mut Nvfp4DeviceRoutesWorkspace,
10146        selected: &[usize],
10147        route_weights: &[f32],
10148        sel_i32: &[i32],
10149        local_out: usize,
10150        n_sel: usize,
10151        activation_limit: Option<f32>,
10152        device_routed: bool,
10153        rank_index: usize,
10154    ) -> Result<(), Box<dyn std::error::Error>> {
10155        {
10156            let engine = &self.ranks[rank_index];
10157            let _main = engine.gpu.enter_main()?;
10158            // EP2: whole-expert full-width sweep, owner-guarded; down+combine fused writes
10159            // this rank's slot-ordered partial straight into its accumulator (the join is
10160            // unchanged). Device-routed only — the host-routed arm and the graph door refuse
10161            // at the caller.
10162            if experts.ep2 {
10163                if !device_routed {
10164                    return Err("NVFP4 EP2 banks support the device-routed decode arm only".into());
10165                }
10166                let gate_bank = &experts.gate[rank_index];
10167                let up_bank = &experts.up[rank_index];
10168                if gate_bank.local_out != experts.expert_width
10169                    || gate_bank.expert_bytes != up_bank.expert_bytes
10170                {
10171                    return Err("NVFP4 EP2 bank geometry drifted".into());
10172                }
10173                {
10174                    let Nvfp4DeviceRoutesWorkspace {
10175                        sel,
10176                        gate_out,
10177                        up_out,
10178                        in_q,
10179                        in_d,
10180                        ..
10181                    } = &mut *workspace;
10182                    engine.qmatvec_nvfp4_sel_gu_ep_into(
10183                        &gate_bank.bank,
10184                        &up_bank.bank,
10185                        &sel[rank_index],
10186                        &in_q[rank_index],
10187                        &in_d[rank_index],
10188                        &mut gate_out[rank_index],
10189                        &mut up_out[rank_index],
10190                        n_sel,
10191                        gate_bank.in_features,
10192                        gate_bank.local_out,
10193                        gate_bank.row_bytes,
10194                        gate_bank.expert_bytes,
10195                        rank_index,
10196                    )?;
10197                }
10198                {
10199                    let Nvfp4DeviceRoutesWorkspace {
10200                        gate_out,
10201                        up_out,
10202                        sel,
10203                        act_q,
10204                        act_d,
10205                        ..
10206                    } = &mut *workspace;
10207                    engine.silu_mul_scaled_q8_1_sel_ep_into(
10208                        &gate_out[rank_index],
10209                        &up_out[rank_index],
10210                        &experts.macros_gate_dev[rank_index],
10211                        &experts.macros_up_dev[rank_index],
10212                        &sel[rank_index],
10213                        activation_limit,
10214                        &mut act_q[rank_index],
10215                        &mut act_d[rank_index],
10216                        local_out,
10217                        n_sel,
10218                        rank_index,
10219                    )?;
10220                }
10221                let shard = &experts.down[rank_index];
10222                if shard.device_rank != rank_index || shard.local_in != local_out {
10223                    return Err("NVFP4 EP2 down bank placement drifted".into());
10224                }
10225                {
10226                    let Nvfp4DeviceRoutesWorkspace {
10227                        sel,
10228                        act_q,
10229                        act_d,
10230                        route_w,
10231                        accumulator,
10232                        ..
10233                    } = &mut *workspace;
10234                    engine.qmatvec_nvfp4_sel_down8_ep_into(
10235                        &shard.bank,
10236                        &sel[rank_index],
10237                        &act_q[rank_index],
10238                        &act_d[rank_index],
10239                        &route_w[rank_index],
10240                        &experts.macros_down_dev[rank_index],
10241                        &mut accumulator[rank_index],
10242                        n_sel,
10243                        shard.local_in,
10244                        shard.out_features,
10245                        shard.row_bytes,
10246                        shard.expert_bytes,
10247                        local_out,
10248                        local_out / 32,
10249                        rank_index,
10250                    )?;
10251                }
10252                return Ok(());
10253            }
10254            if !device_routed {
10255                engine.htod_i32_into(&mut workspace.sel[rank_index], sel_i32)?;
10256                // Folded combine weights (route_weight x down macro) — one 40-byte upload
10257                // replaces the accumulator reset + n_sel sequential axpy launches below.
10258                let folded = (0..n_sel)
10259                    .map(|pair| route_weights[pair] * experts.macros_down[selected[pair]])
10260                    .collect::<Vec<_>>();
10261                let mut view = workspace.combine_w[rank_index].slice_mut(0..n_sel);
10262                engine.stream().memcpy_htod(&folded, &mut view)?;
10263            }
10264            let gate_bank = &experts.gate[rank_index];
10265            let up_bank = &experts.up[rank_index];
10266            let (aq, ad) = (&workspace.in_q[rank_index], &workspace.in_d[rank_index]);
10267            // FUSION #2a (v2 banks): the two sweeps share sel/aq/ad and identical geometry
10268            // — one launch, per-row bit-identical, double the grid fill.
10269            let gu_fused = nvfp4_bank_v2_on()
10270                && gate_bank.in_features == up_bank.in_features
10271                && gate_bank.local_out == up_bank.local_out
10272                && gate_bank.row_bytes == up_bank.row_bytes
10273                && gate_bank.expert_bytes == up_bank.expert_bytes;
10274            if gu_fused {
10275                let Nvfp4DeviceRoutesWorkspace {
10276                    sel,
10277                    gate_out,
10278                    up_out,
10279                    in_q,
10280                    in_d,
10281                    ..
10282                } = &mut *workspace;
10283                engine.qmatvec_nvfp4_sel_gu_into(
10284                    &gate_bank.bank,
10285                    &up_bank.bank,
10286                    &sel[rank_index],
10287                    &in_q[rank_index],
10288                    &in_d[rank_index],
10289                    &mut gate_out[rank_index],
10290                    &mut up_out[rank_index],
10291                    n_sel,
10292                    gate_bank.in_features,
10293                    gate_bank.local_out,
10294                    gate_bank.row_bytes,
10295                    gate_bank.expert_bytes,
10296                )?;
10297            } else {
10298                engine.qmatvec_nvfp4_sel_into(
10299                    &gate_bank.bank,
10300                    &workspace.sel[rank_index],
10301                    aq,
10302                    ad,
10303                    &mut workspace.gate_out[rank_index],
10304                    n_sel,
10305                    gate_bank.in_features,
10306                    gate_bank.local_out,
10307                    gate_bank.row_bytes,
10308                    gate_bank.expert_bytes,
10309                    0,
10310                    0,
10311                )?;
10312                engine.qmatvec_nvfp4_sel_into(
10313                    &up_bank.bank,
10314                    &workspace.sel[rank_index],
10315                    aq,
10316                    ad,
10317                    &mut workspace.up_out[rank_index],
10318                    n_sel,
10319                    up_bank.in_features,
10320                    up_bank.local_out,
10321                    up_bank.row_bytes,
10322                    up_bank.expert_bytes,
10323                    0,
10324                    0,
10325                )?;
10326            }
10327            // Fused macro-scaled SwiGLU that EMITS q8_1 directly — down consumes it with no
10328            // separate quantize launch. act[rank] IS down canonical shard `rank_index`'s
10329            // input-column window (the geometry gift; see the method doc).
10330            {
10331                let Nvfp4DeviceRoutesWorkspace {
10332                    gate_out,
10333                    up_out,
10334                    sel,
10335                    act_q,
10336                    act_d,
10337                    ..
10338                } = &mut *workspace;
10339                engine.silu_mul_scaled_q8_1_sel_into(
10340                    &gate_out[rank_index],
10341                    &up_out[rank_index],
10342                    &experts.macros_gate_dev[rank_index],
10343                    &experts.macros_up_dev[rank_index],
10344                    &sel[rank_index],
10345                    activation_limit,
10346                    &mut act_q[rank_index],
10347                    &mut act_d[rank_index],
10348                    local_out,
10349                    n_sel,
10350                )?;
10351            }
10352            let shard = &experts.down[rank_index];
10353            if shard.device_rank != rank_index || shard.local_in != local_out {
10354                return Err(
10355                    "NVFP4 device routes: down canonical shard placement drifted from \
10356                     the gate/up column split"
10357                        .into(),
10358                );
10359            }
10360            // MEMRA_SEL_DOWN8=1: down sweep + route-weight combine in ONE launch, one warp
10361            // per SLOT instead of one warp per (row, slot) — the q8 `down8 w8` occupancy arm
10362            // (cx-downkernel: waves/SM 0.91 -> 4.36) ported to the NVFP4 banks. Bit-identical
10363            // (same dot program, same reduce tree, same slot-ordered chain), and the
10364            // n_sel x out_f partial buffer round trip disappears. Device-routed only: the
10365            // host-routed arm folds the macro into combine_w instead of reading md on device.
10366            let down8 = device_routed && sel_down8_on() && (shard.local_in >> 5) <= 32;
10367            {
10368                // MEMRA_SWEEP_TRACE=1: one receipt PER DISTINCT decision combo — a
10369                // silently-dead fusion reads as roofline physics without it (and the
10370                // prime's host-routed call must not swallow the decode receipt).
10371                static SEEN: std::sync::Mutex<Vec<(bool, bool)>> =
10372                    std::sync::Mutex::new(Vec::new());
10373                if std::env::var("MEMRA_SWEEP_TRACE").as_deref() == Ok("1") {
10374                    let mut seen = SEEN.lock().unwrap();
10375                    if !seen.contains(&(down8, device_routed)) {
10376                        seen.push((down8, device_routed));
10377                        eprintln!(
10378                            "[sweep-trace] down8={down8} device_routed={device_routed} \
10379                             sel_down8_on={} local_in={} n_sel={n_sel}",
10380                            sel_down8_on(),
10381                            shard.local_in
10382                        );
10383                    }
10384                }
10385            }
10386            if down8 {
10387                let Nvfp4DeviceRoutesWorkspace {
10388                    sel,
10389                    act_q,
10390                    act_d,
10391                    route_w,
10392                    accumulator,
10393                    ..
10394                } = &mut *workspace;
10395                engine.qmatvec_nvfp4_sel_down8_into(
10396                    &shard.bank,
10397                    &sel[rank_index],
10398                    &act_q[rank_index],
10399                    &act_d[rank_index],
10400                    &route_w[rank_index],
10401                    &experts.macros_down_dev[rank_index],
10402                    &mut accumulator[rank_index],
10403                    n_sel,
10404                    shard.local_in,
10405                    shard.out_features,
10406                    shard.row_bytes,
10407                    shard.expert_bytes,
10408                    local_out,
10409                    local_out / 32,
10410                )?;
10411            } else {
10412                let Nvfp4DeviceRoutesWorkspace {
10413                    sel,
10414                    act_q,
10415                    act_d,
10416                    partial,
10417                    ..
10418                } = &mut *workspace;
10419                engine.qmatvec_nvfp4_sel_into(
10420                    &shard.bank,
10421                    &sel[rank_index],
10422                    &act_q[rank_index],
10423                    &act_d[rank_index],
10424                    &mut partial[rank_index],
10425                    n_sel,
10426                    shard.local_in,
10427                    shard.out_features,
10428                    shard.row_bytes,
10429                    shard.expert_bytes,
10430                    local_out,
10431                    local_out / 32,
10432                )?;
10433            }
10434            // Route-weight accumulation: axpy_rows_seq keeps the exact sequential per-pair
10435            // FP chain of the reset + n_sel axpy launches in ONE launch. Device-routed calls
10436            // fold the down macro in-kernel from the device selection. (down8 already
10437            // produced the accumulator inside the sweep.)
10438            if !down8 {
10439                let Nvfp4DeviceRoutesWorkspace {
10440                    partial,
10441                    combine_w,
10442                    route_w,
10443                    sel,
10444                    accumulator,
10445                    ..
10446                } = &mut *workspace;
10447                if device_routed {
10448                    engine.axpy_rows_seq_md_into(
10449                        &partial[rank_index],
10450                        &route_w[rank_index],
10451                        &experts.macros_down_dev[rank_index],
10452                        &sel[rank_index],
10453                        &mut accumulator[rank_index],
10454                        experts.input_width,
10455                        n_sel,
10456                    )?;
10457                } else {
10458                    engine.axpy_rows_seq_into(
10459                        &partial[rank_index],
10460                        &combine_w[rank_index],
10461                        &mut accumulator[rank_index],
10462                        experts.input_width,
10463                        n_sel,
10464                    )?;
10465                }
10466            }
10467        }
10468        Ok(())
10469    }
10470
10471    /// Device-IO twin of `run_tensor_parallel_routes_nvfp4_device`: the layer input arrives as
10472    /// a device row on the model engine `e` and the combined output returns as a fresh
10473    /// `e`-context row — no host round-trip, no host stream sync. Ordering is evented (the v2
10474    /// attention discipline): `ev_entry` is recorded on `e`'s stream AFTER the caller queued
10475    /// the input's producer; each rank waits it before its peer read; the root reduce waits
10476    /// every rank's done event; `e` waits the root's done event before copying out. The
10477    /// program bytes are identical to the host-IO twin — dtoh/htod and dtod preserve f32 bits.
10478    pub fn run_tensor_parallel_routes_nvfp4_device_io(
10479        &self,
10480        experts: &ResidentNvfp4TensorParallel,
10481        e: &Engine,
10482        input_dev: &crate::CudaSlice<f32>,
10483        selected: &[usize],
10484        route_weights: &[f32],
10485        experts_per_token: usize,
10486        activation_limit: Option<f32>,
10487    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10488        if input_dev.len() != experts.input_width {
10489            return Err(format!(
10490                "NVFP4 device-io routes input {} != width {}",
10491                input_dev.len(),
10492                experts.input_width
10493            )
10494            .into());
10495        }
10496        if selected.len() != experts_per_token || route_weights.len() != experts_per_token {
10497            return Err(format!(
10498                "NVFP4 device-io routes selected={} weights={} != experts/token {experts_per_token}",
10499                selected.len(),
10500                route_weights.len(),
10501            )
10502            .into());
10503        }
10504        if !route_weights.iter().all(|weight| weight.is_finite()) {
10505            return Err("NVFP4 device route weights contain a non-finite value".into());
10506        }
10507        let world = self.ranks.len();
10508        if world != NVFP4_CANONICAL_ROW_SHARDS {
10509            return Err(format!(
10510                "NVFP4 device routes require world == canonical shard grid \
10511                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
10512            )
10513            .into());
10514        }
10515        let local_out = experts.expert_width / world;
10516        let n_sel = experts_per_token;
10517
10518        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10519        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
10520        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
10521        let started = timing.then(std::time::Instant::now);
10522
10523        let mut workspace_guard = experts
10524            .device_workspace
10525            .lock()
10526            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10527        if workspace_guard.is_none() {
10528            drop(workspace_guard);
10529            // Build through the host-IO ensure path exactly once: run it with a zero input.
10530            // Cheaper than duplicating the init; the first real call overwrites everything.
10531            let zero = vec![0.0f32; experts.input_width];
10532            let zero_sel = vec![0usize; n_sel];
10533            let zero_w = vec![0.0f32; n_sel];
10534            let _ = self.run_tensor_parallel_routes_nvfp4_device(
10535                experts,
10536                &zero,
10537                &zero_sel,
10538                &zero_w,
10539                n_sel,
10540                activation_limit,
10541            )?;
10542            workspace_guard = experts
10543                .device_workspace
10544                .lock()
10545                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10546        }
10547        let workspace = workspace_guard
10548            .as_mut()
10549            .expect("NVFP4 device routes workspace initialized above");
10550        if workspace.n_sel != n_sel {
10551            return Err(format!(
10552                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
10553                workspace.n_sel
10554            )
10555            .into());
10556        }
10557        for &expert in selected {
10558            if expert >= experts.expert_count {
10559                return Err(format!(
10560                    "NVFP4 device selected expert {expert} outside 0..{}",
10561                    experts.expert_count
10562                )
10563                .into());
10564            }
10565        }
10566        let sel_i32 = selected
10567            .iter()
10568            .map(|&expert| expert as i32)
10569            .collect::<Vec<_>>();
10570
10571        // Entry fence: e's stream position covers the input's producer AND every consumer of
10572        // the previous layer's output (queued on e's stream before this call), guarding the
10573        // workspace reuse exactly like the v2 attention driver.
10574        if let Some((_, device)) = workspace.ev_entry.as_ref() {
10575            if *device != e.ctx().ordinal() {
10576                return Err("NVFP4 device-io routes engine changed".into());
10577            }
10578        } else {
10579            let _main = e.gpu.enter_main()?;
10580            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10581        }
10582        {
10583            let _main = e.gpu.enter_main()?;
10584            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10585            ev_entry.record(&e.stream())?;
10586        }
10587        for (rank_index, engine) in self.ranks.iter().enumerate() {
10588            let _main = engine.gpu.enter_main()?;
10589            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
10590            engine.stream().wait(ev_entry)?;
10591            {
10592                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10593                engine
10594                    .stream()
10595                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10596            }
10597            {
10598                let Nvfp4DeviceRoutesWorkspace {
10599                    input, in_q, in_d, ..
10600                } = &mut *workspace;
10601                engine.quantize_q8_1_into(
10602                    &input[rank_index],
10603                    1,
10604                    experts.input_width,
10605                    &mut in_q[rank_index],
10606                    &mut in_d[rank_index],
10607                )?;
10608            }
10609        }
10610        self.nvfp4_routes_batched_sweeps(
10611            experts,
10612            workspace,
10613            selected,
10614            route_weights,
10615            &sel_i32,
10616            local_out,
10617            n_sel,
10618            activation_limit,
10619            false,
10620        )?;
10621
10622        // Evented combine: rank done events replace the host stream syncs, the reduce runs on
10623        // the root stream in canonical shard order, and e copies the combined row out behind
10624        // the root's done event.
10625        // rank0 == root: its own stream order already covers its sweep; only the PEER
10626        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
10627        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
10628            let _main = engine.gpu.enter_main()?;
10629            workspace.ev_rank[rank_index].record(&engine.stream())?;
10630        }
10631        if moe_direct_on() && self.ranks.len() == 2 {
10632            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
10633            // rank0's is root-stream-ordered. One root event + rank1's own event order
10634            // the model engine's single add — same operand order as root's add
10635            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
10636            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
10637            // hazard class does not apply).
10638            {
10639                let root = &self.ranks[0];
10640                let _main = root.gpu.enter_main()?;
10641                workspace
10642                    .ev_done
10643                    .as_ref()
10644                    .expect("device routes done event")
10645                    .record(&root.stream())?;
10646            }
10647            let _main = e.gpu.enter_main()?;
10648            e.stream().wait(
10649                workspace
10650                    .ev_done
10651                    .as_ref()
10652                    .expect("device routes done event"),
10653            )?;
10654            for ev in workspace.ev_rank.iter().skip(1) {
10655                e.stream().wait(ev)?;
10656            }
10657            let mut output = e.uninit(experts.input_width)?;
10658            e.add(
10659                &workspace.accumulator[0],
10660                &workspace.accumulator[1],
10661                &mut output,
10662                experts.input_width,
10663            )?;
10664            let output = output;
10665            if let Some(started) = started {
10666                use std::sync::atomic::Ordering;
10667                let ns = TIMING_NS
10668                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10669                    + started.elapsed().as_nanos() as u64;
10670                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10671                if calls % 430 == 0 {
10672                    eprintln!(
10673                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10674                        ns as f64 / 1.0e6,
10675                        ns as f64 / calls as f64 / 1.0e3,
10676                    );
10677                }
10678            }
10679            return Ok(output);
10680        }
10681        {
10682            let root = &self.ranks[0];
10683            let _main = root.gpu.enter_main()?;
10684            for ev in workspace.ev_rank.iter().skip(1) {
10685                root.stream().wait(ev)?;
10686            }
10687            root.stream()
10688                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
10689            {
10690                let Nvfp4DeviceRoutesWorkspace {
10691                    accumulator,
10692                    remote,
10693                    combined,
10694                    ..
10695                } = &mut *workspace;
10696                root.add(&accumulator[0], remote, combined, experts.input_width)?;
10697            }
10698            workspace
10699                .ev_done
10700                .as_ref()
10701                .expect("device routes done event")
10702                .record(&root.stream())?;
10703        }
10704        let output = {
10705            let _main = e.gpu.enter_main()?;
10706            e.stream().wait(
10707                workspace
10708                    .ev_done
10709                    .as_ref()
10710                    .expect("device routes done event"),
10711            )?;
10712            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
10713            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
10714            let mut output = e.uninit(experts.input_width)?;
10715            e.stream().memcpy_dtod(
10716                &workspace.combined.slice(0..experts.input_width),
10717                &mut output.slice_mut(0..experts.input_width),
10718            )?;
10719            output
10720        };
10721        if let Some(started) = started {
10722            use std::sync::atomic::Ordering;
10723            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
10724                + started.elapsed().as_nanos() as u64;
10725            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
10726            if calls % 430 == 0 {
10727                eprintln!(
10728                    "[nvfp4-dev-routes-io-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
10729                    ns as f64 / 1.0e6,
10730                    ns as f64 / calls as f64 / 1.0e3,
10731                );
10732            }
10733        }
10734        Ok(output)
10735    }
10736
10737    /// Device-routed twin of `run_tensor_parallel_routes_nvfp4_device_io`: the selection and
10738    /// route weights arrive as the device router's e-context outputs — the per-layer host
10739    /// logits readback disappears. The fresh router outputs are staged into persistent
10740    /// e-context buffers on e's stream (never-free discipline) before the entry event; each
10741    /// rank peer-reads them behind it. The down-macro fold happens in-kernel.
10742    #[allow(clippy::too_many_arguments)]
10743    /// Prestage the routed-expert input: pull the shared row to every rank and quantize it
10744    /// there, WITHOUT the selection — callable before the router so the rank chains overlap
10745    /// it. No-op (returns false) when the workspace is not built yet or the door is off;
10746    /// the routed run then does its own staging as before.
10747    pub fn nvfp4_routes_prestage(
10748        &self,
10749        experts: &ResidentNvfp4TensorParallel,
10750        e: &Engine,
10751        input_dev: &crate::CudaSlice<f32>,
10752    ) -> Result<bool, Box<dyn std::error::Error>> {
10753        self.nvfp4_routes_prestage_with(experts, e, input_dev, |_, _, _, _| Ok(false))
10754    }
10755
10756    /// `nvfp4_routes_prestage` with a PEER-ROUTER hook: after rank1's input pull +
10757    /// quantize, the hook may compute rank1's route selection LOCALLY (replicated router —
10758    /// deterministic kernels on identical input bits produce identical sel/w, so the
10759    /// selection is bit-equal to the root's). Returns true when it wrote sel/route_w; the
10760    /// routed run then skips rank1's sel pull.
10761    pub fn nvfp4_routes_prestage_with(
10762        &self,
10763        experts: &ResidentNvfp4TensorParallel,
10764        e: &Engine,
10765        input_dev: &crate::CudaSlice<f32>,
10766        rank1_router: impl FnOnce(
10767            &Engine,
10768            &crate::CudaSlice<f32>,
10769            &mut crate::CudaSlice<i32>,
10770            &mut crate::CudaSlice<f32>,
10771        ) -> Result<bool, Box<dyn std::error::Error>>,
10772    ) -> Result<bool, Box<dyn std::error::Error>> {
10773        if !routes_prestage_on() || step_tp_graph_enabled()? {
10774            return Ok(false);
10775        }
10776        if input_dev.len() != experts.input_width {
10777            return Err("NVFP4 prestage input width mismatch".into());
10778        }
10779        let mut workspace_guard = experts
10780            .device_workspace
10781            .lock()
10782            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
10783        let Some(workspace) = workspace_guard.as_mut() else {
10784            return Ok(false);
10785        };
10786        if workspace.ev_input.is_none() {
10787            let _main = e.gpu.enter_main()?;
10788            workspace.ev_input = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
10789        } else if workspace.ev_input.as_ref().map(|(_, d)| *d) != Some(e.ctx().ordinal()) {
10790            return Err("NVFP4 prestage engine changed".into());
10791        }
10792        {
10793            let _main = e.gpu.enter_main()?;
10794            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
10795            ev.record(&e.stream())?;
10796        }
10797        for (rank_index, engine) in self.ranks.iter().enumerate() {
10798            let _main = engine.gpu.enter_main()?;
10799            let (ev, _) = workspace.ev_input.as_ref().expect("armed above");
10800            engine.stream().wait(ev)?;
10801            {
10802                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
10803                engine
10804                    .stream()
10805                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
10806            }
10807            {
10808                let Nvfp4DeviceRoutesWorkspace {
10809                    input, in_q, in_d, ..
10810                } = &mut *workspace;
10811                engine.quantize_q8_1_into(
10812                    &input[rank_index],
10813                    1,
10814                    experts.input_width,
10815                    &mut in_q[rank_index],
10816                    &mut in_d[rank_index],
10817                )?;
10818            }
10819        }
10820        if self.ranks.len() == 2 {
10821            let rank1 = &self.ranks[1];
10822            let _r1 = rank1.gpu.enter_main()?;
10823            let Nvfp4DeviceRoutesWorkspace {
10824                input,
10825                sel,
10826                route_w,
10827                ..
10828            } = &mut *workspace;
10829            let (in1, rest_sel) = (&input[1], &mut sel[1]);
10830            if rank1_router(rank1, in1, rest_sel, &mut route_w[1])? {
10831                workspace.rank1_routed = true;
10832            }
10833        }
10834        workspace.prestaged = true;
10835        Ok(true)
10836    }
10837
10838    /// TWO-COLUMN device-routed expert program (spec verify, MEMRA_TCOL_FFN): one gu_tcol
10839    /// sweep over 2*n_sel_col pairs (pair t reads activation row t/n_sel_col — weights the
10840    /// two columns share dedup through L2), the UNCHANGED silu/down kernels at n_sel=16
10841    /// (both already index per pair), and one offset-axpy combine per column (the exact
10842    /// t=1 sequential chain over that column's 8 pairs). No serving doors: no graph, no
10843    /// prestage, no shexp folding — plain evented ordering. Returns [2, input_width] on e.
10844    ///
10845    /// EXACTNESS: every kernel body is the t=1 program per (pair,row) or per element; the
10846    /// per-column combine order equals the t=1 combine; the cross-rank join adds the same
10847    /// operand values elementwise. Gated by the greedy tape like every verify arm.
10848    #[allow(clippy::too_many_arguments)]
10849    pub fn run_tensor_parallel_routes_nvfp4_device_routed_tn(
10850        &self,
10851        experts: &ResidentNvfp4TensorParallel,
10852        e: &Engine,
10853        z_t: &crate::CudaSlice<f32>,
10854        sel_d: &crate::CudaSlice<i32>,
10855        w_d: &crate::CudaSlice<f32>,
10856        t: usize,
10857        n_sel_col: usize,
10858        activation_limit: Option<f32>,
10859    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
10860        let world = self.ranks.len();
10861        if world != NVFP4_CANONICAL_ROW_SHARDS {
10862            return Err("NVFP4 t-row routes require the canonical 2-shard grid".into());
10863        }
10864        let width = experts.input_width;
10865        let n_sel = t * n_sel_col;
10866        if t == 0 || t > 32 || z_t.len() < t * width || sel_d.len() < n_sel || w_d.len() < n_sel {
10867            return Err("NVFP4 t-row routes geometry".into());
10868        }
10869        if !nvfp4_bank_v2_on() {
10870            return Err("NVFP4 t-row routes require the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
10871        }
10872        let local_out = experts.expert_width / world;
10873        let mut guard = experts
10874            .t2_workspace
10875            .lock()
10876            .map_err(|_| "NVFP4 t2 workspace lock is poisoned")?;
10877        if guard.as_ref().is_none_or(|ws| ws.n_sel != n_sel) {
10878            let mut input2 = Vec::new();
10879            let mut in_q2 = Vec::new();
10880            let mut in_d2 = Vec::new();
10881            let mut sel2 = Vec::new();
10882            let mut route_w2 = Vec::new();
10883            let mut gate_out2 = Vec::new();
10884            let mut up_out2 = Vec::new();
10885            let mut act_q2 = Vec::new();
10886            let mut act_d2 = Vec::new();
10887            let mut partial2 = Vec::new();
10888            let mut acc_a = Vec::new();
10889            let mut acc_b = Vec::new();
10890            let mut acc2 = Vec::new();
10891            let mut ev_rank = Vec::new();
10892            for engine in &self.ranks {
10893                let _m = engine.gpu.enter_main()?;
10894                input2.push(engine.uninit(t * width)?);
10895                in_q2.push(engine.alloc_i8_uninit(t * width)?);
10896                in_d2.push(engine.uninit(t * (width / 32))?);
10897                sel2.push(engine.htod_i32(&vec![0i32; n_sel])?);
10898                route_w2.push(engine.uninit(n_sel)?);
10899                gate_out2.push(engine.uninit(n_sel * local_out)?);
10900                up_out2.push(engine.uninit(n_sel * local_out)?);
10901                act_q2.push(engine.alloc_i8_uninit(n_sel * local_out)?);
10902                act_d2.push(engine.uninit(n_sel * (local_out / 32))?);
10903                partial2.push(engine.uninit(n_sel * width)?);
10904                acc_a.push(engine.uninit(width)?);
10905                acc_b.push(engine.uninit(width)?);
10906                acc2.push(engine.uninit(t * width)?);
10907                ev_rank.push(engine.ctx().new_event(None)?);
10908            }
10909            let root = &self.ranks[0];
10910            let (peer_a, peer_b, omix_a, omix_b, peer2, omix2, ev_root) = {
10911                let _m = root.gpu.enter_main()?;
10912                (
10913                    root.uninit(width)?,
10914                    root.uninit(width)?,
10915                    root.uninit(width)?,
10916                    root.uninit(width)?,
10917                    root.uninit(t * width)?,
10918                    root.uninit(t * width)?,
10919                    root.ctx().new_event(None)?,
10920                )
10921            };
10922            let ev_entry = {
10923                let _m = e.gpu.enter_main()?;
10924                e.ctx().new_event(None)?
10925            };
10926            *guard = Some(Nvfp4T2Workspace {
10927                input2,
10928                in_q2,
10929                in_d2,
10930                sel2,
10931                route_w2,
10932                gate_out2,
10933                up_out2,
10934                act_q2,
10935                act_d2,
10936                partial2,
10937                acc_a,
10938                acc_b,
10939                acc2,
10940                peer2,
10941                omix2,
10942                peer_a,
10943                peer_b,
10944                omix_a,
10945                omix_b,
10946                ev_entry,
10947                ev_rank,
10948                ev_root,
10949                n_sel,
10950                e_device: e.ctx().ordinal(),
10951            });
10952        }
10953        let ws = guard.as_mut().expect("armed above");
10954        if ws.e_device != e.ctx().ordinal() {
10955            return Err("NVFP4 t2 routes engine changed".into());
10956        }
10957        {
10958            let _main = e.gpu.enter_main()?;
10959            ws.ev_entry.record(&e.stream())?;
10960        }
10961        // One decision for the sweep AND the join (an acc2 the sweep never wrote must
10962        // never be joined). t > 2 has no split-accumulator fallback: it requires the
10963        // fused rows kernel.
10964        let down8 = sel_down8_on() && (local_out >> 5) <= 32 && n_sel_col <= 8;
10965        if !down8 && t != 2 {
10966            return Err(
10967                "NVFP4 t-row routes at t != 2 require MEMRA_SEL_DOWN8=1 (fused rows kernel)".into(),
10968            );
10969        }
10970        for rank in 0..world {
10971            let engine = &self.ranks[rank];
10972            let _main = engine.gpu.enter_main()?;
10973            engine.stream().wait(&ws.ev_entry)?;
10974            {
10975                let mut dst = ws.input2[rank].slice_mut(0..t * width);
10976                engine
10977                    .stream()
10978                    .memcpy_dtod(&z_t.slice(0..t * width), &mut dst)?;
10979            }
10980            {
10981                let mut dst = ws.sel2[rank].slice_mut(0..n_sel);
10982                engine
10983                    .stream()
10984                    .memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
10985            }
10986            {
10987                let mut dst = ws.route_w2[rank].slice_mut(0..n_sel);
10988                engine
10989                    .stream()
10990                    .memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
10991            }
10992            {
10993                let Nvfp4T2Workspace {
10994                    input2,
10995                    in_q2,
10996                    in_d2,
10997                    ..
10998                } = &mut *ws;
10999                engine.quantize_q8_1_into(
11000                    &input2[rank],
11001                    t,
11002                    width,
11003                    &mut in_q2[rank],
11004                    &mut in_d2[rank],
11005                )?;
11006            }
11007            let gate_bank = &experts.gate[rank];
11008            let up_bank = &experts.up[rank];
11009            if gate_bank.in_features != up_bank.in_features
11010                || gate_bank.local_out != up_bank.local_out
11011                || gate_bank.row_bytes != up_bank.row_bytes
11012                || gate_bank.expert_bytes != up_bank.expert_bytes
11013            {
11014                return Err("NVFP4 t-row routes need matched gate/up bank geometry".into());
11015            }
11016            {
11017                let Nvfp4T2Workspace {
11018                    sel2,
11019                    in_q2,
11020                    in_d2,
11021                    gate_out2,
11022                    up_out2,
11023                    ..
11024                } = &mut *ws;
11025                engine.qmatvec_nvfp4_sel_gu_tcol_into(
11026                    &gate_bank.bank,
11027                    &up_bank.bank,
11028                    &sel2[rank],
11029                    &in_q2[rank],
11030                    &in_d2[rank],
11031                    &mut gate_out2[rank],
11032                    &mut up_out2[rank],
11033                    n_sel,
11034                    n_sel_col,
11035                    gate_bank.in_features,
11036                    gate_bank.local_out,
11037                    gate_bank.row_bytes,
11038                    gate_bank.expert_bytes,
11039                    width,
11040                    width / 32,
11041                )?;
11042            }
11043            {
11044                let Nvfp4T2Workspace {
11045                    gate_out2,
11046                    up_out2,
11047                    sel2,
11048                    act_q2,
11049                    act_d2,
11050                    ..
11051                } = &mut *ws;
11052                engine.silu_mul_scaled_q8_1_sel_into(
11053                    &gate_out2[rank],
11054                    &up_out2[rank],
11055                    &experts.macros_gate_dev[rank],
11056                    &experts.macros_up_dev[rank],
11057                    &sel2[rank],
11058                    activation_limit,
11059                    &mut act_q2[rank],
11060                    &mut act_d2[rank],
11061                    local_out,
11062                    n_sel,
11063                )?;
11064            }
11065            let shard = &experts.down[rank];
11066            if shard.device_rank != rank || shard.local_in != local_out {
11067                return Err("NVFP4 t-row routes: down shard placement drifted".into());
11068            }
11069            // MEMRA_SEL_DOWN8=1: down sweep + per-row combine in ONE launch (t2 twin of
11070            // the t=1 fusion) — kills the n_sel x width partial round-trip and both axpy
11071            // passes. Each row's FP chain == its own down8/axpy pair (bit-identical).
11072            if down8 {
11073                let Nvfp4T2Workspace {
11074                    sel2,
11075                    act_q2,
11076                    act_d2,
11077                    route_w2,
11078                    acc2,
11079                    ..
11080                } = &mut *ws;
11081                engine.qmatvec_nvfp4_sel_down8_rows_into(
11082                    &shard.bank,
11083                    &sel2[rank],
11084                    &act_q2[rank],
11085                    &act_d2[rank],
11086                    &route_w2[rank],
11087                    &experts.macros_down_dev[rank],
11088                    &mut acc2[rank],
11089                    t,
11090                    n_sel_col,
11091                    shard.local_in,
11092                    shard.out_features,
11093                    shard.row_bytes,
11094                    shard.expert_bytes,
11095                    local_out,
11096                    local_out / 32,
11097                )?;
11098            } else {
11099                {
11100                    let Nvfp4T2Workspace {
11101                        sel2,
11102                        act_q2,
11103                        act_d2,
11104                        partial2,
11105                        ..
11106                    } = &mut *ws;
11107                    engine.qmatvec_nvfp4_sel_into(
11108                        &shard.bank,
11109                        &sel2[rank],
11110                        &act_q2[rank],
11111                        &act_d2[rank],
11112                        &mut partial2[rank],
11113                        n_sel,
11114                        shard.local_in,
11115                        shard.out_features,
11116                        shard.row_bytes,
11117                        shard.expert_bytes,
11118                        local_out,
11119                        local_out / 32,
11120                    )?;
11121                }
11122                let Nvfp4T2Workspace {
11123                    partial2,
11124                    route_w2,
11125                    sel2,
11126                    acc_a,
11127                    acc_b,
11128                    ..
11129                } = &mut *ws;
11130                engine.axpy_rows_seq_md_off_into(
11131                    &partial2[rank],
11132                    &route_w2[rank],
11133                    &experts.macros_down_dev[rank],
11134                    &sel2[rank],
11135                    &mut acc_a[rank],
11136                    width,
11137                    n_sel_col,
11138                    0,
11139                )?;
11140                engine.axpy_rows_seq_md_off_into(
11141                    &partial2[rank],
11142                    &route_w2[rank],
11143                    &experts.macros_down_dev[rank],
11144                    &sel2[rank],
11145                    &mut acc_b[rank],
11146                    width,
11147                    n_sel_col,
11148                    n_sel_col,
11149                )?;
11150            }
11151            if rank != 0 {
11152                ws.ev_rank[rank].record(&engine.stream())?;
11153            }
11154        }
11155        let root = &self.ranks[0];
11156        {
11157            let _main = root.gpu.enter_main()?;
11158            for ev in ws.ev_rank.iter().skip(1) {
11159                root.stream().wait(ev)?;
11160            }
11161            if down8 {
11162                // Fused-slab join: ONE peer pull + ONE elementwise add cover every
11163                // row (independent elements; per-element op == the split join).
11164                let Nvfp4T2Workspace {
11165                    acc2, peer2, omix2, ..
11166                } = &mut *ws;
11167                {
11168                    let mut dst = peer2.slice_mut(0..t * width);
11169                    root.stream()
11170                        .memcpy_dtod(&acc2[1].slice(0..t * width), &mut dst)?;
11171                }
11172                root.add(&acc2[0], peer2, omix2, t * width)?;
11173            } else {
11174                let Nvfp4T2Workspace {
11175                    acc_a,
11176                    acc_b,
11177                    peer_a,
11178                    peer_b,
11179                    omix_a,
11180                    omix_b,
11181                    ..
11182                } = &mut *ws;
11183                {
11184                    let mut dst = peer_a.slice_mut(0..width);
11185                    root.stream()
11186                        .memcpy_dtod(&acc_a[1].slice(0..width), &mut dst)?;
11187                }
11188                {
11189                    let mut dst = peer_b.slice_mut(0..width);
11190                    root.stream()
11191                        .memcpy_dtod(&acc_b[1].slice(0..width), &mut dst)?;
11192                }
11193                root.add(&acc_a[0], peer_a, omix_a, width)?;
11194                root.add(&acc_b[0], peer_b, omix_b, width)?;
11195            }
11196            ws.ev_root.record(&root.stream())?;
11197        }
11198        let _main = e.gpu.enter_main()?;
11199        e.stream().wait(&ws.ev_root)?;
11200        let mut out = e.uninit(t * width)?;
11201        if down8 {
11202            e.stream().memcpy_dtod(
11203                &ws.omix2.slice(0..t * width),
11204                &mut out.slice_mut(0..t * width),
11205            )?;
11206        } else {
11207            e.stream()
11208                .memcpy_dtod(&ws.omix_a.slice(0..width), &mut out.slice_mut(0..width))?;
11209            e.stream().memcpy_dtod(
11210                &ws.omix_b.slice(0..width),
11211                &mut out.slice_mut(width..2 * width),
11212            )?;
11213        }
11214        Ok(out)
11215    }
11216
11217    pub fn run_tensor_parallel_routes_nvfp4_device_routed(
11218        &self,
11219        experts: &ResidentNvfp4TensorParallel,
11220        e: &Engine,
11221        input_dev: &crate::CudaSlice<f32>,
11222        sel_d: &crate::CudaSlice<i32>,
11223        w_d: &crate::CudaSlice<f32>,
11224        experts_per_token: usize,
11225        activation_limit: Option<f32>,
11226    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11227        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
11228            experts,
11229            e,
11230            input_dev,
11231            sel_d,
11232            w_d,
11233            experts_per_token,
11234            activation_limit,
11235            || Ok(()),
11236        )
11237    }
11238
11239    /// `run_tensor_parallel_routes_nvfp4_device_routed` with a PREJOIN hook: `pre_join`
11240    /// runs on the host right before the join wait is enqueued on e's stream — work it
11241    /// issues there (e.g. the shexp overlap) executes WHILE the peer rank finishes its
11242    /// sweep, instead of after the join. Value-neutral by construction (the hook only
11243    /// reorders independent host issue).
11244    #[allow(clippy::too_many_arguments)]
11245    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin(
11246        &self,
11247        experts: &ResidentNvfp4TensorParallel,
11248        e: &Engine,
11249        input_dev: &crate::CudaSlice<f32>,
11250        sel_d: &crate::CudaSlice<i32>,
11251        w_d: &crate::CudaSlice<f32>,
11252        experts_per_token: usize,
11253        activation_limit: Option<f32>,
11254        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11255    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11256        self.run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
11257            experts,
11258            e,
11259            input_dev,
11260            sel_d,
11261            w_d,
11262            experts_per_token,
11263            activation_limit,
11264            pre_join,
11265            None,
11266        )
11267    }
11268
11269    /// The prejoin variant with MOE TAIL FUSION M1: when `post_add = Some((sh_raw,
11270    /// scale_raw))`, the direct-join arm folds the shexp apply into the join add
11271    /// (`dst = (acc0+acc1) + sh*scale[0]`, exact split-pair sequence) — the caller skips
11272    /// its apply launch. Raw UVA pointers so no lock is held across the call.
11273    #[allow(clippy::too_many_arguments)]
11274    pub fn run_tensor_parallel_routes_nvfp4_device_routed_prejoin_add3(
11275        &self,
11276        experts: &ResidentNvfp4TensorParallel,
11277        e: &Engine,
11278        input_dev: &crate::CudaSlice<f32>,
11279        sel_d: &crate::CudaSlice<i32>,
11280        w_d: &crate::CudaSlice<f32>,
11281        experts_per_token: usize,
11282        activation_limit: Option<f32>,
11283        pre_join: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
11284        post_add: Option<(u64, u64)>,
11285    ) -> Result<crate::CudaSlice<f32>, Box<dyn std::error::Error>> {
11286        if input_dev.len() != experts.input_width {
11287            return Err(format!(
11288                "NVFP4 device-routed input {} != width {}",
11289                input_dev.len(),
11290                experts.input_width
11291            )
11292            .into());
11293        }
11294        let n_sel = experts_per_token;
11295        if sel_d.len() < n_sel || w_d.len() < n_sel {
11296            return Err(format!(
11297                "NVFP4 device-routed routes sel={} w={} < experts/token {n_sel}",
11298                sel_d.len(),
11299                w_d.len()
11300            )
11301            .into());
11302        }
11303        let world = self.ranks.len();
11304        if world != NVFP4_CANONICAL_ROW_SHARDS {
11305            return Err(format!(
11306                "NVFP4 device routes require world == canonical shard grid \
11307                 ({NVFP4_CANONICAL_ROW_SHARDS}), got {world}"
11308            )
11309            .into());
11310        }
11311        let local_out = if experts.ep2 {
11312            experts.expert_width
11313        } else {
11314            experts.expert_width / world
11315        };
11316
11317        static TIMING_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11318        static TIMING_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
11319        let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
11320        let started = timing.then(std::time::Instant::now);
11321
11322        let mut workspace_guard = experts
11323            .device_workspace
11324            .lock()
11325            .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11326        if workspace_guard.is_none() {
11327            drop(workspace_guard);
11328            let zero = vec![0.0f32; experts.input_width];
11329            let zero_sel = vec![0usize; n_sel];
11330            let zero_w = vec![0.0f32; n_sel];
11331            let _ = self.run_tensor_parallel_routes_nvfp4_device(
11332                experts,
11333                &zero,
11334                &zero_sel,
11335                &zero_w,
11336                n_sel,
11337                activation_limit,
11338            )?;
11339            workspace_guard = experts
11340                .device_workspace
11341                .lock()
11342                .map_err(|_| "NVFP4 device routes workspace lock is poisoned")?;
11343        }
11344        let workspace = workspace_guard
11345            .as_mut()
11346            .expect("NVFP4 device routes workspace initialized above");
11347        if workspace.n_sel != n_sel {
11348            return Err(format!(
11349                "NVFP4 device routes experts/token changed: workspace {} != call {n_sel}",
11350                workspace.n_sel
11351            )
11352            .into());
11353        }
11354
11355        // GRAPH DOOR (MEMRA_STEP_TP_GRAPH=1): the whole rank+root segment replays as one
11356        // stitched multi-device parent launched on e's stream — no events, no per-token node
11357        // updates (every address is persistent staging). VALUE-IDENTICAL to the eager path:
11358        // the children replay exactly the same kernel/copy sequence.
11359        if step_tp_graph_enabled()? {
11360            if experts.ep2 {
11361                return Err(
11362                    "MEMRA_STEP_TP_GRAPH=1 with MEMRA_STEP_NVFP4_EP2=1 has never been \
11363                     co-gated; unset one"
11364                        .into(),
11365                );
11366            }
11367            if workspace.dev_route_e.is_none() {
11368                let _main = e.gpu.enter_main()?;
11369                workspace.dev_route_e = Some((
11370                    e.htod_i32(&vec![0i32; n_sel])?,
11371                    e.htod(&vec![0.0f32; n_sel])?,
11372                ));
11373            }
11374            if workspace.in_stage_e.is_none() {
11375                let _main = e.gpu.enter_main()?;
11376                workspace.in_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
11377                workspace.out_stage_e = Some(e.htod(&vec![0.0f32; experts.input_width])?);
11378            }
11379            if workspace.routes_graph.is_none() {
11380                let graph = self.nvfp4_routes_build_graph(
11381                    experts,
11382                    workspace,
11383                    local_out,
11384                    n_sel,
11385                    activation_limit,
11386                )?;
11387                workspace.routes_graph = Some(graph);
11388                eprintln!(
11389                    "[step-tp-graph] routes segment captured: ranks={world} n_sel={n_sel} \
11390                     children=3 updates=none performance_claim=false"
11391                );
11392            }
11393            let output = {
11394                let _main = e.gpu.enter_main()?;
11395                {
11396                    let (sel_e, w_e) = workspace
11397                        .dev_route_e
11398                        .as_mut()
11399                        .expect("device route staging set above");
11400                    {
11401                        let mut dst = sel_e.slice_mut(0..n_sel);
11402                        e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11403                    }
11404                    {
11405                        let mut dst = w_e.slice_mut(0..n_sel);
11406                        e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11407                    }
11408                }
11409                {
11410                    let in_stage = workspace
11411                        .in_stage_e
11412                        .as_mut()
11413                        .expect("graph staging set above");
11414                    let mut dst = in_stage.slice_mut(0..experts.input_width);
11415                    e.stream()
11416                        .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut dst)?;
11417                }
11418                unsafe {
11419                    let r = cudarc::driver::sys::cuGraphLaunch(
11420                        workspace
11421                            .routes_graph
11422                            .as_ref()
11423                            .expect("routes graph built above")
11424                            .exec,
11425                        e.stream().cu_stream() as cudarc::driver::sys::CUstream,
11426                    );
11427                    if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
11428                        return Err(format!("routes graph launch: {r:?}").into());
11429                    }
11430                }
11431                let mut output = e.uninit(experts.input_width)?;
11432                {
11433                    let out_stage = workspace
11434                        .out_stage_e
11435                        .as_ref()
11436                        .expect("graph staging set above");
11437                    e.stream().memcpy_dtod(
11438                        &out_stage.slice(0..experts.input_width),
11439                        &mut output.slice_mut(0..experts.input_width),
11440                    )?;
11441                }
11442                output
11443            };
11444            if let Some(started) = started {
11445                use std::sync::atomic::Ordering;
11446                let ns = TIMING_NS
11447                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11448                    + started.elapsed().as_nanos() as u64;
11449                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11450                if calls % 430 == 0 {
11451                    eprintln!(
11452                        "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11453                        ns as f64 / 1.0e6,
11454                        ns as f64 / calls as f64 / 1.0e3,
11455                    );
11456                }
11457            }
11458            return Ok(output);
11459        }
11460
11461        // Entry fence + router-output staging, all on e's stream: the fresh sel/w slices are
11462        // copied into the persistent e-context pair, then the event is recorded — the caller's
11463        // sel_d/w_d can free on e's stream with no cross-stream reader.
11464        if let Some((_, device)) = workspace.ev_entry.as_ref() {
11465            if *device != e.ctx().ordinal() {
11466                return Err("NVFP4 device-routed routes engine changed".into());
11467            }
11468        } else {
11469            let _main = e.gpu.enter_main()?;
11470            workspace.ev_entry = Some((e.ctx().new_event(None)?, e.ctx().ordinal()));
11471        }
11472        if workspace.dev_route_e.is_none() {
11473            let _main = e.gpu.enter_main()?;
11474            workspace.dev_route_e = Some((
11475                e.htod_i32(&vec![0i32; n_sel])?,
11476                e.htod(&vec![0.0f32; n_sel])?,
11477            ));
11478        }
11479        // MEMRA_SEL_MIRROR: the staging pair exists so the rank streams read a persistent
11480        // e-context address. The caller's sel_d/w_d ARE persistent (the process-static
11481        // selection rows), so when every consuming rank shares e's device the ranks can read
11482        // them directly and this hop disappears. The graph door keeps the staging (its
11483        // captured copies read the fixed addresses).
11484        let mirror = sel_mirror_on() && !step_tp_graph_enabled()?;
11485        let e_device = e.ctx().ordinal();
11486        // rank1_routed is consumed (taken) below; peek it here for the staging decision.
11487        let rank1_routed_peek = workspace.rank1_routed;
11488        let stage_needed = !mirror
11489            || self.ranks.iter().enumerate().any(|(rank_index, engine)| {
11490                !(rank1_routed_peek && rank_index == 1) && engine.ctx().ordinal() != e_device
11491            });
11492        {
11493            let _main = e.gpu.enter_main()?;
11494            if stage_needed {
11495                let (sel_e, w_e) = workspace
11496                    .dev_route_e
11497                    .as_mut()
11498                    .expect("device route staging set above");
11499                {
11500                    let mut dst = sel_e.slice_mut(0..n_sel);
11501                    e.stream().memcpy_dtod(&sel_d.slice(0..n_sel), &mut dst)?;
11502                }
11503                {
11504                    let mut dst = w_e.slice_mut(0..n_sel);
11505                    e.stream().memcpy_dtod(&w_d.slice(0..n_sel), &mut dst)?;
11506                }
11507            }
11508            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11509            ev_entry.record(&e.stream())?;
11510        }
11511        // Prestage door: input pull + quantize were already issued on the rank streams
11512        // (before the router) — the rank stream order suffices, skip them here.
11513        let prestaged = std::mem::take(&mut workspace.prestaged);
11514        let rank1_routed = std::mem::take(&mut workspace.rank1_routed);
11515        for (rank_index, engine) in self.ranks.iter().enumerate() {
11516            let _main = engine.gpu.enter_main()?;
11517            let (ev_entry, _) = workspace.ev_entry.as_ref().expect("entry event set above");
11518            engine.stream().wait(ev_entry)?;
11519            if !prestaged {
11520                let mut destination = workspace.input[rank_index].slice_mut(0..experts.input_width);
11521                engine
11522                    .stream()
11523                    .memcpy_dtod(&input_dev.slice(0..experts.input_width), &mut destination)?;
11524            }
11525            if !(rank1_routed && rank_index == 1) {
11526                // ONE mirror launch instead of two 32-byte copy-engine dispatches; source is
11527                // the caller's persistent rows when this rank shares e's device (UVA, ordered
11528                // by ev_entry), else the staged e-context pair.
11529                let same_dev = engine.ctx().ordinal() == e_device;
11530                if mirror {
11531                    // Split the workspace borrow so the source (the staged pair, when this
11532                    // rank is off-device) and the destination rows coexist.
11533                    let Nvfp4DeviceRoutesWorkspace {
11534                        sel,
11535                        route_w,
11536                        dev_route_e,
11537                        ..
11538                    } = &mut *workspace;
11539                    let (src_sel, src_w): (&crate::CudaSlice<i32>, &crate::CudaSlice<f32>) =
11540                        if same_dev {
11541                            (sel_d, w_d)
11542                        } else {
11543                            let (sel_e, w_e) = dev_route_e
11544                                .as_ref()
11545                                .expect("device route staging set above");
11546                            (sel_e, w_e)
11547                        };
11548                    engine.moe_sel_w_mirror(
11549                        src_sel,
11550                        src_w,
11551                        &mut sel[rank_index],
11552                        &mut route_w[rank_index],
11553                        n_sel,
11554                    )?;
11555                } else {
11556                    let (sel_e, w_e) = workspace
11557                        .dev_route_e
11558                        .as_ref()
11559                        .expect("device route staging set above");
11560                    {
11561                        let mut dst = workspace.sel[rank_index].slice_mut(0..n_sel);
11562                        engine
11563                            .stream()
11564                            .memcpy_dtod(&sel_e.slice(0..n_sel), &mut dst)?;
11565                    }
11566                    {
11567                        let mut dst = workspace.route_w[rank_index].slice_mut(0..n_sel);
11568                        engine
11569                            .stream()
11570                            .memcpy_dtod(&w_e.slice(0..n_sel), &mut dst)?;
11571                    }
11572                }
11573            }
11574            if !prestaged {
11575                let Nvfp4DeviceRoutesWorkspace {
11576                    input, in_q, in_d, ..
11577                } = &mut *workspace;
11578                engine.quantize_q8_1_into(
11579                    &input[rank_index],
11580                    1,
11581                    experts.input_width,
11582                    &mut in_q[rank_index],
11583                    &mut in_d[rank_index],
11584                )?;
11585            }
11586        }
11587        self.nvfp4_routes_batched_sweeps(
11588            experts,
11589            workspace,
11590            &[],
11591            &[],
11592            &[],
11593            local_out,
11594            n_sel,
11595            activation_limit,
11596            true,
11597        )?;
11598
11599        // rank0 == root: its own stream order already covers its sweep; only the PEER
11600        // ranks need the record/wait pair (host-op diet at the #1 eager seam, 2026-08-21).
11601        for (rank_index, engine) in self.ranks.iter().enumerate().skip(1) {
11602            let _main = engine.gpu.enter_main()?;
11603            workspace.ev_rank[rank_index].record(&engine.stream())?;
11604        }
11605        // Doorbell fences (MEMRA_FENCE_MEMOPS=1): rank1 + root ring their flags; e waits
11606        // the tickets instead of the two events. Arm lazily; 0-len = unsupported.
11607        let memops = fence_memops_on() && moe_direct_on() && self.ranks.len() == 2;
11608        let mut ticket = 0u32;
11609        if memops {
11610            use cudarc::driver::sys;
11611            if workspace.fence_flags_raw == 0 {
11612                let root = &self.ranks[0];
11613                let _main = root.gpu.enter_main()?;
11614                let mut ptr: sys::CUdeviceptr = 0;
11615                let r = unsafe { sys::cuMemAlloc_v2(&mut ptr, 8) };
11616                if r != sys::CUresult::CUDA_SUCCESS {
11617                    return Err(format!("fence flag alloc: {r:?}").into());
11618                }
11619                let r = unsafe { sys::cuMemsetD8_v2(ptr, 0, 8) };
11620                if r != sys::CUresult::CUDA_SUCCESS {
11621                    return Err(format!("fence flag memset: {r:?}").into());
11622                }
11623                workspace.fence_flags_raw = ptr as u64;
11624            }
11625            workspace.fence_ticket = workspace.fence_ticket.wrapping_add(1).max(1);
11626            ticket = workspace.fence_ticket;
11627            let base = workspace.fence_flags_raw;
11628            // rank1's fence: a peer stream MEMOP is rejected over PCIe P2P
11629            // (CUDA_ERROR_INVALID_VALUE, receipted 2026-08-23), but a peer KERNEL STORE into
11630            // root memory is legal — the direct join already relies on it. Under
11631            // MEMRA_FENCE_RANK1 rank1 rings flag[0] that way and e waits it same-device,
11632            // replacing the cross-device event wait below.
11633            if fence_rank1_on() {
11634                let peer = &self.ranks[1];
11635                let _pmain = peer.gpu.enter_main()?;
11636                peer.ring_flag_raw(base, ticket)?;
11637            }
11638            {
11639                let root = &self.ranks[0];
11640                let _main = root.gpu.enter_main()?;
11641                let r = unsafe {
11642                    sys::cuStreamWriteValue32_v2(
11643                        root.stream().cu_stream() as sys::CUstream,
11644                        (base + 4) as sys::CUdeviceptr,
11645                        ticket,
11646                        0,
11647                    )
11648                };
11649                if r != sys::CUresult::CUDA_SUCCESS {
11650                    return Err(format!("fence write root: {r:?}").into());
11651                }
11652            }
11653        }
11654        // PREJOIN hook: rank work is fully issued (dev1 running); independent e-stream
11655        // kernels queued here execute while the peer rank drains its sweep.
11656        pre_join()?;
11657
11658        if moe_direct_on() && self.ranks.len() == 2 {
11659            // DIRECT JOIN: rank1's accumulator is root-resident (P2P single-store pass);
11660            // rank0's is root-stream-ordered. One root event + rank1's own event order
11661            // the model engine's single add — same operand order as root's add
11662            // (accumulator[0] + accumulator[1]): BIT-IDENTICAL. Output is a FRESH
11663            // e-context row (NOT an alias of ws state — the reverted zero-copy handoff's
11664            // hazard class does not apply).
11665            let _main = e.gpu.enter_main()?;
11666            if memops {
11667                use cudarc::driver::sys;
11668                let base = workspace.fence_flags_raw;
11669                let r = unsafe {
11670                    sys::cuStreamWaitValue32_v2(
11671                        e.stream().cu_stream() as sys::CUstream,
11672                        (base + 4) as sys::CUdeviceptr,
11673                        ticket,
11674                        sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
11675                    )
11676                };
11677                if r != sys::CUresult::CUDA_SUCCESS {
11678                    return Err(format!("fence wait: {r:?}").into());
11679                }
11680                if fence_rank1_on() {
11681                    // Same-device wait on the flag rank1 rang over P2P.
11682                    let r = unsafe {
11683                        sys::cuStreamWaitValue32_v2(
11684                            e.stream().cu_stream() as sys::CUstream,
11685                            base as sys::CUdeviceptr,
11686                            ticket,
11687                            sys::CUstreamWaitValue_flags::CU_STREAM_WAIT_VALUE_GEQ as u32,
11688                        )
11689                    };
11690                    if r != sys::CUresult::CUDA_SUCCESS {
11691                        return Err(format!("fence wait rank1: {r:?}").into());
11692                    }
11693                } else {
11694                    for ev in workspace.ev_rank.iter().skip(1) {
11695                        e.stream().wait(ev)?;
11696                    }
11697                }
11698            } else {
11699                {
11700                    let root = &self.ranks[0];
11701                    let _rmain = root.gpu.enter_main()?;
11702                    workspace
11703                        .ev_done
11704                        .as_ref()
11705                        .expect("device routes done event")
11706                        .record(&root.stream())?;
11707                }
11708                e.stream().wait(
11709                    workspace
11710                        .ev_done
11711                        .as_ref()
11712                        .expect("device routes done event"),
11713                )?;
11714                for ev in workspace.ev_rank.iter().skip(1) {
11715                    e.stream().wait(ev)?;
11716                }
11717            }
11718            let mut output = e.uninit(experts.input_width)?;
11719            if let Some((sh_raw, scale_raw)) = post_add {
11720                // MOE TAIL FUSION M1: fold the shexp apply into the join add —
11721                // dst = (acc0 + acc1) + sh*scale[0], the exact split-pair sequence.
11722                e.add3_raw(
11723                    &workspace.accumulator[0],
11724                    &workspace.accumulator[1],
11725                    sh_raw,
11726                    scale_raw,
11727                    &mut output,
11728                    experts.input_width,
11729                )?;
11730            } else {
11731                e.add(
11732                    &workspace.accumulator[0],
11733                    &workspace.accumulator[1],
11734                    &mut output,
11735                    experts.input_width,
11736                )?;
11737            }
11738            let output = output;
11739            if let Some(started) = started {
11740                use std::sync::atomic::Ordering;
11741                let ns = TIMING_NS
11742                    .fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11743                    + started.elapsed().as_nanos() as u64;
11744                let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11745                if calls % 430 == 0 {
11746                    eprintln!(
11747                        "[nvfp4-dev-routes-direct-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11748                        ns as f64 / 1.0e6,
11749                        ns as f64 / calls as f64 / 1.0e3,
11750                    );
11751                }
11752            }
11753            return Ok(output);
11754        }
11755        {
11756            let root = &self.ranks[0];
11757            let _main = root.gpu.enter_main()?;
11758            for ev in workspace.ev_rank.iter().skip(1) {
11759                root.stream().wait(ev)?;
11760            }
11761            root.stream()
11762                .memcpy_dtod(&workspace.accumulator[1], &mut workspace.remote)?;
11763            {
11764                let Nvfp4DeviceRoutesWorkspace {
11765                    accumulator,
11766                    remote,
11767                    combined,
11768                    ..
11769                } = &mut *workspace;
11770                root.add(&accumulator[0], remote, combined, experts.input_width)?;
11771            }
11772            workspace
11773                .ev_done
11774                .as_ref()
11775                .expect("device routes done event")
11776                .record(&root.stream())?;
11777        }
11778        let output = {
11779            let _main = e.gpu.enter_main()?;
11780            e.stream().wait(
11781                workspace
11782                    .ev_done
11783                    .as_ref()
11784                    .expect("device routes done event"),
11785            )?;
11786            // (Zero-copy clone handoff REVERTED 2026-08-21: identity mismatch in the
11787            // routes-diet bisect. The alloc+copy stays until the hazard is understood.)
11788            let mut output = e.uninit(experts.input_width)?;
11789            e.stream().memcpy_dtod(
11790                &workspace.combined.slice(0..experts.input_width),
11791                &mut output.slice_mut(0..experts.input_width),
11792            )?;
11793            output
11794        };
11795        if let Some(started) = started {
11796            use std::sync::atomic::Ordering;
11797            let ns = TIMING_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
11798                + started.elapsed().as_nanos() as u64;
11799            let calls = TIMING_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
11800            if calls % 430 == 0 {
11801                eprintln!(
11802                    "[nvfp4-dev-routed-timing] calls={calls} total_ms={:.1} avg_us={:.1}",
11803                    ns as f64 / 1.0e6,
11804                    ns as f64 / calls as f64 / 1.0e3,
11805                );
11806            }
11807        }
11808        Ok(output)
11809    }
11810
11811    /// The fused finish's ROOT section (combine + shadow gathers), event-free: the eager
11812    /// caller wraps it with rank-event waits + the done record; the token graph captures it
11813    /// verbatim (parent edges provide the ordering).
11814    pub(crate) fn decode_v2_finish_root_fused(
11815        &self,
11816        ws: &mut StepTpDecodeV2Ws,
11817    ) -> Result<(), Box<dyn std::error::Error>> {
11818        let root = &self.ranks[0];
11819        let _main = root.gpu.enter_main()?;
11820        if ws.raw_peer_partial != 0 {
11821            // Capture-safe raw seams (arming happened in the stage flow).
11822            raw_copy_bytes(ws.raw_peer_partial, ws.raw_o_partial1, ws.o_out * 4, root)?;
11823        } else {
11824            root.stream()
11825                .memcpy_dtod(&ws.o_partials[1][0], &mut ws.peer_partial)?;
11826        }
11827        {
11828            let StepTpDecodeV2Ws {
11829                o_partials,
11830                peer_partial,
11831                reduce_a,
11832                o_out,
11833                ..
11834            } = &mut *ws;
11835            root.add(&o_partials[0][0], peer_partial, reduce_a, *o_out)?;
11836        }
11837        let shadows = !no_local_shadow_on() || ws.raw_mixed_stage_e != 0;
11838        if shadows {
11839            // rank0's shadows are same-context (root) copies; rank1's cross-context reads go
11840            // raw when armed.
11841            let mut k_dst = ws.k_shadow.slice_mut(0..ws.local_kv_dim);
11842            root.stream().memcpy_dtod(&ws.k[0], &mut k_dst)?;
11843            let mut v_dst = ws.v_shadow.slice_mut(0..ws.local_kv_dim);
11844            root.stream().memcpy_dtod(&ws.v_raw[0], &mut v_dst)?;
11845        }
11846        if shadows && ws.raw_peer_partial != 0 {
11847            raw_copy_bytes(
11848                ws.raw_k_shadow + (ws.local_kv_dim * 4) as u64,
11849                ws.raw_k1,
11850                ws.local_kv_dim * 4,
11851                root,
11852            )?;
11853            raw_copy_bytes(
11854                ws.raw_v_shadow + (ws.local_kv_dim * 4) as u64,
11855                ws.raw_v1,
11856                ws.local_kv_dim * 4,
11857                root,
11858            )?;
11859        } else if shadows {
11860            let start = ws.local_kv_dim;
11861            let mut k_dst = ws.k_shadow.slice_mut(start..start + ws.local_kv_dim);
11862            root.stream().memcpy_dtod(&ws.k[1], &mut k_dst)?;
11863            let mut v_dst = ws.v_shadow.slice_mut(start..start + ws.local_kv_dim);
11864            root.stream().memcpy_dtod(&ws.v_raw[1], &mut v_dst)?;
11865        }
11866        if ws.raw_mixed_stage_e != 0 {
11867            // Token-graph mirrors: the e-glue children read same-context copies of the
11868            // root-produced rows.
11869            raw_copy_bytes(ws.raw_mixed_stage_e, ws.raw_reduce_a, ws.o_out * 4, root)?;
11870            let (k_stage, v_stage) = ws.raw_shadow_stage_e;
11871            raw_copy_bytes(k_stage, ws.raw_k_shadow, 2 * ws.local_kv_dim * 4, root)?;
11872            raw_copy_bytes(v_stage, ws.raw_v_shadow, 2 * ws.local_kv_dim * 4, root)?;
11873        }
11874        Ok(())
11875    }
11876
11877    /// Arm the token-graph e-context mirrors (orchestrator-supplied fixed addresses) plus
11878    /// reduce_a's own pointer.
11879    pub(crate) fn decode_v2_arm_token_mirrors(
11880        &self,
11881        ws: &mut StepTpDecodeV2Ws,
11882        mixed_stage_e: u64,
11883        shadow_stage_e: (u64, u64),
11884    ) -> Result<(), Box<dyn std::error::Error>> {
11885        use cudarc::driver::DevicePtr;
11886        let root = &self.ranks[0];
11887        let _main = root.gpu.enter_main()?;
11888        let stream = root.stream();
11889        let (a, _g) = ws.reduce_a.device_ptr(&stream);
11890        ws.raw_reduce_a = a as u64;
11891        ws.raw_mixed_stage_e = mixed_stage_e;
11892        ws.raw_shadow_stage_e = shadow_stage_e;
11893        Ok(())
11894    }
11895
11896    /// Build one layer's stitched routes graph: per-rank children captured on their own
11897    /// streams (raw cuMemcpyAsync at every cross-context seam — cudarc's slice tracking is
11898    /// capture-illegal there), a root combine child, and a multi-device parent with
11899    /// {rank0, rank1} -> root dependency edges. Zero per-token updates: every address the
11900    /// nodes touch is persistent workspace/staging.
11901    fn nvfp4_routes_build_graph(
11902        &self,
11903        experts: &ResidentNvfp4TensorParallel,
11904        workspace: &mut Nvfp4DeviceRoutesWorkspace,
11905        local_out: usize,
11906        n_sel: usize,
11907        activation_limit: Option<f32>,
11908    ) -> Result<RoutesGraph, Box<dyn std::error::Error>> {
11909        use cudarc::driver::DevicePtr;
11910        use cudarc::driver::sys;
11911        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
11912            if r == sys::CUresult::CUDA_SUCCESS {
11913                Ok(())
11914            } else {
11915                Err(format!("{what}: {r:?}").into())
11916            }
11917        }
11918        let world = self.ranks.len();
11919        if world != 2 {
11920            return Err("routes graph door is built for the TP2 pair".into());
11921        }
11922        let width = experts.input_width;
11923
11924        // Raw pointers cached before capture (each read with its owner's stream).
11925        let ptr_f32 = |buf: &crate::CudaSlice<f32>, engine: &Engine| -> u64 {
11926            let stream = engine.stream();
11927            let (ptr, _g) = buf.device_ptr(&stream);
11928            ptr as u64
11929        };
11930        let ptr_i32 = |buf: &crate::CudaSlice<i32>, engine: &Engine| -> u64 {
11931            let stream = engine.stream();
11932            let (ptr, _g) = buf.device_ptr(&stream);
11933            ptr as u64
11934        };
11935        let (sel_e, w_e) = workspace
11936            .dev_route_e
11937            .as_ref()
11938            .expect("device route staging set before graph build");
11939        let root_engine = &self.ranks[0];
11940        let p_in_stage = ptr_f32(
11941            workspace.in_stage_e.as_ref().expect("graph staging"),
11942            root_engine,
11943        );
11944        let p_out_stage = ptr_f32(
11945            workspace.out_stage_e.as_ref().expect("graph staging"),
11946            root_engine,
11947        );
11948        let p_sel_e = ptr_i32(sel_e, root_engine);
11949        let p_w_e = ptr_f32(w_e, root_engine);
11950        let p_input: Vec<u64> = (0..world)
11951            .map(|r| ptr_f32(&workspace.input[r], &self.ranks[r]))
11952            .collect();
11953        let p_sel: Vec<u64> = (0..world)
11954            .map(|r| ptr_i32(&workspace.sel[r], &self.ranks[r]))
11955            .collect();
11956        let p_route_w: Vec<u64> = (0..world)
11957            .map(|r| ptr_f32(&workspace.route_w[r], &self.ranks[r]))
11958            .collect();
11959        let p_acc1 = ptr_f32(&workspace.accumulator[1], &self.ranks[1]);
11960        let p_remote = ptr_f32(&workspace.remote, root_engine);
11961        let p_combined = ptr_f32(&workspace.combined, root_engine);
11962
11963        let raw_copy = |dst: u64,
11964                        src: u64,
11965                        bytes: usize,
11966                        engine: &Engine|
11967         -> Result<(), Box<dyn std::error::Error>> {
11968            unsafe {
11969                cu_try(
11970                    sys::cuMemcpyAsync(
11971                        dst as sys::CUdeviceptr,
11972                        src as sys::CUdeviceptr,
11973                        bytes,
11974                        engine.stream().cu_stream() as sys::CUstream,
11975                    ),
11976                    "routes graph cuMemcpyAsync",
11977                )
11978            }
11979        };
11980
11981        let mut children = Vec::with_capacity(3);
11982        for rank in 0..world {
11983            let engine = &self.ranks[rank];
11984            let _main = engine.gpu.enter_main()?;
11985            let (child, _retained) = engine.capture_graph_retained(|_| {
11986                raw_copy(p_input[rank], p_in_stage, width * 4, engine)?;
11987                raw_copy(p_sel[rank], p_sel_e, n_sel * 4, engine)?;
11988                raw_copy(p_route_w[rank], p_w_e, n_sel * 4, engine)?;
11989                {
11990                    let Nvfp4DeviceRoutesWorkspace {
11991                        input, in_q, in_d, ..
11992                    } = &mut *workspace;
11993                    engine.quantize_q8_1_into(
11994                        &input[rank],
11995                        1,
11996                        width,
11997                        &mut in_q[rank],
11998                        &mut in_d[rank],
11999                    )?;
12000                }
12001                self.nvfp4_routes_batched_sweeps_rank(
12002                    experts,
12003                    workspace,
12004                    &[],
12005                    &[],
12006                    &[],
12007                    local_out,
12008                    n_sel,
12009                    activation_limit,
12010                    true,
12011                    rank,
12012                )?;
12013                Ok(())
12014            })?;
12015            children.push(child);
12016        }
12017        {
12018            let root = &self.ranks[0];
12019            let _main = root.gpu.enter_main()?;
12020            let (child, _retained) = root.capture_graph_retained(|_| {
12021                raw_copy(p_remote, p_acc1, width * 4, root)?;
12022                {
12023                    let Nvfp4DeviceRoutesWorkspace {
12024                        accumulator,
12025                        remote,
12026                        combined,
12027                        ..
12028                    } = &mut *workspace;
12029                    root.add(&accumulator[0], remote, combined, width)?;
12030                }
12031                raw_copy(p_out_stage, p_combined, width * 4, root)?;
12032                Ok(())
12033            })?;
12034            children.push(child);
12035        }
12036
12037        let mut parent: sys::CUgraph = std::ptr::null_mut();
12038        unsafe {
12039            cu_try(sys::cuGraphCreate(&mut parent, 0), "routes cuGraphCreate")?;
12040        }
12041        let mut n0: sys::CUgraphNode = std::ptr::null_mut();
12042        let mut n1: sys::CUgraphNode = std::ptr::null_mut();
12043        let mut n2: sys::CUgraphNode = std::ptr::null_mut();
12044        unsafe {
12045            cu_try(
12046                sys::cuGraphAddChildGraphNode(
12047                    &mut n0,
12048                    parent,
12049                    std::ptr::null(),
12050                    0,
12051                    children[0].cu_graph(),
12052                ),
12053                "routes child r0",
12054            )?;
12055            cu_try(
12056                sys::cuGraphAddChildGraphNode(
12057                    &mut n1,
12058                    parent,
12059                    std::ptr::null(),
12060                    0,
12061                    children[1].cu_graph(),
12062                ),
12063                "routes child r1",
12064            )?;
12065            let deps = [n0, n1];
12066            cu_try(
12067                sys::cuGraphAddChildGraphNode(
12068                    &mut n2,
12069                    parent,
12070                    deps.as_ptr(),
12071                    2,
12072                    children[2].cu_graph(),
12073                ),
12074                "routes child root",
12075            )?;
12076        }
12077        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
12078        unsafe {
12079            cu_try(
12080                sys::cuGraphInstantiateWithFlags(&mut exec, parent, 0),
12081                "routes instantiate",
12082            )?;
12083        }
12084        Ok(RoutesGraph {
12085            exec,
12086            parent,
12087            _children: children,
12088        })
12089    }
12090
12091    /// One rank's routes section for the token graph (event-free): staged input copy (raw
12092    /// when the caller supplies the source pointer), quantize, and the batched sweeps.
12093    /// Eager device_routed wraps it with the entry-event wait.
12094    #[allow(clippy::too_many_arguments)]
12095    pub(crate) fn routes_rank_section(
12096        &self,
12097        experts: &ResidentNvfp4TensorParallel,
12098        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12099        raw_input_src: u64,
12100        local_out: usize,
12101        n_sel: usize,
12102        activation_limit: Option<f32>,
12103        rank_index: usize,
12104    ) -> Result<(), Box<dyn std::error::Error>> {
12105        let engine = &self.ranks[rank_index];
12106        {
12107            let _main = engine.gpu.enter_main()?;
12108            // sel/route_w land via raw copies from the e staging (fixed addresses).
12109            let (sel_e_ptr, w_e_ptr) = workspace
12110                .raw_dev_route_e
12111                .ok_or("routes rank section requires armed staging pointers")?;
12112            raw_copy_bytes(
12113                workspace.raw_input[rank_index],
12114                raw_input_src,
12115                experts.input_width * 4,
12116                engine,
12117            )?;
12118            raw_copy_bytes(workspace.raw_sel[rank_index], sel_e_ptr, n_sel * 4, engine)?;
12119            raw_copy_bytes(
12120                workspace.raw_route_w[rank_index],
12121                w_e_ptr,
12122                n_sel * 4,
12123                engine,
12124            )?;
12125            {
12126                let Nvfp4DeviceRoutesWorkspace {
12127                    input, in_q, in_d, ..
12128                } = &mut *workspace;
12129                engine.quantize_q8_1_into(
12130                    &input[rank_index],
12131                    1,
12132                    experts.input_width,
12133                    &mut in_q[rank_index],
12134                    &mut in_d[rank_index],
12135                )?;
12136            }
12137        }
12138        self.nvfp4_routes_batched_sweeps_rank(
12139            experts,
12140            workspace,
12141            &[],
12142            &[],
12143            &[],
12144            local_out,
12145            n_sel,
12146            activation_limit,
12147            true,
12148            rank_index,
12149        )
12150    }
12151
12152    /// The routes ROOT combine section (event-free): peer accumulator read (raw), canonical
12153    /// add, combined row raw-copied into the fixed e-context out stage.
12154    pub(crate) fn routes_root_section(
12155        &self,
12156        experts: &ResidentNvfp4TensorParallel,
12157        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12158    ) -> Result<(), Box<dyn std::error::Error>> {
12159        let root = &self.ranks[0];
12160        let _main = root.gpu.enter_main()?;
12161        let (acc1_ptr, remote_ptr, combined_ptr, out_stage_ptr) = workspace
12162            .raw_combine
12163            .ok_or("routes root section requires armed combine pointers")?;
12164        raw_copy_bytes(remote_ptr, acc1_ptr, experts.input_width * 4, root)?;
12165        {
12166            let Nvfp4DeviceRoutesWorkspace {
12167                accumulator,
12168                remote,
12169                combined,
12170                ..
12171            } = &mut *workspace;
12172            root.add(&accumulator[0], remote, combined, experts.input_width)?;
12173        }
12174        raw_copy_bytes(out_stage_ptr, combined_ptr, experts.input_width * 4, root)?;
12175        Ok(())
12176    }
12177
12178    /// Arm the routes raw pointers (once): staging pair, per-rank input/sel/route_w, and the
12179    /// combine set. Requires dev_route_e + in/out stages already allocated.
12180    pub(crate) fn routes_arm_raw(
12181        &self,
12182        experts: &ResidentNvfp4TensorParallel,
12183        workspace: &mut Nvfp4DeviceRoutesWorkspace,
12184    ) -> Result<(), Box<dyn std::error::Error>> {
12185        use cudarc::driver::DevicePtr;
12186        if workspace.raw_dev_route_e.is_some() {
12187            return Ok(());
12188        }
12189        let _ = experts;
12190        let (sel_e, w_e) = workspace
12191            .dev_route_e
12192            .as_ref()
12193            .ok_or("routes staging not armed")?;
12194        let root = &self.ranks[0];
12195        {
12196            let _main = root.gpu.enter_main()?;
12197            let stream = root.stream();
12198            let (a, _g) = sel_e.device_ptr(&stream);
12199            let (b, _g) = w_e.device_ptr(&stream);
12200            workspace.raw_dev_route_e = Some((a as u64, b as u64));
12201            let (c, _g) = workspace.accumulator[1].device_ptr(&stream);
12202            let (d, _g) = workspace.remote.device_ptr(&stream);
12203            let (f, _g) = workspace.combined.device_ptr(&stream);
12204            let out_stage = workspace
12205                .out_stage_e
12206                .as_ref()
12207                .ok_or("routes out stage not armed")?;
12208            let (g_, _g) = out_stage.device_ptr(&stream);
12209            workspace.raw_combine = Some((c as u64, d as u64, f as u64, g_ as u64));
12210        }
12211        for rank in 0..self.ranks.len() {
12212            let engine = &self.ranks[rank];
12213            let _main = engine.gpu.enter_main()?;
12214            let stream = engine.stream();
12215            let (a, _g) = workspace.input[rank].device_ptr(&stream);
12216            let (b, _g) = workspace.sel[rank].device_ptr(&stream);
12217            let (c, _g) = workspace.route_w[rank].device_ptr(&stream);
12218            workspace.raw_input.push(a as u64);
12219            workspace.raw_sel.push(b as u64);
12220            workspace.raw_route_w.push(c as u64);
12221        }
12222        Ok(())
12223    }
12224
12225    /// Routed NVFP4 expert program, host-canonical transport. Native/bulk P2P transport for the
12226    /// NVFP4 bank is a separate increment; this entry point is exactness-first and reports no
12227    /// throughput claim.
12228    pub fn run_tensor_parallel_routes_nvfp4(
12229        &self,
12230        experts: &ResidentNvfp4TensorParallel,
12231        input: &[f32],
12232        tokens: usize,
12233        selected: &[usize],
12234        route_weights: &[f32],
12235        experts_per_token: usize,
12236        activation_limit: Option<f32>,
12237    ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
12238        validate_activations(input, tokens, experts.input_width)?;
12239        let pairs = tokens
12240            .checked_mul(experts_per_token)
12241            .ok_or("NVFP4 TP route count overflow")?;
12242        if selected.len() != pairs || route_weights.len() != pairs {
12243            return Err(format!(
12244                "NVFP4 TP routes selected={} weights={} != tokens {tokens} x experts/token \
12245                 {experts_per_token} ({pairs})",
12246                selected.len(),
12247                route_weights.len(),
12248            )
12249            .into());
12250        }
12251        if !route_weights.iter().all(|weight| weight.is_finite()) {
12252            return Err("NVFP4 TP route weights contain a non-finite value".into());
12253        }
12254
12255        let mut output = vec![0.0f32; tokens * experts.input_width];
12256        for token in 0..tokens {
12257            let input_row = &input[token * experts.input_width..(token + 1) * experts.input_width];
12258            for slot in 0..experts_per_token {
12259                let pair = token * experts_per_token + slot;
12260                let expert = selected[pair];
12261                if expert >= experts.expert_count {
12262                    return Err(format!(
12263                        "NVFP4 TP selected expert {expert} outside 0..{}",
12264                        experts.expert_count
12265                    )
12266                    .into());
12267                }
12268                // EP2 banks hold the WHOLE expert on rank (expert & 1) at slot (expert >> 1);
12269                // per-row dots are the same full-width program either way (a column shard
12270                // splits ROWS, not the dot), so gate/up are bit-equal across layouts. Only
12271                // down's parenthesization moves (full-width dot vs canonical 2-shard sum) —
12272                // the numeric-class this door declares.
12273                let gate = if experts.ep2 {
12274                    self.run_full_bank_expert_nvfp4(
12275                        &experts.gate,
12276                        &experts.macros_gate,
12277                        expert,
12278                        input_row,
12279                    )?
12280                } else {
12281                    self.run_column_bank_expert_nvfp4(
12282                        &experts.gate,
12283                        &experts.macros_gate,
12284                        expert,
12285                        input_row,
12286                    )?
12287                };
12288                let up = if experts.ep2 {
12289                    self.run_full_bank_expert_nvfp4(
12290                        &experts.up,
12291                        &experts.macros_up,
12292                        expert,
12293                        input_row,
12294                    )?
12295                } else {
12296                    self.run_column_bank_expert_nvfp4(
12297                        &experts.up,
12298                        &experts.macros_up,
12299                        expert,
12300                        input_row,
12301                    )?
12302                };
12303                let activated: Vec<f32> = gate
12304                    .iter()
12305                    .zip(&up)
12306                    .map(|(&gate, &up)| step_expert_activation_host(gate, up, activation_limit))
12307                    .collect();
12308                debug_assert_eq!(activated.len(), experts.expert_width);
12309                let down = if experts.ep2 {
12310                    self.run_full_down_expert_nvfp4(
12311                        &experts.down,
12312                        &experts.macros_down,
12313                        expert,
12314                        &activated,
12315                    )?
12316                } else {
12317                    self.run_row_bank_expert_nvfp4(
12318                        &experts.down,
12319                        &experts.macros_down,
12320                        expert,
12321                        &activated,
12322                    )?
12323                };
12324                let weight = route_weights[pair];
12325                for (sum, value) in output
12326                    [token * experts.input_width..(token + 1) * experts.input_width]
12327                    .iter_mut()
12328                    .zip(down)
12329                {
12330                    *sum += weight * value;
12331                }
12332            }
12333        }
12334        Ok(output)
12335    }
12336}
12337
12338#[cfg(test)]
12339mod tests {
12340    use super::*;
12341
12342    #[test]
12343    fn step_expert_activation_clamps_each_arm_by_the_official_contract() {
12344        let limit = Some(7.0);
12345        assert_eq!(step_expert_activation_host(20.0, 9.0, limit), 49.0);
12346        assert_eq!(step_expert_activation_host(20.0, -9.0, limit), -49.0);
12347        assert!(
12348            step_expert_activation_host(-20.0, 9.0, limit).abs()
12349                < step_expert_activation_host(-20.0, 9.0, None).abs()
12350        );
12351        assert!(validate_step_expert_activation_limit(Some(f32::NAN)).is_err());
12352        assert!(validate_step_expert_activation_limit(Some(0.0)).is_err());
12353        assert!(validate_step_expert_activation_limit(limit).is_ok());
12354    }
12355
12356    #[test]
12357    fn moe_residual_host_preserves_official_add_order() {
12358        let output = moe_residual_host(&[1.0e20], &[-1.0e20], &[1.0]).unwrap();
12359        assert_eq!(output, [0.0]);
12360        assert_eq!(
12361            moe_residual_host(&[0.0], &[0.0, 1.0], &[0.0]).unwrap_err(),
12362            "MoE residual lengths residual=1 routed=2 shared=1"
12363        );
12364    }
12365
12366    #[test]
12367    fn expert_owner_routes_preserve_global_pair_order_with_local_expert_ids() {
12368        let selected = [0, 36, 72, 108, 144, 180, 216, 252];
12369        let owners = partition_expert_owner_routes(288, 4, 1, 8, &selected).unwrap();
12370        assert_eq!(owners.len(), 4);
12371        for (rank, owner) in owners.iter().enumerate() {
12372            assert_eq!(owner.rank, rank);
12373            assert_eq!(owner.selected, vec![0, 36]);
12374            assert_eq!(owner.token_rows, vec![0, 0]);
12375            assert_eq!(owner.global_pairs, vec![rank * 2, rank * 2 + 1]);
12376        }
12377    }
12378
12379    #[test]
12380    fn expert_owner_routes_validate_geometry_and_selected_experts() {
12381        assert!(partition_expert_owner_routes(288, 5, 1, 8, &[0; 8]).is_err());
12382        assert!(partition_expert_owner_routes(288, 4, 2, 8, &[0; 8]).is_err());
12383        let error = partition_expert_owner_routes(288, 4, 1, 8, &[288; 8]).unwrap_err();
12384        assert!(error.contains("outside 0..288"));
12385    }
12386
12387    #[test]
12388    fn step_grouped_owner_routes_validate_dynamic_top8_shapes() {
12389        let selected = [
12390            1, 73, 80, 145, 152, 159, 217, 224, 12, 84, 91, 156, 163, 170, 228, 235,
12391        ];
12392        assert_eq!(
12393            validate_step_grouped_owner_routes(288, 2, &selected).unwrap(),
12394            16
12395        );
12396        let owners = partition_expert_owner_routes(288, 4, 2, 8, &selected).unwrap();
12397        assert_eq!(
12398            owners
12399                .iter()
12400                .map(|owner| owner.selected.len())
12401                .collect::<Vec<_>>(),
12402            vec![2, 4, 6, 4]
12403        );
12404        assert!(validate_step_grouped_owner_routes(288, 2, &selected[..8]).is_err());
12405        assert!(validate_step_grouped_owner_routes(288, 1, &[0; 8]).is_err());
12406        assert!(validate_step_grouped_owner_routes(287, 2, &selected).is_err());
12407    }
12408
12409    #[test]
12410    fn weighted_route_combine_requires_a_canonical_pair_permutation() {
12411        let owner0 = [0usize, 3];
12412        let owner1 = [1usize, 2];
12413        let owners = [owner0.as_slice(), owner1.as_slice()];
12414        assert_eq!(
12415            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, 0.2, 0.3, 0.4],)
12416                .unwrap(),
12417            WeightedRouteCombineShape {
12418                pairs: 4,
12419                max_pairs: 12,
12420            }
12421        );
12422        let duplicate = [owner0.as_slice(), &[1usize, 1][..]];
12423        assert!(
12424            validate_weighted_route_combine(4096, 4, 3, 1, &duplicate, &[0.1, 0.2, 0.3, 0.4],)
12425                .is_err()
12426        );
12427        assert!(
12428            validate_weighted_route_combine(4096, 4, 3, 1, &owners, &[0.1, f32::NAN, 0.3, 0.4],)
12429                .is_err()
12430        );
12431        assert!(
12432            validate_weighted_route_combine(4096, 4, 1, 2, &owners, &[0.1, 0.2, 0.3, 0.4],)
12433                .is_err()
12434        );
12435    }
12436
12437    #[test]
12438    fn native_p2p_door_is_strict_and_default_off() {
12439        assert!(!parse_step_tp_native_p2p(None).unwrap());
12440        assert!(!parse_step_tp_native_p2p(Some("")).unwrap());
12441        assert!(!parse_step_tp_native_p2p(Some("0")).unwrap());
12442        assert!(parse_step_tp_native_p2p(Some("1")).unwrap());
12443        assert!(parse_step_tp_native_p2p(Some("true")).is_err());
12444        assert!(parse_step_tp_native_p2p(Some("2")).is_err());
12445    }
12446
12447    #[test]
12448    fn bulk_p2p_door_is_strict_and_default_off() {
12449        assert!(!parse_step_tp_bulk_p2p(None).unwrap());
12450        assert!(!parse_step_tp_bulk_p2p(Some("")).unwrap());
12451        assert!(!parse_step_tp_bulk_p2p(Some("0")).unwrap());
12452        assert!(parse_step_tp_bulk_p2p(Some("1")).unwrap());
12453        assert!(parse_step_tp_bulk_p2p(Some("true")).is_err());
12454        assert!(parse_step_tp_bulk_p2p(Some("2")).is_err());
12455    }
12456
12457    #[test]
12458    fn ep_device_arithmetic_door_is_strict_and_default_off() {
12459        assert!(!parse_step_ep_device_arithmetic(None).unwrap());
12460        assert!(!parse_step_ep_device_arithmetic(Some("")).unwrap());
12461        assert!(!parse_step_ep_device_arithmetic(Some("0")).unwrap());
12462        assert!(parse_step_ep_device_arithmetic(Some("1")).unwrap());
12463        assert!(parse_step_ep_device_arithmetic(Some("true")).is_err());
12464        assert!(parse_step_ep_device_arithmetic(Some("2")).is_err());
12465    }
12466
12467    #[test]
12468    fn f32_mirror_door_is_strict_and_default_off() {
12469        assert!(!parse_step_tp_f32_mirror(None).unwrap());
12470        assert!(!parse_step_tp_f32_mirror(Some("")).unwrap());
12471        assert!(!parse_step_tp_f32_mirror(Some("0")).unwrap());
12472        assert!(parse_step_tp_f32_mirror(Some("1")).unwrap());
12473        assert!(parse_step_tp_f32_mirror(Some("true")).is_err());
12474        assert!(parse_step_tp_f32_mirror(Some("2")).is_err());
12475    }
12476
12477    fn matrix(out_features: usize, in_features: usize) -> (Vec<u8>, Vec<f32>) {
12478        let codes = (0..out_features * in_features)
12479            .map(|index| (index % 251) as u8)
12480            .collect();
12481        let scales = (0..out_features.div_ceil(FP8_BLOCK) * in_features.div_ceil(FP8_BLOCK))
12482            .map(|index| index as f32 + 1.0)
12483            .collect();
12484        (codes, scales)
12485    }
12486
12487    fn bf16_matrix_bytes(out_features: usize, in_features: usize) -> Vec<u8> {
12488        (0..out_features * in_features)
12489            .flat_map(|value| (value as u16).to_le_bytes())
12490            .collect()
12491    }
12492
12493    fn decode_u16(bytes: &[u8]) -> Vec<u16> {
12494        bytes
12495            .chunks_exact(2)
12496            .map(|bytes| u16::from_le_bytes([bytes[0], bytes[1]]))
12497            .collect()
12498    }
12499
12500    #[test]
12501    fn bf16_matrix_rejects_wrong_byte_count() {
12502        let bytes = vec![0u8; 4 * 4 * 2 - 1];
12503        let matrix = Bf16Matrix {
12504            bytes: &bytes,
12505            out_features: 4,
12506            in_features: 4,
12507        };
12508        assert!(matrix.validate().unwrap_err().contains("4x4x2"));
12509    }
12510
12511    #[test]
12512    fn replicated_device_rows_require_exact_rank_local_shapes() {
12513        assert_eq!(
12514            replicated_device_row_values(3, 4096, 4, &[12_288; 4]).unwrap(),
12515            12_288
12516        );
12517        assert!(replicated_device_row_values(0, 4096, 4, &[0; 4]).is_err());
12518        assert!(replicated_device_row_values(3, 0, 4, &[0; 4]).is_err());
12519        assert!(replicated_device_row_values(3, 4096, 4, &[12_288; 3]).is_err());
12520        assert!(
12521            replicated_device_row_values(3, 4096, 4, &[12_288, 12_288, 12_287, 12_288]).is_err()
12522        );
12523        assert!(replicated_device_row_values(usize::MAX, 2, 1, &[0]).is_err());
12524    }
12525
12526    #[test]
12527    fn replicated_device_row_refresh_requires_exact_root_source() {
12528        assert_eq!(
12529            replicated_device_row_source_values(1, 12_288, 12_288, 3, 3).unwrap(),
12530            12_288
12531        );
12532        assert!(replicated_device_row_source_values(0, 12_288, 0, 3, 3).is_err());
12533        assert!(replicated_device_row_source_values(1, 0, 0, 3, 3).is_err());
12534        assert!(replicated_device_row_source_values(1, 12_288, 12_287, 3, 3).is_err());
12535        assert!(replicated_device_row_source_values(1, 12_288, 12_288, 2, 3).is_err());
12536        assert!(replicated_device_row_source_values(usize::MAX, 2, 0, 3, 3).is_err());
12537    }
12538
12539    #[test]
12540    fn step_bf16_canonical_rows_are_topology_invariant_through_tp8() {
12541        for tp in [1, 2, 4, 8] {
12542            assert_eq!(step_bf16_canonical_chunk_rows(8_192, tp).unwrap(), 1_024);
12543            assert_eq!(step_bf16_canonical_chunk_rows(12_288, tp).unwrap(), 1_536);
12544            assert_eq!(step_bf16_canonical_chunk_rows(1_024, tp).unwrap(), 128);
12545            assert_eq!(step_bf16_canonical_chunk_cols(8_192, tp).unwrap(), 1_024);
12546            assert_eq!(step_bf16_canonical_chunk_cols(12_288, tp).unwrap(), 1_536);
12547        }
12548        assert!(step_bf16_canonical_chunk_rows(12_288, 3).is_err());
12549        assert!(step_bf16_canonical_chunk_rows(1_001, 2).is_err());
12550        assert!(step_bf16_canonical_chunk_cols(12_288, 3).is_err());
12551        assert!(step_bf16_canonical_chunk_cols(1_001, 2).is_err());
12552    }
12553
12554    #[test]
12555    fn cache_rows_split_by_token_then_rank() {
12556        let rows = (0u8..24).collect::<Vec<_>>();
12557        assert_eq!(
12558            cache_rank_rows(&rows, 3, 4, 2, 0).unwrap(),
12559            vec![0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19]
12560        );
12561        assert_eq!(
12562            cache_rank_rows(&rows, 3, 4, 2, 1).unwrap(),
12563            vec![4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23]
12564        );
12565        assert!(cache_rank_rows(&rows[..23], 3, 4, 2, 0).is_err());
12566        assert!(cache_rank_rows(&rows, 3, 4, 2, 2).is_err());
12567    }
12568
12569    #[test]
12570    fn bf16_column_shard_preserves_contiguous_output_rows() {
12571        let bytes = bf16_matrix_bytes(4, 4);
12572        let matrix = Bf16Matrix {
12573            bytes: &bytes,
12574            out_features: 4,
12575            in_features: 4,
12576        };
12577        let shard = bf16_column_shard(matrix, 2, 1).unwrap();
12578        assert_eq!(shard.out_features, 2);
12579        assert_eq!(shard.in_features, 4);
12580        assert_eq!(decode_u16(shard.bytes), (8..16).collect::<Vec<_>>());
12581    }
12582
12583    #[test]
12584    fn bf16_row_shard_preserves_each_input_column_window() {
12585        let bytes = bf16_matrix_bytes(3, 4);
12586        let matrix = Bf16Matrix {
12587            bytes: &bytes,
12588            out_features: 3,
12589            in_features: 4,
12590        };
12591        let shard = bf16_row_shard(matrix, 2, 1).unwrap();
12592        assert_eq!(decode_u16(&shard), vec![2, 3, 6, 7, 10, 11]);
12593    }
12594
12595    #[test]
12596    fn bf16_row_block_preserves_global_column_order() {
12597        let bytes = bf16_matrix_bytes(3, 8);
12598        let matrix = Bf16Matrix {
12599            bytes: &bytes,
12600            out_features: 3,
12601            in_features: 8,
12602        };
12603        let block = bf16_row_block(matrix, 2, 3).unwrap();
12604        assert_eq!(decode_u16(&block), vec![2, 3, 4, 10, 11, 12, 18, 19, 20]);
12605    }
12606
12607    #[test]
12608    fn column_shard_preserves_contiguous_weight_and_scale_rows() {
12609        let (codes, scales) = matrix(1280, 4096);
12610        let matrix = E4m3BlockMatrix {
12611            codes: &codes,
12612            scales: &scales,
12613            out_features: 1280,
12614            in_features: 4096,
12615        };
12616        let shard = column_shard(matrix, 2, 1).unwrap();
12617        assert_eq!(shard.out_features, 640);
12618        assert_eq!(shard.codes, &codes[640 * 4096..]);
12619        assert_eq!(shard.scales, &scales[5 * 32..]);
12620    }
12621
12622    #[test]
12623    fn row_shard_preserves_each_weight_and_scale_column_window() {
12624        let (codes, scales) = matrix(4096, 1280);
12625        let matrix = E4m3BlockMatrix {
12626            codes: &codes,
12627            scales: &scales,
12628            out_features: 4096,
12629            in_features: 1280,
12630        };
12631        let (shard_codes, shard_scales) = row_shard(matrix, 2, 1).unwrap();
12632        assert_eq!(shard_codes.len(), 4096 * 640);
12633        assert_eq!(&shard_codes[..640], &codes[640..1280]);
12634        assert_eq!(&shard_codes[640..1280], &codes[1280 + 640..2560]);
12635        assert_eq!(shard_scales.len(), 32 * 5);
12636        assert_eq!(&shard_scales[..5], &scales[5..10]);
12637        assert_eq!(&shard_scales[5..10], &scales[15..20]);
12638    }
12639
12640    #[test]
12641    fn activation_shards_keep_token_rows_separate() {
12642        let activations: Vec<f32> = (0..2 * 8).map(|value| value as f32).collect();
12643        assert_eq!(
12644            activation_shard(&activations, 2, 8, 2, 1),
12645            vec![4.0, 5.0, 6.0, 7.0, 12.0, 13.0, 14.0, 15.0],
12646        );
12647    }
12648
12649    #[test]
12650    fn expert_bank_selects_expert_major_code_and_scale_planes() {
12651        let expert_count = 2;
12652        let out_features = 128;
12653        let in_features = 128;
12654        let code_stride = out_features * in_features;
12655        let codes: Vec<u8> = (0..expert_count * code_stride)
12656            .map(|index| (index % 251) as u8)
12657            .collect();
12658        let scales = vec![1.0f32, 2.0];
12659        let bank = E4m3ExpertBank {
12660            codes: &codes,
12661            scales: &scales,
12662            expert_count,
12663            out_features,
12664            in_features,
12665        };
12666        bank.validate().unwrap();
12667        let expert = bank.expert(1).unwrap();
12668        assert_eq!(expert.codes, &codes[code_stride..]);
12669        assert_eq!(expert.scales, &[2.0]);
12670    }
12671
12672    #[test]
12673    fn expert_bank_rejects_non_positive_scale() {
12674        let codes = vec![0u8; 128 * 128];
12675        let scales = vec![0.0f32];
12676        let bank = E4m3ExpertBank {
12677            codes: &codes,
12678            scales: &scales,
12679            expert_count: 1,
12680            out_features: 128,
12681            in_features: 128,
12682        };
12683        assert!(bank.validate().unwrap_err().contains("non-positive"));
12684    }
12685
12686    #[test]
12687    fn tensor_parallel_column_bank_keeps_each_expert_scale_plane_separate() {
12688        let expert_count = 2;
12689        let out_features = 256;
12690        let in_features = 128;
12691        let code_stride = out_features * in_features;
12692        let scale_stride = 2;
12693        let codes = (0..expert_count * code_stride)
12694            .map(|index| (index % 251) as u8)
12695            .collect::<Vec<_>>();
12696        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
12697        let bank = E4m3ExpertBank {
12698            codes: &codes,
12699            scales: &scales,
12700            expert_count,
12701            out_features,
12702            in_features,
12703        };
12704
12705        let rank = pack_column_bank_rank(bank, 2, 1).unwrap();
12706        assert_eq!(rank.out_features, 128);
12707        assert_eq!(rank.in_features, 128);
12708        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
12709        assert_eq!(rank.scales, vec![11.0, 21.0]);
12710        assert_eq!(&rank.codes[..128 * 128], &codes[128 * 128..256 * 128]);
12711        assert_eq!(
12712            &rank.codes[128 * 128..],
12713            &codes[code_stride + 128 * 128..2 * code_stride]
12714        );
12715        assert_eq!(scale_stride, scales.len() / expert_count);
12716    }
12717
12718    #[test]
12719    fn tensor_parallel_row_bank_keeps_each_expert_scale_plane_separate() {
12720        let expert_count = 2;
12721        let out_features = 128;
12722        let in_features = 256;
12723        let code_stride = out_features * in_features;
12724        let codes = (0..expert_count * code_stride)
12725            .map(|index| (index % 251) as u8)
12726            .collect::<Vec<_>>();
12727        let scales = vec![10.0f32, 11.0, 20.0, 21.0];
12728        let bank = E4m3ExpertBank {
12729            codes: &codes,
12730            scales: &scales,
12731            expert_count,
12732            out_features,
12733            in_features,
12734        };
12735
12736        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
12737        assert_eq!(rank.out_features, 128);
12738        assert_eq!(rank.in_features, 128);
12739        assert_eq!(rank.k_blocks, Some(1));
12740        assert_eq!(rank.codes.len(), expert_count * 128 * 128);
12741        assert_eq!(rank.scales, vec![11.0, 21.0]);
12742        assert_eq!(&rank.codes[..128], &codes[128..256]);
12743        assert_eq!(
12744            &rank.codes[128 * 128..128 * 128 + 128],
12745            &codes[code_stride + 128..code_stride + 256]
12746        );
12747    }
12748
12749    #[test]
12750    fn tensor_parallel_row_bank_preserves_global_k_block_order() {
12751        let expert_count = 2;
12752        let out_features = 256;
12753        let in_features = 512;
12754        let code_stride = out_features * in_features;
12755        let mut codes = vec![0u8; expert_count * code_stride];
12756        for expert in 0..expert_count {
12757            for row in 0..out_features {
12758                for block in 0..4 {
12759                    let value = (expert * 80 + block * 16 + row % 16) as u8;
12760                    let start = expert * code_stride + row * in_features + block * FP8_BLOCK;
12761                    codes[start..start + FP8_BLOCK].fill(value);
12762                }
12763            }
12764        }
12765        let scales = vec![
12766            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,
12767            112.0, 113.0, 114.0,
12768        ];
12769        let bank = E4m3ExpertBank {
12770            codes: &codes,
12771            scales: &scales,
12772            expert_count,
12773            out_features,
12774            in_features,
12775        };
12776
12777        let rank = pack_row_bank_rank(bank, 2, 1).unwrap();
12778        assert_eq!(rank.out_features, out_features);
12779        assert_eq!(rank.in_features, 256);
12780        assert_eq!(rank.k_blocks, Some(2));
12781        assert_eq!(rank.code_stride, out_features * 256);
12782        assert_eq!(rank.scale_stride, 4);
12783        assert_eq!(&rank.scales[..4], &[3.0, 13.0, 4.0, 14.0]);
12784        assert_eq!(&rank.scales[4..], &[103.0, 113.0, 104.0, 114.0]);
12785
12786        let block_stride = out_features * FP8_BLOCK;
12787        assert!(rank.codes[..FP8_BLOCK].iter().all(|&code| code == 32));
12788        assert!(
12789            rank.codes[block_stride..block_stride + FP8_BLOCK]
12790                .iter()
12791                .all(|&code| code == 48)
12792        );
12793        assert!(
12794            rank.codes[rank.code_stride..rank.code_stride + FP8_BLOCK]
12795                .iter()
12796                .all(|&code| code == 112)
12797        );
12798        assert!(
12799            rank.codes
12800                [rank.code_stride + block_stride..rank.code_stride + block_stride + FP8_BLOCK]
12801                .iter()
12802                .all(|&code| code == 128)
12803        );
12804    }
12805
12806    #[test]
12807    fn step_ep_layer_specs_are_literal_and_fail_closed() {
12808        assert!(parse_step_ep_layer_specs(None).unwrap().is_empty());
12809        assert!(parse_step_ep_layer_specs(Some("0")).unwrap().is_empty());
12810        assert_eq!(
12811            parse_step_ep_layer_specs(Some("24@1,2")).unwrap(),
12812            vec![StepEpLayerSpec {
12813                layer: 24,
12814                devices: vec![1, 2],
12815            }]
12816        );
12817        assert_eq!(
12818            parse_step_ep_layer_specs(Some("24-25@1,2;31@0,2")).unwrap(),
12819            vec![
12820                StepEpLayerSpec {
12821                    layer: 24,
12822                    devices: vec![1, 2],
12823                },
12824                StepEpLayerSpec {
12825                    layer: 25,
12826                    devices: vec![1, 2],
12827                },
12828                StepEpLayerSpec {
12829                    layer: 31,
12830                    devices: vec![0, 2],
12831                },
12832            ]
12833        );
12834        assert!(parse_step_ep_layer_specs(Some("24@1")).is_err());
12835        assert!(parse_step_ep_layer_specs(Some("24@1,1")).is_err());
12836        assert!(parse_step_ep_layer_specs(Some("layer@1,2")).is_err());
12837        assert!(parse_step_ep_layer_specs(Some("25-24@1,2")).is_err());
12838        assert!(parse_step_ep_layer_specs(Some("0-128@1,2")).is_err());
12839        assert!(parse_step_ep_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
12840        assert!(parse_step_ep_layer_specs(Some("all@0,1")).is_err());
12841    }
12842
12843    #[test]
12844    fn step_tp_layer_specs_share_the_fail_closed_layer_contract() {
12845        assert!(parse_step_tp_layer_specs(None).unwrap().is_empty());
12846        assert!(parse_step_tp_layer_specs(Some("0")).unwrap().is_empty());
12847        assert_eq!(
12848            parse_step_tp_layer_specs(Some("24-25@1,2")).unwrap(),
12849            vec![
12850                StepTpLayerSpec {
12851                    layer: 24,
12852                    devices: vec![1, 2],
12853                },
12854                StepTpLayerSpec {
12855                    layer: 25,
12856                    devices: vec![1, 2],
12857                },
12858            ]
12859        );
12860        let error = parse_step_tp_layer_specs(Some("24@1")).unwrap_err();
12861        assert!(error.contains("MEMRA_STEP_TP"));
12862        assert!(parse_step_tp_layer_specs(Some("24@1,1")).is_err());
12863        assert!(parse_step_tp_layer_specs(Some("24-25@1,2;25@0,2")).is_err());
12864
12865        let all = parse_step_tp_layer_specs(Some("all@0,1,2,3,4,5,6,7")).unwrap();
12866        assert_eq!(all.len(), STEP37_TRUNK_LAYERS);
12867        assert_eq!(all.first().unwrap().layer, 0);
12868        assert_eq!(all.last().unwrap().layer, STEP37_TRUNK_LAYERS - 1);
12869        let devices = (0..8).collect::<Vec<_>>();
12870        assert!(all.iter().all(|spec| spec.devices == devices));
12871        assert!(parse_step_tp_layer_specs(Some("all@0,1;44@0,1")).is_err());
12872    }
12873}
12874
12875// ===== Whole-token graph builder (increment B) ==================================================
12876//
12877// The decode fns are already sectioned at every e/rank/root seam (the stage flow, sweeps_rank,
12878// finish splits, the dcw arm). `graph_section` is the one annotation those seams call: eager
12879// mode runs the closure verbatim; build mode wraps it in a stream capture on the section's
12880// device and records a child + its dependency edges. A token then assembles as ONE multi-device
12881// parent (children per section per layer), launched once per token — the launch-collapse the
12882// per-layer minis could not reach (routes-mini negative, 2026-08-21).
12883
12884/// One captured section: the child graph plus which parent node it became, and the CUDA
12885/// context it was captured under (exec memset updates need it).
12886struct TokenGraphChild {
12887    graph: cudarc::driver::CudaGraph,
12888    node: cudarc::driver::sys::CUgraphNode,
12889    ctx: cudarc::driver::sys::CUcontext,
12890}
12891
12892/// Exec-updatable fa geometry discovered in one attention rank child: the three partial-pool
12893/// memsets, the dcw fa kernel, and its combine — everything a bucket change touches. Node
12894/// handles address the parent's CLONED child graphs (the M1-probed update path).
12895struct TokenGraphFaSite {
12896    ctx: cudarc::driver::sys::CUcontext,
12897    memset_o: cudarc::driver::sys::CUgraphNode,
12898    memset_m: [cudarc::driver::sys::CUgraphNode; 2],
12899    fa: cudarc::driver::sys::CUgraphNode,
12900    combine: cudarc::driver::sys::CUgraphNode,
12901    window: usize,
12902    n_head: usize,
12903    n_head_kv: usize,
12904    head_dim: usize,
12905}
12906
12907pub struct TokenGraphBuilder {
12908    parent: cudarc::driver::sys::CUgraph,
12909    children: Vec<TokenGraphChild>,
12910    /// Nodes every NEXT section must depend on (the frontier): one node for serial flow,
12911    /// several while a parallel group is open.
12912    frontier: Vec<cudarc::driver::sys::CUgraphNode>,
12913    /// Detached sections: forked from the frontier at issue time, joined ONLY by the next
12914    /// non-group section (they never gate a parallel group merge — the SH1 shape).
12915    pending_detached: Vec<cudarc::driver::sys::CUgraphNode>,
12916    /// Open parallel group: sections issued under the same group id fork from the SAME
12917    /// predecessor set and merge into the frontier together when the group closes.
12918    group: Option<(
12919        u32,
12920        Vec<cudarc::driver::sys::CUgraphNode>,
12921        Vec<cudarc::driver::sys::CUgraphNode>,
12922    )>,
12923}
12924
12925// SAFETY: single decode thread; graph handles are process handles.
12926unsafe impl Send for TokenGraphBuilder {}
12927
12928impl TokenGraphBuilder {
12929    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
12930        use cudarc::driver::sys;
12931        let mut parent: sys::CUgraph = std::ptr::null_mut();
12932        let r = unsafe { sys::cuGraphCreate(&mut parent, 0) };
12933        if r != sys::CUresult::CUDA_SUCCESS {
12934            return Err(format!("token graph create: {r:?}").into());
12935        }
12936        Ok(Self {
12937            parent,
12938            children: Vec::new(),
12939            frontier: Vec::new(),
12940            pending_detached: Vec::new(),
12941            group: None,
12942        })
12943    }
12944
12945    fn push_child(
12946        &mut self,
12947        graph: cudarc::driver::CudaGraph,
12948        parallel_group: Option<u32>,
12949        detached: bool,
12950        absorb: bool,
12951        ctx: cudarc::driver::sys::CUcontext,
12952    ) -> Result<(), Box<dyn std::error::Error>> {
12953        use cudarc::driver::sys;
12954        // Resolve the dependency set: serial sections depend on the current frontier; a
12955        // parallel-group section depends on the frontier AS OF the group opening; a
12956        // DETACHED section forks like a group member but joins only the next serial section.
12957        let deps: Vec<sys::CUgraphNode> = match (&mut self.group, parallel_group) {
12958            (Some((open, base, _)), Some(group)) if *open == group => base.clone(),
12959            (state, Some(group)) => {
12960                // opening a new group (closing any previous one first)
12961                if let Some((_, _, members)) = state.take() {
12962                    self.frontier = members;
12963                }
12964                let base = self.frontier.clone();
12965                *state = Some((group, base.clone(), Vec::new()));
12966                base
12967            }
12968            (state, None) if detached => match state.as_ref() {
12969                Some((_, base, _)) => base.clone(),
12970                None => self.frontier.clone(),
12971            },
12972            (state, None) => {
12973                if let Some((_, _, members)) = state.take() {
12974                    self.frontier = members;
12975                }
12976                let mut deps = self.frontier.clone();
12977                if absorb {
12978                    deps.append(&mut self.pending_detached);
12979                }
12980                deps
12981            }
12982        };
12983        let mut node: sys::CUgraphNode = std::ptr::null_mut();
12984        let r = unsafe {
12985            sys::cuGraphAddChildGraphNode(
12986                &mut node,
12987                self.parent,
12988                if deps.is_empty() {
12989                    std::ptr::null()
12990                } else {
12991                    deps.as_ptr()
12992                },
12993                deps.len(),
12994                graph.cu_graph(),
12995            )
12996        };
12997        if r != sys::CUresult::CUDA_SUCCESS {
12998            return Err(format!("token graph child: {r:?}").into());
12999        }
13000        match (&mut self.group, parallel_group, detached) {
13001            (_, None, true) => self.pending_detached.push(node),
13002            (Some((_, _, members)), Some(_), _) => members.push(node),
13003            _ => self.frontier = vec![node],
13004        }
13005        self.children.push(TokenGraphChild { graph, node, ctx });
13006        Ok(())
13007    }
13008
13009    pub fn finish(mut self) -> Result<TokenGraph, Box<dyn std::error::Error>> {
13010        use cudarc::driver::sys;
13011        if let Some((_, _, members)) = self.group.take() {
13012            self.frontier = members;
13013        }
13014        // Discover the fa sites BEFORE instantiate: the parent's cloned child graphs hold
13015        // the node handles the exec update path (M1) addresses.
13016        let mut fa_sites = Vec::new();
13017        for child in &self.children {
13018            if let Some(site) = discover_fa_site(child.node, child.ctx)? {
13019                fa_sites.push(site);
13020            }
13021        }
13022        let mut exec: sys::CUgraphExec = std::ptr::null_mut();
13023        let r = unsafe { sys::cuGraphInstantiateWithFlags(&mut exec, self.parent, 0) };
13024        if r != sys::CUresult::CUDA_SUCCESS {
13025            return Err(format!("token graph instantiate: {r:?}").into());
13026        }
13027        Ok(TokenGraph {
13028            exec,
13029            parent: self.parent,
13030            _children: self.children,
13031            fa_sites,
13032        })
13033    }
13034}
13035
13036/// Walk one child graph; if it carries the attention-section signature (exactly three MEMSET
13037/// nodes chained memset->memset->memset->fa_kernel->combine_kernel), return its update site.
13038fn discover_fa_site(
13039    child_node: cudarc::driver::sys::CUgraphNode,
13040    ctx: cudarc::driver::sys::CUcontext,
13041) -> Result<Option<TokenGraphFaSite>, Box<dyn std::error::Error>> {
13042    use cudarc::driver::sys;
13043    fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
13044        if r == sys::CUresult::CUDA_SUCCESS {
13045            Ok(())
13046        } else {
13047            Err(format!("{what}: {r:?}").into())
13048        }
13049    }
13050    let mut graph: sys::CUgraph = std::ptr::null_mut();
13051    unsafe {
13052        cu_try(
13053            sys::cuGraphChildGraphNodeGetGraph(child_node, &mut graph),
13054            "fa-site child GetGraph",
13055        )?;
13056    }
13057    let mut count: usize = 0;
13058    unsafe {
13059        cu_try(
13060            sys::cuGraphGetNodes(graph, std::ptr::null_mut(), &mut count),
13061            "fa-site GetNodes(count)",
13062        )?;
13063    }
13064    let mut nodes: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); count];
13065    unsafe {
13066        cu_try(
13067            sys::cuGraphGetNodes(graph, nodes.as_mut_ptr(), &mut count),
13068            "fa-site GetNodes",
13069        )?;
13070    }
13071    nodes.truncate(count);
13072    let node_type =
13073        |node: sys::CUgraphNode| -> Result<sys::CUgraphNodeType, Box<dyn std::error::Error>> {
13074            let mut ty = sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_EMPTY;
13075            unsafe {
13076                cu_try(
13077                    sys::cuGraphNodeGetType(node, &mut ty),
13078                    "fa-site NodeGetType",
13079                )?;
13080            }
13081            Ok(ty)
13082        };
13083    let memsets: Vec<sys::CUgraphNode> = {
13084        let mut v = Vec::new();
13085        for &node in &nodes {
13086            if node_type(node)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_MEMSET {
13087                v.push(node);
13088            }
13089        }
13090        v
13091    };
13092    if memsets.len() != 3 {
13093        return Ok(None);
13094    }
13095    // Single-stream capture makes the chain linear: follow dependent edges from each memset.
13096    let dependents =
13097        |node: sys::CUgraphNode| -> Result<Vec<sys::CUgraphNode>, Box<dyn std::error::Error>> {
13098            let mut n: usize = 0;
13099            unsafe {
13100                cu_try(
13101                    sys::cuGraphNodeGetDependentNodes_v2(
13102                        node,
13103                        std::ptr::null_mut(),
13104                        std::ptr::null_mut(),
13105                        &mut n,
13106                    ),
13107                    "fa-site GetDependentNodes(count)",
13108                )?;
13109            }
13110            let mut v: Vec<sys::CUgraphNode> = vec![std::ptr::null_mut(); n];
13111            unsafe {
13112                cu_try(
13113                    sys::cuGraphNodeGetDependentNodes_v2(
13114                        node,
13115                        v.as_mut_ptr(),
13116                        std::ptr::null_mut(),
13117                        &mut n,
13118                    ),
13119                    "fa-site GetDependentNodes",
13120                )?;
13121            }
13122            v.truncate(n);
13123            Ok(v)
13124        };
13125    // The LAST memset is the one whose direct dependent is a kernel (fa); the other two are
13126    // ordered among themselves but interchangeable for width updates.
13127    let mut fa: Option<sys::CUgraphNode> = None;
13128    let mut last_memset: Option<sys::CUgraphNode> = None;
13129    for &ms in &memsets {
13130        for dep in dependents(ms)? {
13131            if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
13132                fa = Some(dep);
13133                last_memset = Some(ms);
13134            }
13135        }
13136    }
13137    let (Some(fa), Some(_last)) = (fa, last_memset) else {
13138        return Ok(None);
13139    };
13140    let mut combine: Option<sys::CUgraphNode> = None;
13141    for dep in dependents(fa)? {
13142        if node_type(dep)? == sys::CUgraphNodeType::CU_GRAPH_NODE_TYPE_KERNEL {
13143            combine = Some(dep);
13144        }
13145    }
13146    let Some(combine) = combine else {
13147        return Ok(None);
13148    };
13149    // Read the fa launch geometry from its baked args (arg order pinned by fa_decode_dcw):
13150    // 6=hd 7=nh 8=nhkv 11=win 13=nsp 14=ski.
13151    let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13152    unsafe {
13153        cu_try(
13154            sys::cuGraphKernelNodeGetParams_v2(fa, &mut params),
13155            "fa-site KernelNodeGetParams",
13156        )?;
13157    }
13158    let arg_i32 =
13159        |slot: usize| -> i32 { unsafe { *(*params.kernelParams.add(slot) as *const i32) } };
13160    let (hd, nh, nhkv, win) = (arg_i32(6), arg_i32(7), arg_i32(8), arg_i32(11));
13161    // Identify the o-partial memset (hd x wider than the m/l pair).
13162    let width_of = |node: sys::CUgraphNode| -> Result<usize, Box<dyn std::error::Error>> {
13163        let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
13164        unsafe {
13165            cu_try(
13166                sys::cuGraphMemsetNodeGetParams(node, &mut mp),
13167                "fa-site MemsetNodeGetParams",
13168            )?;
13169        }
13170        Ok(mp.width)
13171    };
13172    let mut widest = memsets[0];
13173    for &ms in &memsets[1..] {
13174        if width_of(ms)? > width_of(widest)? {
13175            widest = ms;
13176        }
13177    }
13178    let memset_m: Vec<sys::CUgraphNode> =
13179        memsets.iter().copied().filter(|&m| m != widest).collect();
13180    Ok(Some(TokenGraphFaSite {
13181        ctx,
13182        memset_o: widest,
13183        memset_m: [memset_m[0], memset_m[1]],
13184        fa,
13185        combine,
13186        window: win as usize,
13187        n_head: nh as usize,
13188        n_head_kv: nhkv as usize,
13189        head_dim: hd as usize,
13190    }))
13191}
13192
13193pub struct TokenGraph {
13194    exec: cudarc::driver::sys::CUgraphExec,
13195    parent: cudarc::driver::sys::CUgraph,
13196    _children: Vec<TokenGraphChild>,
13197    fa_sites: Vec<TokenGraphFaSite>,
13198}
13199
13200unsafe impl Send for TokenGraph {}
13201
13202impl TokenGraph {
13203    /// Retarget every fa site to a new bucket via exec param updates (M1 path) — replaces the
13204    /// per-bucket whole-graph rebuild (~55ms) with ~450 node updates (~1ms). Per site the
13205    /// bucket caps at the layer window; nsp/ski/gridDimY and the partial-pool memset widths
13206    /// move together so the exec always matches what a fresh build at `bucket` would bake.
13207    pub fn retarget_bucket(&mut self, bucket: usize) -> Result<(), Box<dyn std::error::Error>> {
13208        use cudarc::driver::sys;
13209        fn cu_try(r: sys::CUresult, what: &str) -> Result<(), Box<dyn std::error::Error>> {
13210            if r == sys::CUresult::CUDA_SUCCESS {
13211                Ok(())
13212            } else {
13213                Err(format!("{what}: {r:?}").into())
13214            }
13215        }
13216        for site in &self.fa_sites {
13217            let layer_bucket = if site.window > 0 {
13218                bucket.min(site.window)
13219            } else {
13220                bucket
13221            };
13222            let sp = crate::fa_split_keys(layer_bucket, site.n_head_kv);
13223            let nsp = layer_bucket.div_ceil(sp).max(1);
13224            // fa kernel: nsp (slot 13), ski (slot 14), gridDimY = nsp.
13225            let mut params: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13226            unsafe {
13227                cu_try(
13228                    sys::cuGraphKernelNodeGetParams_v2(site.fa, &mut params),
13229                    "retarget fa GetParams",
13230                )?;
13231                *(*params.kernelParams.add(13) as *mut i32) = nsp as i32;
13232                *(*params.kernelParams.add(14) as *mut i32) = sp as i32;
13233                params.gridDimY = nsp as u32;
13234                cu_try(
13235                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.fa, &params),
13236                    "retarget fa SetParams",
13237                )?;
13238            }
13239            // combine: nsp (slot 6).
13240            let mut cparams: sys::CUDA_KERNEL_NODE_PARAMS = unsafe { std::mem::zeroed() };
13241            unsafe {
13242                cu_try(
13243                    sys::cuGraphKernelNodeGetParams_v2(site.combine, &mut cparams),
13244                    "retarget combine GetParams",
13245                )?;
13246                *(*cparams.kernelParams.add(6) as *mut i32) = nsp as i32;
13247                cu_try(
13248                    sys::cuGraphExecKernelNodeSetParams_v2(self.exec, site.combine, &cparams),
13249                    "retarget combine SetParams",
13250                )?;
13251            }
13252            // partial-pool memsets: o = nh*nsp*hd elements, m/l = nh*nsp.
13253            let set_width =
13254                |node: sys::CUgraphNode, width: usize| -> Result<(), Box<dyn std::error::Error>> {
13255                    let mut mp: sys::CUDA_MEMSET_NODE_PARAMS = unsafe { std::mem::zeroed() };
13256                    unsafe {
13257                        cu_try(
13258                            sys::cuGraphMemsetNodeGetParams(node, &mut mp),
13259                            "retarget memset GetParams",
13260                        )?;
13261                    }
13262                    mp.width = width;
13263                    unsafe {
13264                        cu_try(
13265                            sys::cuGraphExecMemsetNodeSetParams(self.exec, node, &mp, site.ctx),
13266                            "retarget memset SetParams",
13267                        )?;
13268                    }
13269                    Ok(())
13270                };
13271            set_width(site.memset_o, site.n_head * nsp * site.head_dim)?;
13272            set_width(site.memset_m[0], site.n_head * nsp)?;
13273            set_width(site.memset_m[1], site.n_head * nsp)?;
13274        }
13275        Ok(())
13276    }
13277
13278    pub fn launch(&self, e: &Engine) -> Result<(), Box<dyn std::error::Error>> {
13279        use cudarc::driver::sys;
13280        let _main = e.gpu.enter_main()?;
13281        let r = unsafe { sys::cuGraphLaunch(self.exec, e.stream().cu_stream() as sys::CUstream) };
13282        if r != sys::CUresult::CUDA_SUCCESS {
13283            return Err(format!("token graph launch: {r:?}").into());
13284        }
13285        Ok(())
13286    }
13287}
13288
13289impl Drop for TokenGraph {
13290    fn drop(&mut self) {
13291        unsafe {
13292            let _ = cudarc::driver::sys::cuGraphExecDestroy(self.exec);
13293            let _ = cudarc::driver::sys::cuGraphDestroy(self.parent);
13294        }
13295    }
13296}
13297
13298std::thread_local! {
13299    static TOKEN_GRAPH_BUILDER: std::cell::RefCell<Option<TokenGraphBuilder>> =
13300        const { std::cell::RefCell::new(None) };
13301}
13302
13303/// Arm the thread-local builder (build mode) — the next `graph_section` calls capture.
13304pub fn token_graph_build_begin() -> Result<(), Box<dyn std::error::Error>> {
13305    let builder = TokenGraphBuilder::new()?;
13306    TOKEN_GRAPH_BUILDER.with(|cell| *cell.borrow_mut() = Some(builder));
13307    Ok(())
13308}
13309
13310/// Take the finished parent (ends build mode).
13311pub fn token_graph_build_finish() -> Result<TokenGraph, Box<dyn std::error::Error>> {
13312    let builder = TOKEN_GRAPH_BUILDER
13313        .with(|cell| cell.borrow_mut().take())
13314        .ok_or("token graph build was not begun")?;
13315    builder.finish()
13316}
13317
13318/// True while the thread-local builder is armed.
13319pub fn token_graph_building() -> bool {
13320    TOKEN_GRAPH_BUILDER.with(|cell| cell.borrow().is_some())
13321}
13322
13323/// The section annotation: eager mode runs the closure verbatim; build mode wraps it in a
13324/// stream capture on `engine`'s stream and records the child. Sections sharing a
13325/// `parallel_group` id fork from the same predecessor set and merge together. The closure
13326/// must be capture-safe (raw copies at cross-context seams, no host syncs, no events).
13327pub fn graph_section<F>(
13328    engine: &Engine,
13329    parallel_group: Option<u32>,
13330    f: F,
13331) -> Result<(), Box<dyn std::error::Error>>
13332where
13333    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13334{
13335    graph_section_opts(engine, parallel_group, false, false, f)
13336}
13337
13338/// Serial section that ALSO joins every pending detached section (the SH1 consumer shape).
13339pub fn graph_section_absorbing<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
13340where
13341    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13342{
13343    graph_section_opts(engine, None, false, true, f)
13344}
13345
13346/// `graph_section` with the DETACHED shape: forks from the current frontier (or the open
13347/// group base) and is joined only by the next serial section — never gates a group merge.
13348pub fn graph_section_detached<F>(engine: &Engine, f: F) -> Result<(), Box<dyn std::error::Error>>
13349where
13350    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13351{
13352    graph_section_opts(engine, None, true, false, f)
13353}
13354
13355pub fn graph_section_opts<F>(
13356    engine: &Engine,
13357    parallel_group: Option<u32>,
13358    detached: bool,
13359    absorb: bool,
13360    f: F,
13361) -> Result<(), Box<dyn std::error::Error>>
13362where
13363    F: FnMut() -> Result<(), Box<dyn std::error::Error>>,
13364{
13365    let building = token_graph_building();
13366    if !building {
13367        let mut f = f;
13368        return f();
13369    }
13370    let (child, ctx) = {
13371        let _main = engine.gpu.enter_main()?;
13372        let mut ctx: cudarc::driver::sys::CUcontext = std::ptr::null_mut();
13373        let r = unsafe { cudarc::driver::sys::cuCtxGetCurrent(&mut ctx) };
13374        if r != cudarc::driver::sys::CUresult::CUDA_SUCCESS {
13375            return Err(format!("graph section ctx query: {r:?}").into());
13376        }
13377        let mut f = f;
13378        // NO WARMUP RUNS: section bodies carry device side effects (dcw appends, counter
13379        // incs) that a warmup would really execute — the len_d-drift crash of 2026-08-21.
13380        let (child, _retained) = engine.capture_graph_retained_nowarm(|_| f())?;
13381        (child, ctx)
13382    };
13383    TOKEN_GRAPH_BUILDER.with(|cell| {
13384        cell.borrow_mut()
13385            .as_mut()
13386            .expect("builder checked above")
13387            .push_child(child, parallel_group, detached, absorb, ctx)
13388    })
13389}